我遇到了一个人们称之为" Struct Hack"我们可以在结构中声明一个指针变量,如下所示:
struct myStruct{
int data;
int *array;
};
以及稍后当我们在struct myStruct
函数中使用malloc
为main()
分配内存时,我们可以在同一步骤中同时为int *array
指针分配内存,如这样:
struct myStruct *p = malloc(sizeof(struct myStruct) + 100 * sizeof(int));
p->array = p+1;
而不是
struct myStruct *p = malloc(sizeof(struct myStruct));
p->array = malloc(100 * sizeof(int));
假设我们想要一个大小为100的数组。
据说第一个选项更好,因为我们可以获得连续的内存块,我们可以在后一种情况下通过一次调用free()和2次调用来释放整个块。
试验,我写了这个:
#include<stdio.h>
#include<stdlib.h>
struct myStruct{
int i;
int *array;
};
int main(){
/* I ask for only 40 more bytes (10 * sizeof(int)) */
struct myStruct *p = malloc(sizeof(struct myStruct) + 10 * sizeof(int));
p->array = p+1;
/* I assign values way beyond the initial allocation*/
for (int i = 0; i < 804; i++){
p->array[i] = i;
}
/* printing*/
for (int i = 0; i < 804; i++){
printf("%d\n",p->array[i]);
}
return 0;
}
我可以毫无问题地执行它,没有任何分段错误。看起来很奇怪。
我也知道C99有一条规定说,我们可以int *array
而不是在结构中声明int array[]
,我只使用malloc()
执行此操作结构,如
struct myStruct *p = malloc(sizeof(struct myStruct));
并像这样初始化array []
p->array[10] = 0; /* I hope this sets the array size to 10
and also initialises array entries to 0 */
但是再一次这种奇怪的地方,我能够访问并分配超出数组大小的数组索引,并打印条目:
for(int i = 0; i < 296; i++){ // first loop
p->array[i] = i;
}
for(int i = 0; i < 296; i++){ // second loop
printf("%d\n",p->array[i]);
}
打印p->array[i]
到i = 296
后,它会给我一个细分错误,但显然除了i = 9
之外没有问题。
(如果我在上面的第一个for循环中增加&#39; i&#39;直到300,我立即得到分段错误,程序不会打印任何值。)
关于发生了什么的任何线索?它是未定义的行为还是什么?
编辑:当我使用命令
编译第一个片段时cc -Wall -g -std=c11 -O struct3.c -o struct3
我收到了这个警告:
warning: incompatible pointer types assigning to 'int *' from
'struct str *' [-Wincompatible-pointer-types]
p->array = p+1;
答案 0 :(得分:3)
是的,你在这里看到的是一个未定义行为的例子。
写入超出已分配数组的末尾(也就是缓冲区溢出)是未定义行为的一个很好的例子:它经常会出现在#34;正常工作&#34;,而有时它会崩溃(例如&#34;分段错误&#34;)。
一个低级别的解释:内存中的控制结构与您分配的对象相距一定距离。如果你的程序有很大的缓冲区溢出,那么它将更有可能损坏这些控制结构,而对于更温和的溢出,它会损坏一些未使用的数据(例如填充)。但是,在任何情况下,缓冲区溢出都会调用未定义的行为。
&#34; struct hack&#34;在你的第一种形式中也会调用未定义的行为(如警告所示),但是在一些特殊类型中 - 它几乎可以保证在大多数编译器中它总能正常工作。但是,它仍然是未定义的行为,因此不建议使用。为了制裁它的使用,C委员会发明了这个&#34;灵活的阵列成员&#34;语法(你的第二种语法),保证可以工作。
只是为了说清楚 - 对数组元素的赋值永远不会为该元素分配空间(至少在C中)。在C中,当分配给一个元素时,它应该已经被分配,即使该数组是&#34;灵活的&#34;。您的代码应该知道在分配内存时要分配多少。如果您不知道要分配多少,请使用以下技术之一:
struct myStruct{
int data;
int array[100]; // you will never need more than 100 numbers
};
realloc
答案 1 :(得分:0)
你所描述的&#34; Struct Hack&#34;确实是一个黑客。这不值得IMO。
p->array = p+1;
会在许多需要显式转换的编译器上给你带来问题:
p->array = (int *) (p+1);
我可以毫无问题地执行它,没有任何分段错误。看起来很奇怪。
这是未定义的行为。您正在访问堆上的内存,许多编译器和操作系统不会阻止您这样做。但使用它是非常糟糕的做法。