Valgrind error: Conditional jump or move depends on uninitialised value

Valgrind error: Conditional jump or move depends on uninitialised value

这是我第一次 post 在这里,对于任何违反规范的行为,我们深表歉意... 我正在完成哈佛 Cs50 的拼写问题。任务是将字典文件加载到哈希映射中,然后检查文本文件中是否存在拼写错误的单词。 我的程序运行良好并通过了除 valgrind 测试之外的所有测试。 这是我的问题代码,还有另一个哈佛编写的文件,其中包含程序的主要部分并调用我编写的所有函数:

// Implements a dictionary's functionality

#include <ctype.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>
#include "dictionary.h"

// Represents a node in a hash table

typedef struct node
{
    char word[LENGTH + 1];
    struct node* next;
}
node;

void deleteList(node* n);
bool checkList(node* n, const char* checkword);


// TODO: Choose number of buckets in hash table
const unsigned int N = 21952;

// Hash table
node* table[N];

// Returns true if word is in dictionary, else false
bool check(const char* word)
{
    int code = hash(word);
    return checkList(table[code], word);
}

int dictSize = 0;
bool dictLoaded = false;

// Hashes word to a number
unsigned int hash(const char* word)
{
    int index = 0;
    //char* wordCopy = malloc((sizeof(char) * strlen(word))); //VALGRIND ERROR Conditional jump or move depends on uninitialised value
    char* wordCopy = calloc(strlen(word), sizeof(char));
    strncpy(wordCopy, word, strlen(word)); //this line


    int sum = 0;
    //iterate thru the word until it ends or we get to our requisite 3 characters
    //must accomodate any capitalization or apostraphes
    while (word[index] != '[=10=]' && index < 3) // this line is still an an error, just like when word was wordcopy
    {
        //change chars to int vals so that null is 0, a is 1, z is 26, and ' is 27
        int ascii = 0; // this remains unchanged if the current char is null
        if (isalpha(wordCopy[index]) != 0)
        {
            wordCopy[index] = tolower(wordCopy[index]);
            ascii = wordCopy[index] - 96;
        }
        else if (wordCopy[index] == '\'')
        {
            ascii = 27;
        }

// add the current chars val to the sum of the word's first three vals
// the math here ensures that "null null null" will be 0, and ''' will be 21,951 or the last index of our hash map
    if (index == 0)
        sum += ascii * 784;

    if (index == 1)
        sum += ascii * 28;

    if (index == 2)
        sum += ascii;

    index++;
    }


    free(wordCopy);

    return sum;
}

// Loads dictionary into memory, returning true if successful, else false
bool load(const char* dictionary)
{

    FILE* dict = fopen(dictionary, "r");



   int wordLen = 0;
   char* tmpWord = malloc((sizeof(char) * 45) + 1);

   //hard code the first word

   // this first word would screw our loop bc the loop compares the null at the end of a word to a previous null
   //this first word has no previous null to compare to
    int index = 0;

    char c;
    while (fread(&c, sizeof(char), 1, dict))
    {

        if (c != '\n')
        {
            tmpWord[index] = c;
            index++;
        }
        else
        {
            break;
        }
    }


    if (index < 1)
    {
        printf("first word broke load function\n");
        return false;
    }
//find some memory for our word
    node* firstNode = malloc(sizeof(node));

    //copy over the word into the node
    strncpy(firstNode->word, tmpWord, index);

    int code = hash(firstNode->word);

    firstNode->next = table[code];
    table[code] = firstNode;
    dictSize++;


    int lastNull = index - 1;
    int tmpWordIndex = 0;

    //now we can loop thru that ish!
    while (fread(&c, sizeof(char), 1, dict))
    {


        if (c != '\n' && c != '[=10=]')
        {
            tmpWord[tmpWordIndex] = c; //this starts copying into the tmp word at 0
            tmpWordIndex++;
        }
        else // we have reached the end of a string, dictionary[i] == [=10=]
        {
            wordLen = index - lastNull - 1; // -1 for the new line characters

            //create a new node to store this new word
            node* newNode = malloc(sizeof(node));

            // then we actually copy the word over from tmpWord
            strncpy(newNode->word, tmpWord, wordLen);

            code = hash(newNode->word);

            //insert node at the beginning of our list by changing pointer to current first item
            //then change the head node at table[code] to point to our new node
            newNode->next = table[code];
            table[code] = newNode;
            dictSize++;

            //reset the word index so that the next word will copy into the start of tmp word
            //reset the last null encountered to our current char, a null
            tmpWordIndex = 0;
            lastNull = index;

        }


        index++;
    }
        //do it all once more outside loop to grab the last character
            wordLen = index - lastNull - 1; 
            node* newNode = malloc(sizeof(node));
            strncpy(newNode->word, tmpWord, wordLen);
            code = hash(newNode->word);
            newNode->next = table[code];
            table[code] = newNode;


    
    free(tmpWord);
    dictLoaded = true;


    fclose(dict);
    return true;

}

// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
    if (dictLoaded)
    {
        return dictSize;
    }
    else
    {
        return 0;
    }
}

// Unloads dictionary from memory, returning true if successful, else false
bool unload(void)
{
    bool unloaded = false;
    for (int n = 0; n < N; n++)
    {
        node* tmp;
        while(table[n] != NULL)
        {
            tmp = table[n];
            table[n] = table[n]->next;
            free(tmp);
        }
        if(n == N-1)
        {
            unloaded = true;
        }
    }

    return unloaded;
}

void deleteList(node* n) 
{
    // base case is if next pointer is null
    if (n->next == NULL)
    {
        free(n);
        return;
    }

    deleteList(n->next);
}

bool checkList(node* n, const char* checkword)//recursion brah
{
    if (n == NULL)
    {
        return false;
    }
    if (strcasecmp(n->word, checkword) == 0) //error here, see below


// VALGRIND ERROR Conditional jump or move depends on uninitialised value(s): (file: dictionary.c, line: 258)
//Use of uninitialised value of size 8: (file: dictionary.c, line: 258)


    {
        return true;
    }
    else
    {
        return checkList(n->next, checkword); 
    }

}

哈佛大学的检查测试特别针对这些行给我一个错误: 在 hash(const char* word) 函数中

char* wordCopy = calloc(strlen(word), sizeof(char));
strncpy(wordCopy, word, strlen(word));
...
while (word[index] != '[=11=]' && index < 3)

我试过将 malloc 更改为 calloc。我已经尝试将我要求 malloc/calloc 的内存大小增加和减少 1,即 malloc((strlen(word) +/- 1) * sizeof(char)). 原代码在while循环中对比了wordCopy[index],但是word[index]还是报同样的错误。 我在 valgrind 上尝试了 运行 --track-origins=yes arg(见下文),但这并没有给我任何东西,只有哈佛的测试实际上告诉我错误。

speller/ $ valgrind -s --track-origins=yes ./speller
==7117== Memcheck, a memory error detector
==7117== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7117== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==7117== Command: ./speller
==7117== 
Usage: ./speller [DICTIONARY] text
==7117== 
==7117== HEAP SUMMARY:
==7117==     in use at exit: 0 bytes in 0 blocks
==7117==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==7117== 
==7117== All heap blocks were freed -- no leaks are possible
==7117== 
==7117== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

哈佛的测试结果如下:

running valgrind --show-leak-kinds=all --xml=yes --xml-file=/tmp/tmpqjc7357q -- ./speller substring/dict substring/text...
checking for output "MISSPELLED WORDS\n\nca\ncats\ncaterpill\ncaterpillars\n\nWORDS MISSPELLED: 4\nWORDS IN DICTIONARY: 2\nWORDS IN TEXT: 6\n"...
checking that program exited with status 0...
checking for valgrind errors...
Conditional jump or move depends on uninitialised value(s): (file: dictionary.c, line: 45)
Conditional jump or move depends on uninitialised value(s): (file: dictionary.c, line: 46)
Conditional jump or move depends on uninitialised value(s): (file: dictionary.c, line: 57)
Conditional jump or move depends on uninitialised value(s): (file: dictionary.c, line: 267)
Use of uninitialised value of size 8: (file: dictionary.c, line: 267) 

