Namespaces
Variants

Compound literals (since C99)

From cppreference.net

构造一个指定类型(可以是结构体、联合体甚至数组类型)的未命名对象,并就地初始化。

目录

语法

( 存储类说明符  (可选) (自 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前)
若复合字面量在函数体外部且在任何形参列表外部求值,则其关联文件作用域;否则关联当前包围块。根据此关联关系,存储类说明符(可能为空)、类型名及初始化式列表(若存在)必须满足以下形式在文件作用域或块作用域中分别作为对象定义的有效说明符:
storage-class-specifiers type typeof( type ) ID = { initializer-list } ;
其中 ID 是整个程序中唯一的标识符。复合字面量提供一个无名对象,其值、类型、存储期及其他属性如同通过上述定义语法给定;若存储期为自动,则该无名对象实例的生存期是当前包围块的执行周期。若存储类说明符包含除 constexpr static register thread_local 之外的其他说明符,则行为未定义。
(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)