"int *a = (int[2]){0, 2};"的确切含义是什么?

10
当我看到这个符号时,感到非常惊讶。它是做什么的,属于什么类型的C符号呢?

当在 C 模式下编译时,使用 VC++ 编译器无法通过编译。你尝试过什么编译器? - Khaled Alshaya
它在GCC上运行,所以可能不符合C标准? - Dpp
我猜你是对的——这又是GCC的另一个扩展。不过我不确定你能从中获得什么好处。 - Michael Dorgan
1
@AraK:我认为VC++中的C模式有点不足。 - el.pescado - нет войне
4
编译C代码时,VC++仅支持C89标准而不支持C99标准,而这个特性是在C99中定义的。 - Tyler McHenry
5个回答

19

这是C99标准第6.5.2.5节中定义的复合文字。

由于它不是C++语言的一部分,因此C++编译器无法编译它(事实上,Java或Ada编译器也无法编译)。

复合文字的值是一个由初始化列表初始化的未命名对象的值。如果复合文字出现在函数体外,则对象具有静态存储期;否则,它具有与封闭块关联的自动存储期。

因此,它不会破坏堆栈。编译器为对象分配存储空间。

括号将类型括起来,然后跟随初始化列表——这不是一个转换,因为在C99语法中,裸初始化列表没有意义;相反,它是应用于类型的后缀运算符,产生给定类型的对象。你没有创建{ 0, 3 }并将其强制转换为数组,而是使用值0和3初始化了一个int[2]


至于为什么要使用它,在你的单行代码中我看不出一个好的理由,尽管可能是因为a可能被重新分配到某个其他的数组,所以这是做前两行的更短的方法:

int default_a[] = { 0, 2 };
int *a = default_a;

if (some_test) a = get_another_array();

我发现它对于将临时联合传递给函数非常有用

// fills an array of unions with a value
kin_array_fill ( array, ( kin_variant_t ) { .ref = value } )

很棒的解释! 使用 union 的好例子,这是一种相当简短的初始化并传递指向函数的指针的方式。它似乎看起来像匿名数组。遗憾的是它没有在 C++ 中实现。 - Dpp
我刚刚查了一下C++中的匿名数组,似乎在C++0x中会实现类似的功能:std::initializer_list<>。例如,在函数原型中可以指定std::initializer_list<int>作为参数,并在函数中给出初始化列表,如{0, 2}。 - Dpp

6

这是c99的一个构造,称为复合字面量

来自2005年5月委员会草案第6.5.2.5节:

由括号括起来的类型名称后跟大括号括起来的初始化器列表组成的后缀表达式是复合字面量。它提供了一个未命名对象,其值由初始化器列表给出。

...

示例1 文件范围定义

int *p = (int []){2, 4}; 

初始化p指向一个由两个整数组成的数组的第一个元素,第一个元素的值为2,第二个元素的值为4。这个复合字面量中的表达式必须是常量。这个未命名的对象具有静态存储期。

1
  1. 在堆栈上为两个 int 数组分配空间。
  2. 将两个 int 数组的值分别设置为 02
  3. 声明一个类型为 int* 的本地变量,并将该变量赋值为两个 int 数组的地址。

0

(int[2]) 告诉编译器以下表达式应该被转换为 int[2]。这是必需的,因为 {0, 2} 可以被转换为不同的类型,比如 long[2]。强制转换发生在编译时 - 不是运行时。

整个表达式在内存中创建一个数组,并将 a 指向此数组。


-2
  • {0, 2} 是一个由0和2组成的数组的表示法。
  • (int[2]) 将其转换为一个数组(不知道为什么)。
  • int * a = 将其赋值给整型指针a。

没有进行类型转换,编译器会将其视为试图将多个值赋给标量变量的尝试。 - eemz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接