除非我在其中放入printf语句,否则代码会崩溃

Ale*_*lex 2 c debugging

这是我正在使用的数组库中的代码片段。这在Windows上运行良好,但是如果我在Linux上使用gcc进行编译(如果该函数崩溃),则可以正常运行。当试图缩小问题范围时,我向其添加了一条printf语句,并且代码停止崩溃。

void _arrayCreateSize( void ***array, int capacity )
{
    (*array) = malloc( (capacity * sizeof(int)) + sizeof(ArrayHeader) );
    ((ArrayHeader*)(*array))->size = 0;
    ((ArrayHeader*)(*array))->capacity = capacity;
    // printf("Test!\n");
    *(char**)array += sizeof(ArrayHeader);
}
Run Code Online (Sandbox Code Playgroud)

取出printf后,它再次开始崩溃。我完全不知道为什么会这样。

Jon*_*ler 5

函数的最后一行没有按照预期执行。该代码难以理解。

看来目标是分配的数组int,因为sizeof(int)在第一个内存分配中。至少,如果要分配结构指针数组,则需要使用sizeof(SomeType *),某种指针类型的大小(sizeof(void *)可以)。如所写,这将在64位环境中严重失败。

该数组分配有一个结构头(ArrayHeader),后跟适当的数组。返回值应该在数组的开始处;可以通过从指针中减去来找到ArrayHeader。这是罪恶的丑陋现象,无法启动。它可以工作,但需要格外小心,而且(如Brian Kernighan所说)“如果编写代码时尽可能聪明,那么如何调试它?”。

不幸的是,最后一行是错误的:

void _arrayCreateSize( void ***array, int capacity )
{
    (*array) = malloc( (capacity * sizeof(int)) + sizeof(ArrayHeader) );
    ((ArrayHeader*)(*array))->size = 0;
    ((ArrayHeader*)(*array))->capacity = capacity;
    // printf("Test!\n");
    *(char**)array += sizeof(ArrayHeader);
}
Run Code Online (Sandbox Code Playgroud)

它添加sizeof(ArrayHeader) * sizeof(char *)到地址,而不是预期的sizeof(ArrayHeader) * sizeof(char)。最后一行应为:

*(char *)array += sizeof(ArrayHeader);
Run Code Online (Sandbox Code Playgroud)

或者,如评论和替代答案中所述:

*(ArrayHeader *)array += 1;
*(ArrayHeader *)array++;
Run Code Online (Sandbox Code Playgroud)

我注意到,函数名称实际上不应以下划线开头。以下划线开头的外部名称保留给(C编译器和库)实现使用。


问题问“为什么printf()语句“修复”东西”。答案是因为它可以解决问题。您有一个Heisenbug,因为滥用了分配的内存,并且printf()管理程序的存在会稍微改变代码的行为。

建议

  1. 在下运行程序valgrind。如果没有,请获取。
  2. 修改代码,以便函数检查的返回值malloc(),并返回指向分配数组结构的指针。
  3. 使用Michael Burr的答案中列出的更清晰的代码。

  • 或者只是`*(ArrayHeader *)array + = 1`。 (3认同)