45、46 和 57 是上面提到的散列函数中的行。第267行如下,在代码底部的checkList函数中找到。

if (strcasecmp(n->word, checkword) == 0) 

这是我的第一个编码 class,这个特殊问题完全阻碍了我的进步。如果代码不太好看,我深表歉意。感谢阅读我的 post!

编辑:下面是 dictionary.h 和 speller.c,它们都是我预先编写的...

// Declares a dictionary's functionality

#ifndef DICTIONARY_H
#define DICTIONARY_H

#include <stdbool.h>

// Maximum length for a word
// (e.g., pneumonoultramicroscopicsilicovolcanoconiosis)
#define LENGTH 45

// Prototypes
bool check(const char* word);
unsigned int hash(const char* word);
bool load(const char* dictionary);
unsigned int size(void);
bool unload(void);

#endif // DICTIONARY_H
// Implements a spell-checker

#include <ctype.h>
#include <stdio.h>
#include <sys/resource.h>
#include <sys/time.h>

#include "dictionary.h"

// Undefine any definitions
#undef calculate
#undef getrusage

// Default dictionary
#define DICTIONARY "dictionaries/large"

// Prototype
double calculate(const struct rusage *b, const struct rusage *a);

int main(int argc, char *argv[])
{
    // Check for correct number of args
    if (argc != 2 && argc != 3)
    {
        printf("Usage: ./speller [DICTIONARY] text\n");
        return 1;
    }

    // Structures for timing data
    struct rusage before, after;

    // Benchmarks
    double time_load = 0.0, time_check = 0.0, time_size = 0.0, time_unload = 0.0;

    // Determine dictionary to use
    char *dictionary = (argc == 3) ? argv[1] : DICTIONARY;

    // Load dictionary
    getrusage(RUSAGE_SELF, &before);
    bool loaded = load(dictionary);
    getrusage(RUSAGE_SELF, &after);

    // Exit if dictionary not loaded
    if (!loaded)
    {
        printf("Could not load %s.\n", dictionary);
        return 1;
    }

    // Calculate time to load dictionary
    time_load = calculate(&before, &after);

    // Try to open text
    char *text = (argc == 3) ? argv[2] : argv[1];
    FILE *file = fopen(text, "r");
    if (file == NULL)
    {
        printf("Could not open %s.\n", text);
        unload();
        return 1;
    }

    // Prepare to report misspellings
    printf("\nMISSPELLED WORDS\n\n");

    // Prepare to spell-check
    int index = 0, misspellings = 0, words = 0;
    char word[LENGTH + 1];

    // Spell-check each word in text
    char c;
    while (fread(&c, sizeof(char), 1, file))
    {
        // Allow only alphabetical characters and apostrophes
        if (isalpha(c) || (c == '\'' && index > 0))
        {
            // Append character to word
            word[index] = c;
            index++;

            // Ignore alphabetical strings too long to be words
            if (index > LENGTH)
            {
                // Consume remainder of alphabetical string
                while (fread(&c, sizeof(char), 1, file) && isalpha(c));

                // Prepare for new word
                index = 0;
            }
        }

        // Ignore words with numbers (like MS Word can)
        else if (isdigit(c))
        {
            // Consume remainder of alphanumeric string
            while (fread(&c, sizeof(char), 1, file) && isalnum(c));

            // Prepare for new word
            index = 0;
        }

        // We must have found a whole word
        else if (index > 0)
        {
            // Terminate current word
            word[index] = '[=16=]';

            // Update counter
            words++;

            // Check word's spelling
            getrusage(RUSAGE_SELF, &before);
            bool misspelled = !check(word);
            getrusage(RUSAGE_SELF, &after);

            // Update benchmark
            time_check += calculate(&before, &after);

            // Print word if misspelled
            if (misspelled)
            {
                printf("%s\n", word);
                misspellings++;
            }

            // Prepare for next word
            index = 0;
        }
    }

    // Check whether there was an error
    if (ferror(file))
    {
        fclose(file);
        printf("Error reading %s.\n", text);
        unload();
        return 1;
    }

    // Close text
    fclose(file);


    // Determine dictionary's size
    getrusage(RUSAGE_SELF, &before);
    unsigned int n = size();
    getrusage(RUSAGE_SELF, &after);

    // Calculate time to determine dictionary's size
    time_size = calculate(&before, &after);

    // Unload dictionary
    // causing core dump!!
    getrusage(RUSAGE_SELF, &before);
    bool unloaded = unload();
    getrusage(RUSAGE_SELF, &after);

    // Abort if dictionary not unloaded
    if (!unloaded)
    {
        printf("Could not unload %s.\n", dictionary);
        return 1;
    }

    // Calculate time to unload dictionary
    time_unload = calculate(&before, &after);

    // Report benchmarks
    printf("\nWORDS MISSPELLED:     %d\n", misspellings);
    printf("WORDS IN DICTIONARY:  %d\n", n);
    printf("WORDS IN TEXT:        %d\n", words);
    printf("TIME IN load:         %.2f\n", time_load);
    printf("TIME IN check:        %.2f\n", time_check);
    printf("TIME IN size:         %.2f\n", time_size);
    printf("TIME IN unload:       %.2f\n", time_unload);
    printf("TIME IN TOTAL:        %.2f\n\n",
           time_load + time_check + time_size + time_unload);

    // Success
    return 0;
}

