当前位置: C语言 -- 专题 -- 复合字面量

复合字面量

根据ISO/IEC 9899:2018标准第6.5.2.5 Compound literals节,复合字面量是一个后缀表达式,该表达式由一个( )括起来的类型名后跟一个{ }括起来的初始化列表组成,其语法格式如下所示:

(type-name){initializer-list}

复合字面量提供了一个未命名对象,其类型由类型名(type-name)指定,其值由初始化列表(initializer-list)指定,例如:

int *ptr = (int []){1, 2, 3};

指针ptr指向一个int类型数组,该数组有3个元素,其初始值分别为123


复合字面量的值是可以改变的。

char *ptrOne = "Hello";
char *ptrTwo = (char []){"Hello"};

ptrOne[0] = 'h';    //未定义行为。
ptrTwo[0] = 'h';    //合法。

指针ptrOne指向字符串字面量,字符串字面量是不可修改的;如果试图修改字符串字面量,将导致未定义行为。指针ptrTwo指向复合字面量,复合字面量可以被修改,所以修改其值的行为是合法的。


如果不希望复合字面量被修改,可以使用const类型限定符进行限定,例如:

const char *ptr = (const char []){"Hello"}; 
ptr[0] = 'h';   //非法。

使用const类型限定符限定的复合字面量能够像字符串字面量一样存放在只读存储区,甚至可以和字符串字面量共享存储位置。

(const char []){"Hello"} == "Hello"

如果复合字面量和字符串字面量存储的位置相同,上述表达式的值为1;如果复合字面量和字符串字面量存储的位置不同,上述表达式的值为0


初始化过程中所有用于初始化列表的语法规则都可用于复合字面量,所以也可以对复合字面量进行指定初始化,例如:

int *ptr = (int []){[5]=7};

如果复合字面量出现在函数体外,其具有静态存储期限,初始化列表中的表达式应为常量;如果复合字面量出现在函数体内,其具有动态存储期限,初始化列表中的表达式可以为变量。

extern int a;
extern int b;
int *ptr = (int []){a, b};  //非法。

int main(void)
{
    /*其它代码。*/
    
    return 0;
}

这个例子非法的原因是因为复合字面量出现在函数体外,初始化列表项ab都是变量,而不是常量;但下面的例子却是合法的。

int main(void)
{
    int a = 3;
    int b = 5;
    int *ptr = (int []){a, b};  //合法。
    
    /*其它代码。*/

    return 0;
}

在指定的范围内每个复合字面量都会生成一个未命名对象。

int *ptrOne = (int []){3, 5, 7};
int *ptrTwo = (int []){3, 5, 7};

printf("ptrOne: %p\n", ptrOne);
printf("ptrTwo: %p\n", ptrTwo);

将输出:

ptrOne: 0061fed8

ptrTwo: 0061fee4

这里两个复合字面量尽管看上去一样,但事实上它们却是不同的对象,两个对象的地址是不一样的,例如:表达式(int []){3, 5, 7} == (int []){3, 5, 7}的值为0,因为这里比较的是两个不同对象的地址。


此外ISO/IEC 9899:2018标准还对复合字面量的使用作了一些限制:

 复合字面量的类型应为完整对象类型或者未知大小的数组类型,但不能是变长数组类型。

 初始化时对初始化列表项的所有限制均适用于复合字面量。