Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/58.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 结构末尾大小为0的数组_C_Arrays_Pointers_Gcc_Struct - Fatal编程技术网

C 结构末尾大小为0的数组

C 结构末尾大小为0的数组,c,arrays,pointers,gcc,struct,C,Arrays,Pointers,Gcc,Struct,我今天要上的一门系统编程课程的教授告诉我们,要定义一个末尾带有零长度数组的结构: struct array{ size_t size; int data[0]; }; typedef struct array array; 这是一个有用的结构,用于定义或初始化带有变量的数组,例如,如下所示: array *array_new(size_t size){ array* a = malloc(sizeof(array) + size * sizeof(int));

我今天要上的一门系统编程课程的教授告诉我们,要定义一个末尾带有零长度数组的结构:

struct array{
    size_t size;
    int data[0];
};

typedef struct array array;
这是一个有用的结构,用于定义或初始化带有变量的数组,例如,如下所示:

array *array_new(size_t size){
    array* a = malloc(sizeof(array) + size * sizeof(int));

    if(a){
        a->size = size;
    }

    return a;
}
也就是说,使用
malloc()
,我们还为大小为零的数组分配内存。这对我来说是全新的,而且似乎很奇怪,因为根据我的理解,结构的元素不一定在连续的位置

为什么
array\u new
中的代码将内存分配给
数据[0]
?比如说,为什么访问是合法的

array * a = array_new(3);
a->data[1] = 12;
array * a = array_new(3);
a->data[1] = 12;
?

从他告诉我们的情况来看,在结构末尾定义为长度为零的数组似乎确保紧跟在结构的最后一个元素之后,但这似乎很奇怪,因为根据我的理解,结构可能有填充


我还看到,这只是gcc的一个特性,没有任何标准定义。这是真的吗?

目前,存在一种标准功能,如C11第§6.7.2.1章所述,称为柔性阵列成员

引用该标准

作为特例,具有多个命名成员的结构的最后一个元素可以 具有不完整的数组类型;这称为灵活数组成员。在大多数情况下, 将忽略灵活数组成员。特别是,结构的大小就像 省略了flexible数组成员,只是它的尾部填充可能比 这一遗漏意味着。[……]

语法应该是

struct s { int n; double d[]; };
如果最后一个元素是不完整类型,(没有数组维度,甚至没有0

因此,您的代码最好看起来像

struct array{
    size_t size;
    int data[ ];
};
符合标准


现在来看您的示例,对于0大小的阵列,这是一种实现相同功能的传统方法(“struct hack”)。在C99之前,您需要模拟灵活的阵列成员功能。

您的教授感到困惑。他们应该去读书。这是一个非标准的GCC扩展;它不是有效的C,也不是他们应该教学生使用(*)的东西

相反,请使用标准的C灵活数组成员。与您的零大小阵列不同,它实际上可以便携工作:

struct array{
    size_t size;
    int data[];
};
当您在结构上使用
sizeof
时,灵活数组成员保证计数为零,允许您执行以下操作:

malloc(sizeof(array) + sizeof(int[size]));


(*)早在90年代,人们就利用一种不安全的漏洞在结构后添加数据,称为“结构黑客”。为了提供扩展结构的安全方法,GCC将零大小数组特性作为非标准扩展实现。1999年,当C标准最终提供了一种更好的方法时,它就过时了。

更标准的方法是使用1的数据大小定义数组,如:

struct array{
    size_t size;
    int data[1]; // <--- will work across compilers
};

这可以有效地使用array.data作为额外数据可能去向的标记(取决于大小)。

其他答案解释了零长度数组是GCC扩展,C允许可变长度数组,但没有人回答您的其他问题

根据我的理解,结构的元素不一定在连续的位置

对<代码>结构数据类型的元素不一定位于连续位置

为什么
array\u new
中的代码将内存分配给
数据[0]
?比如说,为什么访问是合法的

array * a = array_new(3);
a->data[1] = 12;
array * a = array_new(3);
a->data[1] = 12;
?

您应该注意,零长度数组的限制之一是它必须是结构的最后一个成员。这样,编译器知道结构可以有可变长度的对象,并且在运行时需要更多的内存。
但是,你不应该被混淆;“由于零长度数组是该结构的最后一个成员,那么为零长度数组分配的内存必须添加到该结构的末尾,而且由于结构的元素不一定位于连续位置,那么如何访问分配的内存?”

不,不是这样的。结构成员的内存分配不一定是连续的,它们之间可能有填充,但分配的内存必须使用变量
data
访问。是的,填充在这里没有效果。规则是: §6.7.2.1/15

在结构对象中,非位字段成员和位字段所在的单位 驻存地址按声明顺序增加


我还看到,这只是gcc的一个特性,没有任何标准定义。这是真的吗

对。正如其他答案已经提到的,标准C不支持零长度数组,而是GCC编译器的扩展。C99引入了灵活的阵列成员。C标准(6.7.2.1)中的一个示例:

声明之后:

struct s { int n; double d[]; };
结构struct
s
具有灵活的数组成员
d
。一种典型的使用方法是:

int m = /* some value */;
struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));
并且假设对
malloc
的调用成功,那么
p
指向的对象在大多数情况下的行为就好像
p
被声明为:

struct { int n; double d[m]; } *p;
(在某些情况下,这种等效性被破坏;特别是,成员
d
的偏移量可能不相同)


我过去的做法是在结构的末尾没有一个虚拟成员:结构本身的大小告诉您刚刚经过它的地址。将1添加到类型化指针会出现以下情况:

header * p = malloc (sizeof (header) + buffersize);
char * buffer = (char*)(p+1);

对于一般的结构,您可以知道字段是按顺序排列的。能够匹配文件格式二进制图像、操作系统调用或硬件所需的某些强制结构是使用C的一个优势。您必须知道对齐填充是如何工作的,但是它们是有序的,并且在一个连续的块中。

“结构的元素不一定在连续的位置”-结构中的数组有。@MartinJames如何计算到的大小