【问题标题】:Why Valgrind complains about fgets?为什么 Valgrind 抱怨 fgets?
【发布时间】:2020-03-25 09:23:52
【问题描述】:

我有一个简单的 C 代码。

ma​​in.c

int main() {
    FILE *stream = fopen("../input.txt", "r");
    cube test_cube;
    cube_initialization(&test_cube, stream);

/* and some code with free memory which is not needed to understand the situation */

}

cube.c

/* just the function valgrind complains about */

void cube_initialization(cube *cube, FILE *input_file) {
    int side_length;
    char buffer[BUF_SIZE];
    char waste_buffer[BUF_SIZE];


    fgets(buffer, BUF_SIZE, input_file);
    fgets(waste_buffer, BUF_SIZE, input_file); /* empty line */
    side_length = (int) strtol(buffer, NULL, 10);
    cube->side_length = side_length;
    cube->cube_array = malloc(side_length * sizeof(int **));
    int z;
    for (z = 0; z < side_length; z++) {
        cube->cube_array[z] = malloc(side_length * sizeof(int *));
        int y;
        for (y = 0; y < side_length; y++) {
            cube->cube_array[z][y] = malloc(side_length * sizeof(int));
        }
    }
}

和valgrind输出

==8251== Invalid read of size 4
==8251==    at 0x48F3727: fgets (iofgets.c:47)
==8251==    by 0x1093C2: cube_initialization (cube.c:11)
==8251==    by 0x10928D: main (main.c:11)
==8251==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==8251== 
==8251== 
==8251== Process terminating with default action of signal 11 (SIGSEGV)
==8251==  Access not within mapped region at address 0x0
==8251==    at 0x48F3727: fgets (iofgets.c:47)
==8251==    by 0x1093C2: cube_initialization (cube.c:11)
==8251==    by 0x10928D: main (main.c:11)
==8251==  If you believe this happened as a result of a stack
==8251==  overflow in your program's main thread (unlikely but
==8251==  possible), you can try to increase the size of the
==8251==  main thread stack using the --main-stacksize= flag.
==8251==  The main thread stack size used in this run was 8388608.
==8251== 
==8251== HEAP SUMMARY:
==8251==     in use at exit: 0 bytes in 0 blocks
==8251==   total heap usage: 1 allocs, 1 frees, 488 bytes allocated
==8251== 
==8251== All heap blocks were freed -- no leaks are possible
==8251== 
==8251== For lists of detected and suppressed errors, rerun with: -s
==8251== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)

我不明白 Valgrind 为什么抱怨 fgets()。我从文件中读取数据并使用带有指向大缓冲区(256)的指针的fgets,但它只需要我读取大约 1-6 个符号的短行(我需要 fgets,因为它在末尾找到 '\n' 时会停止线)。也许问题出在 fgets() 尝试读取 256 个符号的行并且它在 1-5 和 '\n' 之后停止?

【问题讨论】:

  • 您忘记验证文件是否已成功打开。
  • C 和 C++ 是不同的语言。除非您询问它们之间的差异,否则不要同时标记两者。
  • fopen之后检查stream指针,然后将其传递给cube_initialization()
  • @molbdnilo 谢谢,它有帮助。这是因为 valgrind 在自己的环境中运行并且看不到我的文件吗? P.s 对不起 c++ 标签,我认为 valgrind 是一个用于 C/C++ 泄漏检查的工具,我也可以将它标记为 c++。

标签: c valgrind fopen


【解决方案1】:

Valgrind 投诉非法地址0x0 访问:

地址 0x0 没有被堆栈、malloc 或(最近)释放

在您的fgets() 电话中

fgets(buffer, BUF_SIZE, input_file);

buffer 参数很好,因为它是在堆栈中分配的。唯一负责的可能是input_file 参数,来自main()。该参数是从fopen() 调用获得的流。你在传递给cube_initialization()之前没有检查它!

检查一下,然后明白为什么它返回NULL(可能你试图打开一个不存在的路径)。

int main() {
    FILE *stream = fopen("../input.txt", "r");
    cube test_cube;
    if( stream != NULL )
    {
        cube_initialization(&test_cube, stream);

        /* Continue execution*/
    }
}

【讨论】:

  • 我认为它返回 NULL 因为程序在特殊的 vlagrind 环境中运行并且看不到我的文件路径。是否正确?
【解决方案2】:

Valgrind 抱怨 fgets() 中的读取无效。问题不太可能是目标数组的大小,这可能会触发无效写入,但确实是 BUF_SIZE 字节的数组,您将其记录为 256 的合理值。问题更可能与流指针有关,正如 Valgrind 报告的那样,它可能为空:

Address 0x0 is not stack'd, malloc'd or (recently) free'd`

您应该修复代码中潜在的未定义行为:

  • 验证fopen() 是否返回了有效的FILE*,以避免在您尝试从input_file 读取数据时出现未定义的行为。
  • 验证fgets() 是否成功(不返回NULL),以避免从目标数组读取时出现未定义的行为。

【讨论】:

  • 感谢您的解释。这也帮助我更好地了解情况!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-12
  • 2019-08-14
  • 2015-06-09
  • 2020-08-12
  • 2016-02-27
  • 1970-01-01
  • 2018-11-17
相关资源
最近更新 更多