简体   繁体   English

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

[英]Code crashes unless I put a printf statement in it

This is a snippet of code from an array library I'm using. 这是我正在使用的数组库的代码片段。 This runs fine on windows, but when I compile with gcc on linux if crashes in this function. 这在windows上运行正常,但是当我在linux上用gcc编译时如果在这个函数中崩溃了。 when trying to narrow down the problem, I added a printf statement to it, and the code stopped crashing. 当我试图缩小问题的范围时,我向它添加了一个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);
}

As soon as that printf is taken out it starts crashing on me again. 一旦取出printf,它就会再次开始撞击我。 I'm completely baffled as to why it's happening. 我完全不知道为什么会这样。

The last line in the function is not doing what was intended. 函数的最后一行没有按预期执行。 The code is obscure to the point of impenetrability. 代码模糊到不可穿透性的程度。

It appears that the goal is to allocate an array of int , because of the sizeof(int) in the first memory allocation. 似乎目标是分配一个int数组,因为第一个内存分配中的sizeof(int) At the very least, if you are meant to be allocating an array of structure pointers, you need to use sizeof(SomeType *) , the size of some pointer type ( sizeof(void *) would do). 至少,如果你要分配一个结构指针数组,你需要使用sizeof(SomeType *) ,一些指针类型的大小( sizeof(void *)会这样做)。 As written, this will fail horribly in a 64-bit environment. 如上所述,这将在64位环境中失败。

The array is allocated with a structure header ( ArrayHeader ) followed by the array proper. 该数组分配有结构头( ArrayHeader ),后面是数组。 The returned value is supposed to the start of the array proper; 返回的值应该是数组的正确开始; the ArrayHeader will presumably be found by subtraction from the pointer. 大概可以通过从指针中减去找到ArrayHeader。 This is ugly as sin, and unmaintainable to boot. 这是罪恶的丑陋,并且无法维持。 It can be made to work, but it requires extreme care, and (as Brian Kernighan said) "if you're as clever as possible when you write the code, how are you ever going to debug it?". 它可以工作,但它需要非常小心,并且(正如Brian Kernighan所说)“如果你在编写代码时尽可能聪明,那么你将如何进行调试?”。

Unfortunately, the last line is wrong: 不幸的是,最后一行是错误的:

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);
}

It adds sizeof(ArrayHeader) * sizeof(char *) to the address, instead of the intended sizeof(ArrayHeader) * sizeof(char) . 它将sizeof(ArrayHeader) * sizeof(char *)到地址,而不是预期的sizeof(ArrayHeader) * sizeof(char) The last line should read, therefore: 因此,最后一行应该是:

*(char *)array += sizeof(ArrayHeader);

or, as noted in the comments and an alternative answer: 或者,正如评论和备选答案中所述:

*(ArrayHeader *)array += 1;
*(ArrayHeader *)array++;

I note in passing that the function name should not really start with an underscore. 我注意到,函数名称不应该以下划线开头。 External names starting with an underscore are reserved to the implementation (of the C compiler and library). 以下划线开头的外部名称保留给(C编译器和库)的实现。


The question asks "why does the printf() statement 'fix' things". 问题是“为什么printf()语句'修复'事物”。 The answer is because it moves the problem around. 答案是因为它解决了问题。 You've got a Heisenbug because there is abuse of the allocated memory, and the presence of the printf() manages to alter the behaviour of the code slightly. 你有一个Heisenbug,因为滥用了分配的内存, printf()的存在设法稍微改变了代码的行为。

Recommendation 建议

  1. Run the program under valgrind . valgrind下运行程序。 If you don't have it, get it. 如果你没有它,那就去吧。
  2. Revise the code so that the function checks the return value from malloc() , and so it returns a pointer to a structure for the allocated array. 修改代码,以便函数检查malloc()的返回值,因此它返回指向已分配数组的结构的指针。
  3. Use the clearer code outlined in Michael Burr 's answer. 使用Michael Burr的答案中概述的更清晰的代码。

Arbitrary random crashing when adding seemingly unrelated printf() statements often is a sign of a corrupted heap. 添加看似无关的printf()语句时,任意随机崩溃通常是堆损坏的标志。 The compiler sometimes stores information about allocated memory directly on the heap itself. 编译器有时会直接在堆本身上存储有关已分配内存的信息。 Overwriting that metadata leads to surprising runtime behavior. 覆盖该元数据会导致令人惊讶的运行时行为。

A few suggestions: 一些建议:

  • are you sure that you need void *** ? 你确定你需要void ***吗?
  • try replacing your argument to malloc() with 10000 . 尝试用10000替换你的参数malloc() Does it work now? 它现在有效吗?


Moreover, if you just want arrays that store some metadata, your current code is a bad approach. 此外,如果您只想要存储一些元数据的数组,那么您当前的代码是一种糟糕的方法。 A clean solution would probably use a structure like the following: 一个干净的解决方案可能会使用如下结构:

struct Array {
    size_t nmemb;    // size of an array element
    size_t size;     // current size of array
    size_t capacity; // maximum size of array
    void *data;      // the array itself
};

Now you can pass an object of type Array to functions that know about the Array type, and Array->data cast to the proper type to everything else. 现在,您可以将Array类型的对象传递给了解Array类型的函数,并将Array->data传递给其他所有类型的正确类型。 The memory layout might even be the same as in your current approach, but access to the metadata is significantly easier and especially more obvious. 内存布局甚至可能与您当前的方法相同,但访问元数据要容易得多,尤其是更加明显。

Your main audience is the poor guy that has to maintain your code 5 years from now. 您的主要受众是5年后必须维护您的代码的穷人。

Now that Jonathan Leffler has pointed out what the bug was , might I suggest that the function be written in a manner that's a little less puzzling?: 既然Jonathan Leffler已经指出了这个bug是什么 ,我是否可以建议以一种不那么令人费解的方式编写函数?:

void _arrayCreateSize( void ***array, int capacity )
{
    // aloocate a header followed by an appropriately sized array of pointers
    ArrayHeader* p = malloc( sizeof(ArrayHeader) + (capacity * sizeof(void*)));

    p->size = 0;
    p->capacity = capacity;

    *array = (void**)(p+1);   // return a pointer to just past the header 
                              //   (pointing at the array of pointers)
}

Mix in your own desired handling of malloc() failure. 混合你自己想要的malloc()失败处理。

I think this will probably help the next person who needs to look at it. 我认为这可能会帮助下一个需要关注它的人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM