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

时间:2013-01-31 15:41:43

标签: c gcc c99 c11 variable-length-array

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

$ cat a.c
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 a.c && 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 a.c && echo $?
0

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

$ icc a.c -o a.icc
a.c(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 a.c -o a.clang
a.c: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标准中有效吗?

3 个答案:

答案 0 :(得分:9)

GCC不允许,使用-std=c99 -pedantic-errors进行编译。结构中的VLA显然是一个(文档记录很少)的非标准GNU C特性。 See this

答案 1 :(得分:4)

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

  

6.7.2.1结构和联合说明符

     

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

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

  

6.7.6声明者

     

3 - [...]如果,在完整的嵌套序列中的声明符   声明器,有一个声明器指定一个可变长度数组类型,完整声明符指定的类型据说是可变的修改。此外,通过可变修改类型的声明符类型派生派生的任何类型本身都会被可变地修改。

答案 2 :(得分:-1)

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

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

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