【发布时间】:2016-11-07 03:34:21
【问题描述】:
我遇到了一些人称之为“结构黑客”的概念,我们可以在结构中声明一个指针变量,如下所示:
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));
并像这样初始化数组[]
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,我会立即遇到分段错误,并且程序不会打印任何值。)
关于发生了什么的任何线索?是未定义的行为还是什么?
编辑:当我使用命令编译第一个 sn-p 时
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;
【问题讨论】:
-
你仍然需要为整数分配内存。
-
您的代码中没有可变长度数组。您所做的称为 flxeible 数组成员 (FAM)。你认为
malloc应该如何知道你希望这个数组包含多少个元素? -
我不会给你怎么做,因为你可以很容易地确定你是否考虑一下而不是专心询问。就是这样:您的问题中已经显示了所有必要的信息。
-
@WeatherVane:数组的长度为 1 个条目。超越边界的解除引用绝对是 UB。
-
@tectonicfury:如果您不了解所有含义,则永远不要仅仅为了使编译器静音而进行强制转换。如果你认为 UB 会因演员而神奇地消失:好吧,你错了!