如果我在C / C ++中定义一个0大小的数组会发生什么?
只是好奇,如果我定义一个零长度的数组int array[0];
在代码中? 海湾合作委员会根本没有抱怨。
示例程序
#include <stdio.h> int main() { int arr[0]; return 0; }
澄清
实际上,我试图弄清楚是否零长度的数组是以这种方式初始化的,而不是像Darhazer的评论中指出的可变长度那样被优化。
这是因为我必须发布一些代码到野外,所以我想弄清楚是否必须处理SIZE
被定义为0
,这发生在一些具有静态定义的int array[SIZE];
代码中int array[SIZE];
我真的很惊讶,海湾合作委员会不抱怨,这导致了我的问题。 从我收到的答案来看,我相信没有警告很大程度上是由于支持没有用new []语法更新的旧代码。
因为我主要是想知道这个错误,所以我把Lundin的回答标为正确(Nawaz的第一个,但并不是完整的) – 其他人指出了其实际用于尾巴结构,而相关的,正是我所期待的。
一个数组不能有零大小。
ISO 9899:2011 6.7.6.2:
如果表达式是一个常量表达式,则其值应大于零。
上面的文本对于一个普通数组(第1段)都是正确的。 对于VLA(可变长度数组),如果表达式的值小于或等于零(第5段),则行为是不确定的。 这是C标准中的规范性文本。 编译器不允许以不同的方式实现。
gcc -std=c99 -pedantic
给出了非VLA情况的警告。
通常情况下,这是不允许的。
然而在C中使用灵活的数组是目前的做法。
C99 6.7.2.1,§16 :作为一种特殊情况,具有多于一个命名成员的结构的最后一个元素可能具有不完整的数组类型; 这被称为灵活的数组成员。
示范:
struct Array { size_t size; int content[]; };
这个想法是,你会然后分配它:
void foo(size_t x) { Array* array = malloc(sizeof(size_t) + x * sizeof(int)); array->size = x; for (size_t i = 0; i != x; ++i) { array->content[i] = 0; } }
您也可以静态使用它(gcc扩展名):
Array a = { 3, { 1, 2, 3 } };
这也被称为尾部填充结构 (这个术语早于C99标准的出版)或结构破解 (感谢Joe Wreschnig指出)。
然而,这个语法在C99最近才被标准化(并且保证了效果)。 在恒定大小是必要的之前。
-
1
是可移植的方式,虽然这很奇怪 -
0
在指示意图方面较好,但就标准而言并不合法,并被一些编译器(包括gcc)支持作为扩展,
然而,尾部填充练习依赖于存储可用的事实(小心的malloc
),因此不适合一般的堆栈使用。
在标准C和C ++中,不允许使用零大小的数组。
如果您使用的是GCC,请使用-pedantic
选项进行编译。 它会发出警告 ,说:
zero.c:3:6: warning: ISO C forbids zero-size array 'a' [-pedantic]
在C ++的情况下,它给出了类似的警告。
这是完全非法的,而且一直是这样,但是很多编译器忽略了这个错误。 我不知道你为什么要这样做。 我知道的一个用途是从布尔值触发编译时错误:
char someCondition[ condition ];
如果condition
是错误的,那么我得到一个编译时错误。 因为编译器确实允许这个,但是,我已经开始使用:
char someCondition[ 2 * condition - 1 ];
这给出了1或-1的大小,我从来没有找到一个编译器接受大小为-1。
我将补充一点,在这个参数上有一个gcc在线文档的完整页面 。
一些报价:
在GNU C中允许零长度数组
在ISO C90中,你必须给内容长度为1
和
3.0之前的GCC版本允许零长度数组静态初始化,就像它们是灵活的数组一样。 除了那些有用的情况之外,它还允许在会破坏以后的数据的情况下进行初始化
所以你可以
int arr[0] = { 1 };
和繁荣:-)
如果它们被允许,那么在结构中的零大小的数组声明将是有用的,并且如果语义是这样的(1)它们将强制对齐,否则不分配任何空间,并且(2)索引数组将被认为是定义的行为在这种情况下,生成的指针与结构体在同一块内存中。 这样的行为从来没有被任何C标准所允许,但是一些较老的编译器在编译器成为标准之前就允许它允许带有空括号的不完整的数组声明。
像使用大小为1的数组通常实现的struct hack是不可靠的,我不认为编译器有任何要求不要破坏它。 例如,我期望如果一个编译器看到int a[1]
,它就有权将a[i]
视为a[0]
。 如果有人试图通过类似的东西来解决struct hack的对齐问题
typedef struct { uint32_t大小; uint8_t data [4]; //使用四,以避免填充抛出结构的大小 }
编译器可能会变得聪明,并假定数组的大小真的是四:
; 正如书面 foo = myStruct-> data [i]; ; 按照解释(假设小端硬件) foo =((*(uint32_t *)myStruct-> data)>>(i << 3))&0xFF;
这样的优化可能是合理的,特别是如果myStruct->data
可以以与myStruct->size
相同的操作加载到寄存器中。 我在标准中什么也不知道会禁止这样的优化,尽管当然会破坏任何可能期望访问超出第四个元素的代码。
零长度数组的另一个用途是制作可变长度的对象(C99之前)。 零长度数组 不同于 []没有0的灵活数组 。
从gcc文档引用:
在GNU C中允许使用零长度的数组。它们作为结构的最后一个元素非常有用,它是可变长度对象的头部:
struct line { int length; char contents[0]; }; struct line *thisline = (struct line *) malloc (sizeof (struct line) + this_length); thisline->length = this_length;
在ISO C99中,您将使用灵活的数组成员,这在语法和语义上略有不同:
- 灵活的数组成员被写为内容[]而不是0。
- 灵活的数组成员有不完整的类型,所以sizeof运算符可能不适用。
一个真实世界的例子是kdbus.h (一个Linux内核模块)中的struct kdbus_item
零长度数组。