struct中间的可变长度数组 – 为什么这个C代码对gcc有效

使用VLA(可变长度数组)有一些奇怪的代码,它被gcc 4.6视为有效C(C99,C11):

$ cat ac int main(int argc,char**argv) { struct args_t{ int a; int params[argc]; // << Wat? // VLA in the middle of some struct, between other fields int b; } args; args.b=0; for(args.a=0;args.a<argc;args.a++) { args.params[args.a]=argv[0][0]; args.b++; } return args.b; } 

编译时此代码没有警告:

 $ gcc-4.6 -Wall -std=c99 ac && echo $? 0 $ ./a.out ; echo $? 1 $ ./a.out 2; echo $? 2 $ ./a.out 2 3; echo $? 3 

对于-std=c1x

 $ gcc-4.6 -Wall -std=c1x ac && echo $? 0 

但这不适用于英特尔C编译器或Clang + LLVM:

 $ icc ac -o a.icc ac(5): warning #1361: variable-length array field type will be treated as zero-length array field type int params[argc]; ^ $ ./a.icc; echo $? 47 $ clang ac -o a.clang ac:5:10: error: fields must have a constant size: 'variable length array in structure' extension will never be supported int params[argc]; ^ 1 error generated. 

所以:

  1. 为什么GCC认为这有效?
  2. 如果它是GCC的扩展,它在哪里描述?
  3. 它在C99和C11 ISO标准中有效吗?

GCC不允许它,使用-std=c99 -pedantic-errors编译。 结构中的VLA显然是一个(文档记录很少)的非标准GNU Cfunction。 看到这个 。

标准很清楚,结构中不允许使用VLA:

6.7.2.1结构和联合说明符

9 – 结构或联合的成员可以具有除可变修改类型之外的任何完整对象类型。 […]

可变修改类型(如您所料)从可变长度数组派生的类型(例如,通过添加数组维度或cv限定符):

6.7.6声明者

3 – […]如果在完整声明符中的嵌套的声明符序列中,有一个声明符指定了一个可变长度的数组类型,那么完整声明符指定的类型被称为可变地修改。 此外,通过可变修改类型的声明符类型派生导出的任何类型本身都是可变地修改的。

C89标准的作者认识到许多实现实现了有用的function,这些function在其他实现上可能是不切实际的,并且认为这是一件好事 。 该标准旨在作为实施的最低要求; 它从来没有打算阻止实现提供超出该function的function。

标准要求如果符合实现允许在块范围内定义的结构内声明可变长度数组,则它必须将此类行为记录为扩展,或者在代码包含此类声明时发出诊断。 由于实现可以自由地处理代码,但是在发出这样的诊断之后它是喜欢的, 无论它是否记录扩展 ,对文档扩展的要求只能有意义地应用于不产生诊断的扩展。 反过来,这表明这些事情必须是允许的。

标准确实要求扩展不会对任何严格遵守程序的行为产生不利影响,但由于此类程序中没有任何此类程序可能包含VLA声明,因此此要求不是问题。