[英]Weird behaviour with variable length arrays in struct in C
我遇到了一个被某些人称为“ Struct Hack”的概念,我们可以在结构内部声明一个指针变量,如下所示:
struct myStruct{
int data;
int *array;
};
之后,当我们在main()
函数中使用malloc
为struct myStruct
分配内存时,我们可以在同一步骤中同时为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()释放整个块,而在第二种情况下可以进行两次调用。
做实验时,我这样写:
#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循环中将“ i”增加到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;
是的,您在此处看到的是一个未定义行为的示例。
写超出已分配数组的末尾(aka缓冲区溢出)是未定义行为的一个很好的例子:它通常看起来像是“正常工作”,而其他时候它会崩溃(例如,“分段错误”)。
低级解释:内存中有一些控制结构,这些结构与您分配的对象相距一定距离。 如果您的程序发生较大的缓冲区溢出,则很有可能损坏这些控制结构,而对于较小的溢出,则将损坏一些未使用的数据(例如,填充)。 但是,无论如何,缓冲区溢出会调用未定义的行为。
第一种形式的“结构hack”也会调用未定义的行为(如警告所示),但是它是一种特殊的行为-几乎可以保证,在大多数编译器中,它始终可以正常工作。 但是,它仍然是未定义的行为,因此不建议使用。 为了批准其使用,C委员会发明了这种“灵活数组成员”语法(您的第二种语法),该语法可以保证正常工作。
为了明确起见-分配给数组的元素永远不会为该元素分配空间(至少不在C中)。 在C语言中,分配给元素时,即使数组是“ flexible”的,也应该已经分配了它。 您的代码应该知道分配内存时要分配多少。 如果您不知道要分配多少,请使用以下技术之一:
struct myStruct{ int data; int array[100]; // you will never need more than 100 numbers };
struct myStruct{ int data; int array[100]; // you will never need more than 100 numbers };
realloc
您所说的“结构黑客”确实是一种黑客。 这是不值得的IMO。
p->array = p+1;
在许多需要显式转换的编译器上会给您带来问题:
p->array = (int *) (p+1);
我能够执行它而没有任何问题,没有任何分割错误。 对我来说看起来很奇怪。
这是不确定的行为。 您正在访问堆上的内存,许多编译器和操作系统都不会阻止您这样做。 但是使用它是非常糟糕的做法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.