在 C99 中,我们可以将复合文字用作未命名数组。
但是这个字面量是常量吗,例如100
, 'c'
,123.4f
等等。
我注意到我可以这样做:
((int []) {1,2,3})[0] = 100;
而且,我没有编译错误,并且可以猜测该未命名数组的第一个元素被修改为 100。
因此,作为复合文字的数组似乎是左值而不是常量值。
在 C99 中,我们可以将复合文字用作未命名数组。
但是这个字面量是常量吗,例如100
, 'c'
,123.4f
等等。
我注意到我可以这样做:
((int []) {1,2,3})[0] = 100;
而且,我没有编译错误,并且可以猜测该未命名数组的第一个元素被修改为 100。
因此,作为复合文字的数组似乎是左值而不是常量值。
它是一个左值,如果我们查看草案 C99 标准部分6.5.2.5
Compound literals它说(强调我的),我们可以看到这一点:
如果类型名称指定一个未知大小的数组,则大小由 6.7.8 中指定的初始化列表确定,复合文字的类型是完整数组类型的类型。否则(当类型名称指定对象类型时),复合文字的类型是类型名称指定的类型。无论哪种情况,结果都是左值。
如果您想要一个const版本,稍后在同一部分中会给出以下示例:
示例 4 可以通过以下结构指定只读复合文字:
(const float []){1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6}
我们可以在 Dobb 博士的文章The New C: Compound Literals中找到对术语的解释,并说:
复合文字不是真正的常量,因为文字的值可能会改变,如下所示。这给我们带来了一些术语。C99 和 C90 标准 [2, 3] 使用“常量”一词来表示真正不可更改的值,这些值在语言中是不可能修改的。因此,10 和 3.14 分别是整数十进制常量和 double 类型的浮点常量。“literal”一词用于表示可能不是那么恒定的值。例如,早期的 C 实现允许修改引用字符串的值。C90 和 C99 通过说任何修改字符串文字的程序具有未定义的行为来禁止这种做法,这是标准的说法,它可能会起作用,或者程序可能会以一种神秘的方式失败。[...]
据我记得你是对的,复合文字是左值*,你也可以获取这种文字的指针(指向它的第一个元素):
int *p = (int []){1, 2, 3};
*p = 5; /* modified first element */
也可以const
在这种复合文字上应用限定符,因此元素是只读的:
const int *p = (const int []){1, 2, 3};
*p = 5; /* wrong, violation of `const` qualifier */
*注意这并不意味着它是可自动修改的左值(因此它可以用作赋值运算符的左操作数),因为它具有数组类型并引用 C99 草案6.3.2.1
左值、数组和函数指示符:
可修改的左值是没有数组类型的左值,[...]
复合文字是左值,它的元素可以修改。你可以给它赋值。甚至允许指向复合文字的指针。
参考C11标准草案N1570:
第 6.5.2.5p4 节:
无论哪种情况,结果都是左值。
“左值”大致是一个指定对象的表达式——但重要的是要注意并非所有左值都是可修改的。一个简单的例子:
const int x = 42;
该名称x
是一个左值,但它不是一个可修改的左值。(数组类型的表达式不能是可修改的左值,因为您不能分配给数组对象,但数组的元素可能是可修改的。)
同一节的第 5 段:
复合文字的值是由初始化列表初始化的未命名对象的值。如果复合文字出现在函数体之外,则该对象具有静态存储持续时间;否则,它具有与封闭块关联的自动存储持续时间。
描述复合文字的部分没有具体说明未命名对象是否可修改。在没有这样的声明的情况下,对象被认为是可修改的,除非类型是const
- 限定的。
问题中的示例:
((int []) {1,2,3})[0] = 100;
并不是特别有用,因为在赋值之后无法引用未命名的对象。但是类似的构造可能非常有用。一个人为的例子:
#include <stdio.h>
int main(void) {
int *ptr = (int[]){1, 2, 3};
ptr[0] = 100;
printf("%d %d %d\n", ptr[0], ptr[1], ptr[2]);
}
如上所述,数组具有自动存储期限,这意味着如果它是在函数内部创建的,则在函数返回时它将不复存在。复合文字不能替代malloc
.