C:下标一个不完整类型的数组是合法的吗?
我找不到标准中的相关位,但是gcc和clang允许它,所以我想我想知道它是编译器扩展还是语言的一部分。 如果可以,请提供链接。
这可以通过以下方式产生:
extern char arr[]; func(arr[7]); /*No error.*/
晚编辑:我想我最好能清楚地理解这一点,虽然我已经离开了,但我从未做过这样的事情,所以我将奖励给第一个给我一个明确,简明的参考资料的人。关于为什么允许这样做的C89标准。 如果没有人能在C89中找到答案,C99是可以接受的,但您需要先查看C89标准。
以下声明
extern char arr[];
是一个带有外部链接的声明,并说arr
有一个char
数组,这意味着arr
可以有一个不完整的类型。
根据“6.7声明”(n1570):
7如果声明对象的标识符没有链接,则对象的类型应在其声明者的末尾完成,或者如果它具有初始化器,则在其init-declarator的末尾完成; 在函数参数(包括原型)的情况下,需要完成的是调整后的类型(见6.7.6.3)。
并且arr[7]
等于*(arr + 7)
,并且arr
需要有一种“指向完整对象类型的指针”,并且arr
的类型将从“ char
of char
”转换为“指向char
指针”。这个案例。
根据“6.3.2.1 Lvalues,数组和函数指示符”(n1570):
3除非它是
sizeof
运算符,_Alignof
运算符或一元&
运算符的操作数,或者是用于初始化数组的字符串文字,否则将类型为”数组’的数组的表达式转换为表达式类型为”指向类型’的指针,指向数组对象的初始元素,而不是左值。
“post fi x表达式后跟方括号[]中的表达式是数组对象元素的下标。下标operator []的定义是E1 [E2]与(*((E1)+()相同E2)))”
来自ISO / IEC 9899:201x委员会草案 – 2011年4月12日
所以arr [7]完全合法,7 [arr]也是如此。 它是一个合法的表达并不意味着它指的是您的进程有权访问的内存位置,或者您想要的内存位置。
引自WG14/N1124 Committee Draft May 6, 2005 ISO/IEC 9899:TC2
6.2.5类型
[22]未知大小的数组类型是不完整类型。 对于该类型的标识符,通过在稍后的声明中指定大小(具有内部或外部链接)来完成。
extern char arr[];
应该是一个不完整的类型。
6.5.2.1数组下标
[2]后缀表达式后跟方括号[]中的表达式是数组对象元素的下标名称。 下标运算符[]的定义是E1 [E2]与(*((E1)+(E2)))相同。 由于适用于binary +运算符的转换规则,如果E1是数组对象(等效地,指向数组对象的初始元素的指针)并且E2是整数,则E1 [E2]指定E2的第E2个元素。 E1(从零开始计数)。
FUNC(ARR [7]); / *或func(7 [arr]); * /
是完全相同的
func(*(arr + 7)); / *在其他模块中分配arr的内存; * /
6.7.5.2数组声明符
[4]如果大小不存在,则数组类型是不完整类型。
[8]例2注意声明之间的区别
extern int * x;
extern int y [];
第一个声明x是一个指向int的指针; 第二个声明y是一个未指定大小的int数组(一个不完整的类型),其存储在其他地方定义。
x是完整的,因为sizeof x是已知的。 y是不完整的,因为编译此单位时y的大小未知。
extern char arr [];
与… 不完全相同
extern char * arr;
脚注[92]
如果先前的无效指针操作(例如数组边界外的访问)产生了未定义的行为,则后续比较也会产生未定义的行为。
附件J.2未定义的行为
– 数组下标超出范围,即使一个对象显然可以使用给定的下标访问(如左边的表达式a 1 [7]给定声明int [4] [5])(6.5.6)。
– 将指针加到或减去数组对象和整数类型会产生一个指向数组对象之外的结果,并用作被计算的一元*运算符的操作数(6.5.6)。
编译器应生成访问arr
第 7 个元素的代码(基于0),如果arr
定义不包含7+1
即8个元素或更多元素,则行为应该是未定义的 。 但是只要链接的arr
具有足够的大小,代码就是可编译的并且将表现出良好定义的行为。
对于不完整类型,您必须进行自己的内存管理和边界检查,这意味着您知道arr[7]
是否是有效位置。
由于此数组中的访问和索引位置是使用不完整类型的唯一方法。
例如,你不能使用完整类型的值(如arr = arr2[5]
初始化一个不完整类型的arr[]
,即使你知道为arr2
分配了足够的内存以适应arr
你只能记忆或迭代每个插槽。