Compound literals (since C99)
构造一个指定类型(可以是结构体、联合体甚至数组类型)的未命名对象,并就地初始化。
目录 |
语法
(
存储类说明符
(可选)
(自 C23 起)
类型
)
{
初始化列表
}
|
(自 C99 起) | ||||||||
(
存储类说明符
(可选)
(自 C23 起)
类型
)
{
初始化列表
,
}
|
(自 C99 起) | ||||||||
(
存储类说明符
(可选)
类型
)
{
}
|
(自 C23 起) | ||||||||
其中
| storage-class-specifiers | - | (since C23) 一个 存储类说明符 列表,仅可包含 constexpr , static , register , 或 thread_local |
| type | - | 指定任何完整对象类型或未知大小数组的 类型名称 ,但不能是可变长度数组(VLA) |
| initializer-list | - | 适用于对 type 类型对象进行 初始化 的初始化器列表 |
说明
复合字面量表达式构造一个由 type 指定类型的无名对象,并按照 initializer-list 指定的方式进行初始化。支持 指派初始化器 。
复合字面量的类型是 type (除非 type 是未知大小的数组;其大小会像 数组初始化 中那样从 initializer-list 推导得出)。
复合字面量的值类别是 左值 (可以获取其地址)。
| 复合字面量求值所得的无名对象,若该复合字面量出现在文件作用域则具有静态 存储期 ,若出现在块作用域则具有自动 存储期 (此种情况下对象的 生存期 在包围块结尾结束)。 | (C23前) | |||||||||||||||||||||||
若复合字面量在函数体外部且在任何形参列表外部求值,则其关联文件作用域;否则关联当前包围块。根据此关联关系,存储类说明符(可能为空)、类型名及初始化式列表(若存在)必须满足以下形式在文件作用域或块作用域中分别作为对象定义的有效说明符:
|
(C23起) | |||||||||||||||||||||||
注释
常量限定的字符或宽字符数组类型的复合字面量可能与 字符串字面量 共享存储空间。
(const char []){"abc"} == "abc" // 结果可能为1或0,未指定
每个复合字面量在其作用域内仅创建一个对象:
#include <assert.h> int main(void) { struct S { int i; } *p = 0, *q; int j = 0; again: q = p, p = &((struct S){ j++ }); // 创建一个未命名的 S 类型对象, // 将其初始化为先前 j 持有的值, // 然后将该未命名对象的地址赋值给指针 p if (j < 2) goto again; // 注意:如果使用循环,此处将结束作用域, // 这将终止复合字面量的生命周期, // 使 p 成为悬垂指针 assert(p == q && q->i == 1); }
由于复合字面量是未命名的,因此复合字面量无法引用自身(具名结构体可以包含指向自身的指针)。
尽管复合字面量的语法类似于 强制类型转换 ,但重要区别在于:强制类型转换是非左值表达式,而复合字面量是左值。
示例
#include <stdio.h> int *p = (int[]){2, 4}; // 创建一个未命名的静态数组,类型为 int[2] // 将数组初始化为值 {2, 4} // 创建指针 p 指向数组的第一个元素 const float *pc = (const float []){1e0, 1e1, 1e2}; // 只读复合字面量 struct point {double x,y;}; int main(void) { int n = 2, *p = &n; p = (int [2]){*p}; // 创建一个未命名的自动数组,类型为 int[2] // 将第一个元素初始化为先前 *p 持有的值 // 将第二个元素初始化为零 // 将第一个元素的地址存储在 p 中 void drawline1(struct point from, struct point to); void drawline2(struct point *from, struct point *to); drawline1( (struct point){.x=1, .y=1}, // 创建两个具有块作用域的结构体 (struct point){.x=3, .y=4}); // 调用 drawline1,按值传递它们 drawline2( &(struct point){.x=1, .y=1}, // 创建两个具有块作用域的结构体 &(struct point){.x=3, .y=4}); // 调用 drawline2,传递它们的地址 } void drawline1(struct point from, struct point to) { printf("drawline1: `from` @ %p {%.2f, %.2f}, `to` @ %p {%.2f, %.2f}\n", (void*)&from, from.x, from.y, (void*)&to, to.x, to.y); } void drawline2(struct point *from, struct point *to) { printf("drawline2: `from` @ %p {%.2f, %.2f}, `to` @ %p {%.2f, %.2f}\n", (void*)from, from->x, from->y, (void*)to, to->x, to->y); }
可能的输出:
drawline1: `from` @ 0x7ffd24facea0 {1.00, 1.00}, `to` @ 0x7ffd24face90 {3.00, 4.00}
drawline2: `from` @ 0x7ffd24facec0 {1.00, 1.00}, `to` @ 0x7ffd24faced0 {3.00, 4.00}
参考文献
- C23 标准 (ISO/IEC 9899:2024):
-
- 6.5.2.5 复合字面量 (p: 77-80)
- C17 标准 (ISO/IEC 9899:2018):
-
- 6.5.2.5 复合字面量 (p: 61-63)
- C11 标准 (ISO/IEC 9899:2011):
-
- 6.5.2.5 复合字面量 (p: 85-87)
- C99标准(ISO/IEC 9899:1999):
-
- 6.5.2.5 复合字面量(页码:75-77)