这是我正在使用的数组库的代码片段。这在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);
}
一旦取出printf,它就会再次开始撞击我。我完全不知道为什么会这样。
答案 0 :(得分: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);
}
它会将sizeof(ArrayHeader) * sizeof(char *)
添加到地址,而不是预期的sizeof(ArrayHeader) * sizeof(char)
。因此,最后一行应该是:
*(char *)array += sizeof(ArrayHeader);
或者,正如评论和备选答案中所述:
*(ArrayHeader *)array += 1;
*(ArrayHeader *)array++;
我顺便注意到函数名称不应该以下划线开头。以下划线开头的外部名称保留给(C编译器和库)的实现。
问题是“为什么printf()
陈述'修复'事物”。答案是因为它解决了问题。你有一个Heisenbug,因为滥用了已分配的内存,printf()
的存在设法稍微改变了代码的行为。
valgrind
下运行该程序。如果你没有它,那就去吧。malloc()
的返回值,因此它返回指向已分配数组的结构的指针。答案 1 :(得分:2)
添加看似无关的printf()
语句时,任意随机崩溃通常表示堆已损坏。编译器有时会直接在堆本身上存储有关已分配内存的信息。覆盖该元数据会导致令人惊讶的运行时行为。
一些建议:
void ***
吗?malloc()
将您的参数替换为10000
。它现在有效吗?
此外,如果您只想要存储一些元数据的数组,那么您当前的代码是一种糟糕的方法。一个干净的解决方案可能会使用如下结构:
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
};
现在,您可以将类型为Array
的对象传递给知道Array
类型的函数,并将Array->data
强制转换为适当的类型。内存布局甚至可能与您当前的方法相同,但访问元数据要容易得多,尤其是更明显。
您的主要受众是5年后必须维护您的代码的穷人。
答案 2 :(得分:1)
既然是Jonathan Leffler has pointed out what the bug was,我是否可以建议以一种不那么令人费解的方式编写函数?:
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)
}
混合您自己想要的malloc()
失败处理。
我认为这可能会帮助下一个需要查看它的人。