Valgrind使用单位化值和无效读数

时间:2018-02-17 02:47:32

标签: c arrays pointers valgrind

我正在尝试将包含不同行的文本文件读入数组。该数组称为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)

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

2 个答案:

答案 0 :(得分:3)

几个问题和建议: -

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

  • 另外一件事 - 您检查字符串长度是否大于MAX_LINE_LEN是错误的。你永远不能从中推断出“线太长”。读取fgets返回值以获得所需的行为。 (它读取MAX_LINE_LEN-1个字符,最后一个位置包含\0。因此,您可以确定正在读取完整行的一种方法是找到\n - 如果它在那里,那么正在读完整行。)

    fgets返回NULL时,您正在释放它们,但是再次将其设置为NULL,以便在打印时可以选择性地打印有效的({1}}那些有NULL)的人。你也必须考虑这个问题。是否从fgets返回是由于错误情况 - 将使用feof()ferror()进行检查。如果您使用的是POSIX,则fgets会设置errno,如果它遇到除文件结束条件之外的某些故障。检查reference以获得更多想法。

  • 在这种情况下,您分配的内存量大于您所需的内存量 - 但使用错误的type的方式会产生问题。 char *lineArr = (char *) malloc(sizeof(char*) * (MAX_LINE_LEN + 1));您要为MAX_LINE_LEN+1 char*分配空间 - 应该是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)); 
    

答案 1 :(得分:1)

由于lineArr是一个字符数组而不是字符串数组,因此应该将sizeof(char)作为参数而不是sizeof(char *)

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

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;