// Returns number of seconds between b and a
double calculate(const struct rusage *b, const struct rusage *a)
{
    if (b == NULL || a == NULL)
    {
        return 0.0;
    }
    else
    {
        return ((((a->ru_utime.tv_sec * 1000000 + a->ru_utime.tv_usec) -
                  (b->ru_utime.tv_sec * 1000000 + b->ru_utime.tv_usec)) +
                 ((a->ru_stime.tv_sec * 1000000 + a->ru_stime.tv_usec) -
                  (b->ru_stime.tv_sec * 1000000 + b->ru_stime.tv_usec)))
                / 1000000.0);
    }
}

编辑: pm100 的回答帮助缓解了一切,除了我的哈希函数中的 while 循环检查。我认为我传递给散列的所有单词都应该在适当的索引中以 null 结尾。我将留下我的旧代码作为参考。 以下是我当前相关功能的代码。我在加载函数中添加了空值,因为此函数将乱码字符串传递给给出错误的散列函数。 为了消除哈希中的 malloc 错误,我想我可以只复制当前索引处的字符,而不是引用传递给函数的整个单词的副本。那有帮助。但是出于某种原因,在 while 循环条件中将 d char 与 null 进行比较并不好。


// Hashes word to a number
unsigned int hash(const char* word)
{
    int index = 0;

    int sum = 0;
    //iterate thru the word until it ends or we get to our requisite 3 characters
    //must accomodate any capitalization or apostraphes
    char d = word[0];
    while (d != '[=17=]' && index < 3) // this line is still an an error, just like when it word was wordcopy
    {

        //change chars to int vals so that null is 0, a is 1, z is 26, and ' is 27
        int ascii = 0; // this remains unchanged if the current char is null
        if (isalpha(d) != 0)
        {
            d = tolower(d);
            ascii = d - 96;
        }
        else if (d == '\'')
        {
            ascii = 27;
        }

// add the current chars val to the sum of the word's first three vals
// the math here ensures that "null null null" will be 0, and ''' will be 21,951 or the last index of our hash map
    if (index == 0)
        sum += ascii * 784;

    if (index == 1)
        sum += ascii * 28;

    if (index == 2)
        sum += ascii;

    index++;
    d = word[index];

    }



    return sum;
}

