Valgrind 使用未初始化的值和无效读取

Valgrind Use of unitialized value and Invalid read

我正在尝试将包含不同行的文本文件读入数组。该数组称为fileArr,它是一个指针数组,每个索引指向一个lineArr字符串。

正在打开文件并检查文件长度。但是当我开始将文件读入数组时,就会出现问题。这是我的代码:

int main()
{   
    fseek(file, 0, SEEK_END);   // seek to end of file
    int fileLen = ftell(file);  // get current file pointer
    fseek(file, 0, SEEK_SET);   // seek back to beginning of file
    printf("file length = %d\n", fileLen);

    char **fileArr = malloc(sizeof(char*) * fileLen);
    char *lineArr = (char *) malloc(sizeof(char*) * (MAX_LINE_LEN + 1));
    int i = 0;

    while(1) {
        fileArr[i] = malloc(sizeof(char*) * (MAX_LINE_LEN + 1)); //This is line 73

        // Read from the file
        if(fgets(lineArr, MAX_LINE_LEN, file) != NULL)  {
            // Check if line is too long
            if(strlen(lineArr) > MAX_LINE_LEN) {
                fprintf(stderr, "Line too long");
                exit(1);  // exit with return code 1
            }
            // If not, write content in one line to array
            strcpy(fileArr[i], lineArr);  
        }
        else {  // If reach to the end of file
            // Free the fileArr at index i
            free(fileArr[i]);
            break;
        }
        i++;
    }
    // Then print out the array
    printLines(fileArr, fileLen); // This is line 91

    // Free memory
    free(lineArr);
    free(fileArr);
    return 0;
}

/** Method to print out array **/
void printLines (char *ptArray[], size_t count)
{
    for (size_t i = 0; i < count; i++) {
        printf("%s\n", ptArray[i]); // This is line 100 
    }
}

文件已打印出来,但出现分段错误。然后 valgrind 打印出这个巨大的可怕消息(包含此代码的文件的名称是 textsort.c):

==13032== Invalid read of size 1
==13032==    at 0x4C30F62: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032==    by 0x4EA969B: puts (ioputs.c:35)
==13032==    by 0x400DB0: printLines (textsort3.c:100)
==13032==    by 0x400D54: main (textsort3.c:91)
==13032==  Address 0x5207180 is 0 bytes inside a block of size 1,032 free'd
==13032==    at 0x4C2EDEB: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032==    by 0x400D28: main (textsort3.c:83)
==13032==  Block was alloc'd at
==13032==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032==    by 0x400C88: main (textsort3.c:73)
==13032== 

==13032== Use of uninitialised value of size 8
==13032==    at 0x4C30F62: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032==    by 0x4EA969B: puts (ioputs.c:35)
==13032==    by 0x400DB0: printLines (textsort3.c:100)
==13032==    by 0x400D54: main (textsort3.c:91)
==13032== 
==13032== 
==13032== Process terminating with default action of signal 11 (SIGSEGV)
==13032==  Access not within mapped region at address 0x0
==13032==    at 0x4C30F62: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==13032==    by 0x4EA969B: puts (ioputs.c:35)
==13032==    by 0x400DB0: printLines (textsort3.c:100)
==13032==    by 0x400D54: main (textsort3.c:91)
==13032==  If you believe this happened as a result of a stack
==13032==  overflow in your program's main thread (unlikely but
==13032==  possible), you can try to increase the size of the
==13032==  main thread stack using the --main-stacksize= flag.
==13032==  The main thread stack size used in this run was 8388608.
==13032== 
==13032== HEAP SUMMARY:
==13032==     in use at exit: 6,304 bytes in 6 blocks
==13032==   total heap usage: 10 allocs, 4 frees, 13,008 bytes allocated
==13032== 
==13032== LEAK SUMMARY:
==13032==    definitely lost: 0 bytes in 0 blocks
==13032==    indirectly lost: 0 bytes in 0 blocks
==13032==      possibly lost: 0 bytes in 0 blocks
==13032==    still reachable: 6,304 bytes in 6 blocks
==13032==         suppressed: 0 bytes in 0 blocks
==13032== Reachable blocks (those to which a pointer was found) are not shown.
==13032== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==13032== 
==13032== For counts of detected and suppressed errors, rerun with: -v
==13032== Use --track-origins=yes to see where uninitialised values come from
==13032== ERROR SUMMARY: 3 errors from 2 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)

它的问题是什么?你有什么建议吗?感谢您的帮助!

几个问题和建议:-

  • printLines 函数中,您总是打印直到索引达到 fileLen。现在,如果 fileLen 不等于 i 或更高的值 - 它将尝试访问未初始化的值并将它们传递给 printf 将是未定义的行为。 (chux评论和alk评论)

  • 还有一件事——你检查的字符串长度大于 MAX_LINE_LEN 是错误的。你永远不能从中推断出 "Line is too Long"。读取 fgets return 值以获得所需的行为。 (它读取 MAX_LINE_LEN-1 个字符,最后一个位置包含 [=19=]。因此,您可以确定正在读取完整行的一种方法是找到 \n - 如果它在那里,那么正在读取整行)。

    此外,当 fgets returns NULL 你正在释放它们,但你又没有将其设置为 NULL,这样当你打印时你可以选择打印有效的(不是那些有 NULL 的)。你也得考虑这个。来自 fgets 的 return 是否是由于错误条件引起的 - 将使用 feof()ferror() 检查。如果您使用 POSIX 一个,那么 fgets 设置 errno 如果遇到文件结束条件以外的其他故障。查看 reference 了解更多信息。

  • 在这种情况下,您分配的内存量比您需要的要大 - 但您使用错误 type 的方式会产生问题。 char *lineArr = (char *) malloc(sizeof(char*) * (MAX_LINE_LEN + 1)); 您正在为 MAX_LINE_LEN+1 char*-s 分配 space 它应该是 char-s。那就是你要存储在 lineArr 指向的内存中的内容。 fileArr[i].

    中的其他分配也是如此

    更正后(请注意,转换已删除,转换是隐式的)sizeof char 始终是 1。所以我们也可以按照评论中的方式来做。

    char *lineArr =  malloc(sizeof(char) * (MAX_LINE_LEN + 1));//malloc(MAX_LINE_LEN + 1)
    ...
    fileArr[i] = malloc(sizeof(char) * (MAX_LINE_LEN + 1)); 
    

由于 lineArr 是一个字符数组而不是字符串数组,它应该被赋予 sizeof(char) 作为参数而不是 sizeof(char*)

以下是您发布的代码的工作版本。我已经用 calloc 替换了 malloc(个人喜好,否则两者都可以)。并将 while(1) 替换为 while(fgets(lineArr, MAX_LINE_LEN, file)) 以便循环在文件结束后立即结束。

char **fileArr = (char**)calloc(sizeof(char*), fileLen);
char *lineArr = (char *)calloc(sizeof(char), (MAX_LINE_LEN + 1));
int i = 0;


while (fgets(lineArr, MAX_LINE_LEN, file))
{
    fileArr[i] = (char*)calloc(sizeof(char), (MAX_LINE_LEN + 1)); //This is line 73

    if (strlen(lineArr) > MAX_LINE_LEN)
    {
        fprintf(stderr, "Line too long");
        exit(1);
    }

    strcpy(fileArr[i], lineArr);
    i++;
}

printLines(fileArr, fileLen); 

free(lineArr);
free(fileArr);

getch();
return 0;