我有这个极其微不足道的C代码:
static int arr[];
int main(void) {
*arr = 4;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我理解第一个语句是非法的(我已经声明了一个具有静态存储持续时间和文件范围但没有指定大小的文件范围数组),但是为什么它会导致链接器错误?:
/usr/bin/ld: /tmp/cch9lPwA.o: in function `main':
unit.c:(.text+0xd): undefined reference to `arr'
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)
编译器是否应该能够在链接器之前捕获它?
我也很奇怪,如果我省略了static存储类,编译器只是假设数组是长度的,1并且不会产生任何错误:
int arr[];
int main(void) {
*arr = 4;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
结果是:
unit.c:5:5: warning: array 'arr' assumed to have one element
int arr[];
Run Code Online (Sandbox Code Playgroud)
为什么省略存储类会导致不同的行为,为什么第一段代码会产生链接器错误?谢谢.
Lun*_*din 12
空数组static int arr[];和零长度数组static int arr[0];是gcc非标准扩展.
这些扩展的目的是作为旧"结构黑客"的修复.回到C90时代,人们编写了如下代码:
typedef struct
{
header stuff;
...
int data[1]; // the "struct hack"
} protocol;
Run Code Online (Sandbox Code Playgroud)
在那里data,好像有可变大小取决于什么是在标题部分的阵列将无法再被使用.这样的代码是错误的,将数据写入填充字节并且通常调用数组越界未定义的行为.
gcc通过添加空/零数组作为编译器扩展来解决这个问题,使代码行为没有错误,尽管它不再是可移植的.
C标准委员会认识到这个gcc特性是有用的,因此他们在1999年将灵活的阵列成员添加到C语言中.从那时起,gcc特征被认为是过时的,因为使用C标准的灵活阵列成员更喜欢.
正如链接的gcc文档所认可的那样:
不鼓励在其他上下文中声明零长度数组,包括作为结构对象的内部成员或非成员对象.
这就是你的代码所做的.
请注意,没有编译器选项的-std=gnu90gcc 默认传递给(gcc <5.0)或-std=gnu11(gcc> 5.0).这为您提供了所有非标准扩展,因此程序编译但不链接.
如果您需要标准兼容行为,则必须编译为
gcc -std=c11 -pedantic-errors
Run Code Online (Sandbox Code Playgroud)
该-pedantic标志禁用gcc扩展,并且链接器错误按预期切换到编译器错误.对于你的情况下的空数组,你得到:
错误:'arr'中缺少数组大小
对于零长度数组,您将得到:
错误:ISO C禁止零大小数组'arr'[-Wpedantic]
之所以int arr[]奏效,是因为这是一个具有外部联系的暂定定义的数组声明(见C17 6.9.2).它是有效的C并且可以被视为前瞻性声明.这意味着在代码的其他地方,编译器(或者更确切地说是链接器)应该期望找到例如int arr[10],然后引用相同的变量.这样,arr可以在大小已知之前在代码中使用.(我不建议使用这种语言功能,因为它是"意大利面条编程"的一种形式.)
当您使用时static,通过强制变量具有内部链接来阻止在其他地方指定数组大小的可能性.