C99/C23标准中复合字面量初始化结构体时引用已设字段的行为界定
复合字面量初始化结构体时引用同对象已设字段的行为规范
在C语言中,使用复合字面量为结构体设置初始值时,能否引用同语句中已经设置的字段?比如写法struct foo v = {.v1 = ..., .v2 = .v1+1};是否合法?
示例代码
struct s { int i1; int i2; }; void func(void) { // Case A: 赋值语句中使用复合字面量 struct s v1 = {3}; v1 = (struct s){.i1=4, .i2=v1.i1+10}; // 运行结果:v1.i2=13,而非预期的14 // Case B: 初始化语句中使用复合字面量引用自身字段 // 此写法是否合法? struct s v2 = (struct s){.i1=1, .i2=v2.i1+10}; // GCC中运行结果:v2.i2=11 }
Case A 行为分析
对于Case A,C标准有明确规定:赋值操作右侧的复合字面量会创建一个临时结构体对象,其中v1.i1的取值是赋值操作前的旧值(也就是3),因此计算得到v1.i2=3+10=13。本质等价于:
struct s v1 = {3}; struct s temp = (struct s){.i1=4, .i2=v1.i1+10}; v1 = temp;
Case B 的标准规定(C99/C23)
核心结论
Case B中,初始化时通过复合字面量引用正在创建的对象v2的字段,属于未定义行为,既不是标准强制要求的必需行为,也不属于可移植的实现特定行为。
标准依据
根据C99和C23标准的定义:在对象的初始化表达式中,引用正在被初始化的对象本身没有合法语义——此时对象尚未完成初始化,内存状态未确定,对其成员的访问属于无效操作。
GCC的特殊实现
GCC允许这种写法是编译器的非标准扩展行为:它会按照字段的初始化顺序(先i1后i2),将已初始化的i1值用于后续字段的计算,因此得到v2.i2=1+10=11。但这种行为不具备可移植性,换用其他编译器(如Clang、MSVC)可能得到不同结果,甚至触发未定义行为导致的异常。
工具检测说明
使用GCC9开启-Wall、-Wextra及Coverity检测未告警,是因为默认规则未覆盖此类非标准扩展场景。若需捕捉这类问题,可启用-pedantic等严格标准合规选项,部分编译器会对此类写法发出警告。
内容的提问来源于stack exchange,提问作者dash-o
相关产品推荐
相关产品推荐

