C23之前如何在块作用域用复合字面量指针初始化静态常量结构体?
C23前块作用域中静态const结构体的指针初始化问题
以下代码会被GCC和Clang拒绝:
struct thing; typedef enum { THING_TYPE_A, THING_TYPE_B, } thing_type_t; typedef struct thing_a { int i; } thing_a_t; typedef struct thing_b { struct thing const *t; } thing_b_t; typedef struct thing { thing_type_t type; union { thing_a_t a; thing_b_t b; } t; } thing_t; thing_t const *get_thing(void) { static const thing_t s_thing = { .type = THING_TYPE_B, .t = { .b = { .t = &(thing_t) { .type = THING_TYPE_A, .t = { .a = { .i = 234 } } } } }, }; return &s_thing; }
根据C标准中复合字面量的规则:
复合字面量所指代的无名对象,若在文件作用域则具有静态存储期,若在块作用域则具有自动存储期(此时对象生命周期随块结束而终止)。
这就是编译报错的核心原因:用来初始化s_thing.t.b.t的匿名thing_t是块作用域内的复合字面量,属于自动存储期,其地址并非编译时常量,无法用于静态变量的初始化。如果把s_thing移到文件作用域,编译器就会接受——因为此时复合字面量拥有静态存储期。
C23标准新增了在复合字面量括号内指定constexpr的特性,能直接解决这个问题,但在C23之前,我们能不能在块作用域中实现类似s_thing的声明:初始化一个包含指向另一常量变量指针的静态const结构体,同时无需显式将匿名thing_t声明为单独变量?
答案是不行,必须显式声明一个静态常量对象替代复合字面量,但可以通过将这个静态对象放在块作用域内,避免污染全局命名空间,代码依然保持紧凑:
thing_t const *get_thing(void) { // 块作用域内的静态常量对象,仅当前函数可见 static const thing_t nested_thing = { .type = THING_TYPE_A, .t = { .a = { .i = 234 } } }; static const thing_t s_thing = { .type = THING_TYPE_B, .t = { .b = { .t = &nested_thing } }, }; return &s_thing; }
这种方式既满足了块作用域的限制,又保证所有对象都是静态存储期,其地址可以合法作为静态变量的初始化器。
内容的提问来源于stack exchange,提问作者Charles Nicholson
相关产品推荐
相关产品推荐

