复合字面量

来自cppreference.com
< c‎ | language

就地构造一个指定类型的无名对象,在只需要一次数组、结构体或联合体变量时使用。

语法

( 类型 ) { 初始化器列表 } (C99 起)
( 类型 ) { 初始化器列表 , } (C99 起)
( 类型 ) { } (C23 起)

其中

类型 - 指定任何完整对象类型或未知大小的数组的类型名,但不能是 VLA
初始化器列表 - 适合 类型 类型变量初始化的初始化器列表

解释

复合字面量构造一个 类型 所指定类型的无名对象,并以 初始化器列表 的指定初始化之。接受指派初始化器

复合字面量的类型是 类型 (除非 类型 是未知大小的数组;如在数组初始化中一般从 初始化器列表 推导出其大小)。

复合字面量的值类别是左值(能取其地址)。

若复合字面量出现于文件作用域,则其求值所得的无名对象拥有静态存储期,若复合字面量出现于块作用域,则该对象拥有自动存储期(此情况下对象的生存期结束于外围块的结尾)。

注解

字符或宽字符数组类型的 const 限定复合字面量,可能会与字符串字面量共享存储。

(const char []){"abc"} == "abc" // 可以为 1 或 0 ,未指定

每个复合字面量仅在其作用域内创建一个对象:

int f (void)
{
    struct s {int i;} *p = 0, *q;
    int j = 0;
again:
    q = p, p = &((struct s){ j++ });
    if (j < 2) goto again; // 注意:若使用循环,则其作用域会结束于此,
                           // 这会终止复合字面量的生存期,
                           // 令 p 为悬垂指针
    return p == q && q->i == 1; // 始终返回 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}

引用

  • C17 标准(ISO/IEC 9899:2018):
  • 6.5.2.5 Compound literals (第 61-63 页)
  • C11 标准(ISO/IEC 9899:2011):
  • 6.5.2.5 Compound literals (第 85-87 页)
  • C99 标准(ISO/IEC 9899:1999):
  • 6.5.2.5 Compound literals (第 75-77 页)