// Loads dictionary into memory, returning true if successful, else false
bool load(const char* dictionary)
{
    FILE* dict = fopen(dictionary, "r");



   int wordLen = 0;
   char* tmpWord = malloc((sizeof(char) * 45) + 1);

   //hard code the first word

   // this first word would screw our loop bc the loop compares the null at the end of a word to a previous null
   //this first word has no previous null to compare to
    int index = 0;

    char c;
    while (fread(&c, sizeof(char), 1, dict))
    {
        //printf("%c\n", c);
        if (c != '\n')
        {
            tmpWord[index] = c;
            index++;
        }
        else
        {
            break;
        }
    }
    tmpWord[index] = '[=17=]';

    if (index < 1)
    {
        printf("first word broke load function\n");
        return false;
    }
//find some memory for our word
    node* firstNode = malloc(sizeof(node));

    //copy over the word into the node
    strncpy(firstNode->word, tmpWord, index + 1);

    int code = hash(tmpWord);


    firstNode->next = table[code];
    table[code] = firstNode;
    dictSize++;


    int lastNull = index - 1;
    int tmpWordIndex = 0;

    //now we can loop thru that!
    while (fread(&c, sizeof(char), 1, dict))
    {

        if (c != '\n' && c != '[=17=]')
        {
            tmpWord[tmpWordIndex] = c; //this starts copying into the tmp word at 0
            tmpWordIndex++;
        }
        else // we have reached the end of a string, dictionary[i] == \n
        {
            wordLen = index - lastNull - 1; // -2 for the null and new line characters

            //create a new node to store this new word
            node* newNode = malloc(sizeof(node));
            tmpWord[tmpWordIndex] = '[=17=]';
            // then we actually copy the word over from tmpWord
            strncpy(newNode->word, tmpWord, wordLen + 1);
            //insert node at the beginning of our list by changing pointer to current first item
            //then change the head node at table[code] to point to our new node
            newNode->next = table[code];
            table[code] = newNode;
            dictSize++;

            //reset the word index so that the next word will copy into the start of tmp word
            //reset the last null encountered to our current char, a null
            tmpWordIndex = 0;
            lastNull = index;

        }


        index++;
    }

            wordLen = index - lastNull - 1;
            node* newNode = malloc(sizeof(node));
            tmpWord[tmpWordIndex] = '[=17=]';
            strncpy(newNode->word, tmpWord, wordLen);
            code = hash(newNode->word);
            newNode->next = table[code];
            table[code] = newNode;



    free(tmpWord);
    dictLoaded = true;

    fclose(dict);
    return true;

}

哈希函数里面的这一行while (d != '[=19=]' && index < 3)还在返回条件跳转valgrind错误

好的,你的主要问题是空终止符,我不太理解你们的逻辑,我只是​​用一个简单的 4 字文件调用了加载。

您必须确保您为 null 赚了足够的 space 并且您必须确保您有一个 null。

所以

我找到的,你必须修复其他的

char c;
while (fread(&c, sizeof(char), 1, dict))
{

    if (c != '\n')
    {
        tmpWord[index] = c;
        index++;
    }
    else
    {
        break;
    }
}
tmpWord[index] = 0; <<<<===== add the null at the end

   //copy over the word into the node
   // +1 for the null
    strncpy(firstNode->word, tmpWord, index + 1);

现在在哈希中

char* wordCopy = calloc(strlen(word)+1, sizeof(char));
strncpy(wordCopy, word, strlen(word)+1); 

+1, +1

我确定还有其他人。

valgrind 抱怨的原因是因为没有像 strlen 这样的尾随 null 函数会一直读入内存,直到他们找到一个随机的 null,valgrind 看到你从复制的末尾读取字符

为确保您拥有空值,请输入 printf 或在调试器中暂停并查看字符串。如果你看到

 "cat"

一切都很好,但如果你看到

 "cat!!@*foodle!....ll"

那么你知道你错过了 null