开启-O2优化时编译器错误删除指令,该C++代码是否存在未定义行为?
结论
这不是编译器bug,你的代码存在未定义行为,违反了C++标准的严格别名(Strict Aliasing)规则,开启O2优化后触发了错误优化。
问题原因
C++严格别名规则规定:除了char/unsigned char/std::byte类型外,不同类型的指针不能别名指向同一块内存,编译器可以基于该规则做优化,认为修改某类型指针指向的内容,不会影响另一不兼容类型指针指向的内容。
你代码中((uint32_t*)&data_ptr[i])[1] = remainder;这一行,将uint64_t*类型的指针强转为uint32_t*修改内存,后续又通过uint64_t类型的data_ptr[i]读取值。开启O2优化时,编译器会认为这两个操作访问的是无关内存,直接把你的强转赋值操作优化删除,导致后续计算逻辑完全错误。
两种临时恢复正常的原因:
- 添加printf语句后,编译器无法确定printf会不会修改目标内存,不敢做激进的别名优化,所以赋值操作被保留
- 替换为
((uint32_t*)data_ptr)[2 * i + 1] = remainder;只是碰巧避开了当前版本gcc的优化识别,本质仍然是违反标准的未定义行为,不可靠。
标准兼容的修复方案
- 位运算直接赋值(最推荐)
你要修改uint64_t值的高32位,直接用位运算操作即可,完全符合标准:
// 替换原来的强转赋值行 data_ptr[i] = (data_ptr[i] & 0xFFFFFFFFULL) | (static_cast<uint64_t>(remainder) << 32);
- memcpy复制修改
利用char类型指针可以别名任意内存的规则,通过memcpy操作修改:
uint32_t parts[2]; memcpy(parts, &data_ptr[i], sizeof(parts)); parts[1] = remainder; memcpy(&data_ptr[i], parts, sizeof(parts));
- 关闭严格别名优化
如果不想修改代码,可以在gcc编译参数中添加-fno-strict-aliasing关闭该优化,不过这只是临时解决方案,会损失部分优化性能。
其他代码问题提示
你代码中存在一处永远不会执行的delete,导致内存泄漏:
if (rank == 0) { return new char[2]{ '0','\0' }; delete[] decimal_res; // return之后的代码永远不会执行 }
调整为delete在前return在后即可。
内容的提问来源于stack exchange,提问作者r33s3n6
相关产品推荐
相关产品推荐

