C - valgrind - 大小为1的读取无效

1 c profiling valgrind

我在使用Valgrind调试代码时遇到了一些麻烦.以下是出现错误的结构和主要部分:

struct trieNode {
    char *word;
    struct trieNode *(subNode[LEAF_NUM]);
    struct sharpNode *sharp;    
};


//linked list of sharp(s)
struct sharpNode {
    char *word;
    struct sharpNode *next;
}; 
Run Code Online (Sandbox Code Playgroud)

if (head->word == NULL){ 
    //strlen(head->word) == 0){
    head->word = (char *)malloc(MAX_LEN * sizeof(char));     //LINE 191
    //memset(head->word, '\0',strlen(head->word));
    strncpy (head->word, word, strlen(word));

} else { 

    if (head->sharp == NULL) {
        head->sharp = sharpNodeCreate();
        head->sharp->word = (char *)malloc(MAX_LEN * sizeof(char));    //LINE 200
        //head->sharp->word[strlen(word)] = '\0';
        strncpy (head->sharp->word, word, strlen(word));
    }

}
Run Code Online (Sandbox Code Playgroud)

            } else if ( sharpIndex == 0 && strlen(trie_ptr->word) > 0) {
                printf("%s\n", trie_ptr->word);         //LINE 135
            } else if (notSharp == 0 && sharp_ptr != NULL) {
                printf("%s\n", sharp_ptr->word);       //LINE 137
            }  else {
                printf("There are no more T9onyms\n");
            }
Run Code Online (Sandbox Code Playgroud)

当我运行valgrind时,它会抱怨:

==20040== Invalid read of size 1
==20040==    at 0x4A09264: __GI_strlen (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==20040==    by 0x334BC6DD2B: puts (in /usr/lib64/libc-2.17.so)
==20040==    by 0x400C2C: lookupTrie (trie.c:135)
==20040==    by 0x400905: main (t9.c:23)
==20040==  Address 0x4c4e2f4 is 0 bytes after a block of size 4 alloc'd
==20040==    at 0x4A06409: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==20040==    by 0x400D6B: populateTrie (trie.c:191)
==20040==    by 0x4008F9: main (t9.c:22)
==20040== 
ace

Enter Key Sequence (or "#" for next word):
#
==20040== Invalid read of size 1
==20040==    at 0x4A09264: __GI_strlen (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==20040==    by 0x334BC6DD2B: puts (in /usr/lib64/libc-2.17.so)
==20040==    by 0x400C4A: lookupTrie (trie.c:137)
==20040==    by 0x400905: main (t9.c:23)
==20040==  Address 0x4dad294 is 0 bytes after a block of size 4 alloc'd
==20040==    at 0x4A06409: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==20040==    by 0x400DDA: populateTrie (trie.c:200)
==20040==    by 0x4008F9: main (t9.c:22)
Run Code Online (Sandbox Code Playgroud)

有人能指出我正确的方向吗?

Iwi*_*ist 6

你得到的错误是由于

strncpy (head->word, word, strlen(word));
Run Code Online (Sandbox Code Playgroud)

strncpy (head->sharp->word, word, strlen(word));
Run Code Online (Sandbox Code Playgroud)

没有用NUL字符终止他们的目标.strncpy()并不总是NUL终止其输出字符串; 事实上,它只会做,如果word(以字符数)比strncpy()的大小参数.

因此,在运行时并使用当前的源字符串和size-argument strncpy()(其中单词中的字符数精确等于且不小于size-argument),strncpy()最后不会写入NUL字符的head->word.但是当printf试图打印你的字符串时,它必须隐式地找到该字符串的结尾,用它的终止NUL字符标记.因此,它会读取您分配的所有缓冲区,而无法找到NUL字符,而且只需运气就可以在缓冲区结束后找到一个缓冲区,从而不会崩溃.然而,这是一个无效的读物; Valgrind找到了它,但你必须解决它.

要修复它,我建议您替换strncpy()with 的size-argument MAX_LEN-1,并手动终止字符串副本head->word[MAX_LEN-1] = '\0'.

或者你可以像我之前那样做,并实现一个strzcpy(char* d, char* s, size_t len)复制len-1字符和NUL终止的功能.遗憾的是,这种功能直到C11才被标准化.


n. *_* m. 5

 strncpy (head->word, word, strlen(word));
Run Code Online (Sandbox Code Playgroud)

旁白:这不是我第一次看到这种模式。它如何设法自我传播?这个模因的承载者应该已经被自然选择杀死了(如果只是......)

strncpy是为了防止缓冲区溢出而发明的。缓冲区是函数的第一个参数(在其中填充数据)。当您尝试填充超过其所能容纳的数据时,就会发生溢出。为了防止这种情况发生,您可以通过告诉strncpy 缓冲区能够容纳多少数据来限制可以放入其中的数据量。您传入了THE SIZE OF THE BUFFER。您不会传递必须复制的数据量。strncpy完全能够自己弄清楚。它无法知道复制多少数据是安全的这就是为什么你需要在一个单独的参数中传递给它的数量

当然,您必须以空字符结尾您的字符串。strncpy如果你让它填满你的缓冲区,就不会为你做这件事。