Apple Clang(g++兼容模式)O3优化下值初始化对象成员被误判未初始化的优化问题
Apple Clang(g++兼容模式)O3优化下值初始化对象成员被误判未初始化的优化问题
看起来你撞上了Apple Clang(用g兼容模式编译时)的一个棘手优化问题:当你用()或者{}对一个带有空构造函数的对象做值初始化时,编译器错误地判定对象成员是未初始化的,进而做出了不正确的优化——比如调用printf打印成员时,直接跳过传递成员值,导致输出垃圾数据。但按照C标准,用()或{}做值初始化时,哪怕构造函数什么都不做,内存也应该先被清零,编译器理应知道成员是有效的。而且这个问题在Ubuntu等Linux发行版上没法复现,明显是苹果这边的编译器独有的问题。
复现代码
#include "stdio.h" #include <string.h> class ThirdPartyWidget { public: ThirdPartyWidget() { /* 构造函数什么都不做,成员变量未被初始化 */ } int data; void p() { // 打印对象值 printf("this %p (%lu): data: %d\n", this, sizeof(*this), data); } void px() { // 十六进制转储对象 printf("px: this %p (%zu): ", this, sizeof(*this)); unsigned char * c = (unsigned char *)this; for (int i=0; i < sizeof(*this); i++) { printf(" %02x", *c++); } printf("\n"); } }; int main() { // 栈上分配,未初始化 ThirdPartyWidget w0; // 无初始化 // w1->data 是未初始化值,编译器可以在调用p()时忽略这些值 auto w1 = new ThirdPartyWidget; // 用圆括号做值初始化:即使构造函数什么都不做,w2的内存会先被清零,编译器应该知道w2->data是有效的 auto w2 = new ThirdPartyWidget(); // 用空大括号做列表初始化:同样会执行值初始化 auto w3 = new ThirdPartyWidget{}; printf("\nw0\n"); // w0未初始化,MacOS编译器正确地不传递任何值给printf w0.p(); w0.px(); printf("\nw1\n"); // w1未初始化,MacOS编译器正确地不传递任何值给printf w1->p(); w1->px(); printf("\nw2\n"); // 这里MacOS编译器错误地认为w2的成员未初始化,忽略它们,不传递给printf() w2->p(); w2->px(); printf("\nw3\n"); // 这里MacOS编译器错误地认为w3的成员未初始化,忽略它们,不传递给printf() w3->p(); w3->px(); // 调用过px()后,MacOS编译器知道成员变量现在是有效的,不再忽略它们 w3->p(); return 0; }
编译命令
g++ -O3 -std=c++23 buggy_cpp_initialization.cpp -o buggy_cpp_initialization.bin
运行输出
buggy_cpp_initialization with O3 opt w0 this 0x16f5131dc (4): data: 154385960 <-- 符合预期,data是垃圾值 px: this 0x16f5131dc (4): 02 00 00 00 w1 this 0x6000033b4030 (4): data: 154385960 <-- 符合预期,data是垃圾值 px: this 0x6000033b4030 (4): 00 00 00 00 w2 this 0x6000033b4040 (4): data: 154385960 <-- 不符合预期,编译器错误,data应该是0 px: this 0x6000033b4040 (4): 00 00 00 00 w3 this 0x6000033b4050 (4): data: 154385960 <-- 不符合预期,编译器错误,data应该是0 px: this 0x6000033b4050 (4): 00 00 00 00 this 0x6000033b4050 (4): data: 0 <-- 符合预期,此时编译器终于知道data是0了
汇编对比(错误vs正确)
错误的汇编(w2/w3第一次调用p()时)
编译器只传递了前两个参数(this和sizeof值),完全跳过了data参数,导致printf输出垃圾值:
100000650: a90067f3 stp x19, x25, [sp] 100000654: aa1403e0 mov x0, x20 100000658: 9400002a bl 0x100000700 <_puts+0x100000700>
正确的汇编(w3最后一次调用p()时)
此时编译器正确传递了三个参数(this、sizeof值、data),和printf的格式字符串匹配:
1000006c0: b9400268 ldr w8, [x19] 1000006c4: a900a3f9 stp x25, x8, [sp, #0x8] 1000006c8: f90003f3 str x19, [sp] 1000006cc: aa1403e0 mov x0, x20 1000006d0: 9400000c bl 0x100000700 <_puts+0x100000700>
编译器版本信息
g++ --version Apple clang version 17.0.0 (clang-1700.0.13.5) Target: arm64-apple-darwin24.5.0 Thread model: posix InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
内容来源于stack exchange
相关产品推荐
相关产品推荐

