C/C++混合环境下用union做类型双关是否属于未定义行为?
C/C++混合环境下Union类型双关的规则与行为分析
C11标准明确允许通过union进行类型双关操作,但C标准(直至C20)并未认可这种行为的合法性。当代码中同时存在C和C++模块时,我们需要明确不同场景下的标准约束与编译器实际表现,以下结合示例代码逐一分析:
示例代码
c_code.c
#include "c_code.h" // 变量声明位置是否影响结果? union UnionTest test; int read_int_in_c(void) { return test.m_int; } void write_int_in_c(void) { test.m_int = 3; } float read_float_in_c(void) { return test.m_float; } void write_float_in_c(void) { test.m_float = 3.14f; }
c_code.h
#ifndef C_CODE_H #define C_CODE_H #ifdef __cplusplus extern "C" { #endif union UnionTest { int m_int; float m_float; }; extern union UnionTest test; int read_int_in_c(void); void write_int_in_c(void); float read_float_in_c(void); void write_float_in_c(void); #ifdef __cplusplus } #endif #endif /* C_CODE_H */
cpp_code.h
#ifndef CPP_CODE_H #define CPP_CODE_H #include "c_code.h" int read_int_in_cpp(void); void write_int_in_cpp(void); float read_float_in_cpp(void); void write_float_in_cpp(void); #endif /* CPP_CODE_H */
cpp_code.cpp
#include "cpp_code.h" // 变量声明位置是否影响结果? //UnionTest test; int read_int_in_cpp(void) { return test.m_int; } void write_int_in_cpp(void) { test.m_int = 3; } float read_float_in_cpp(void) { return test.m_float; } void write_float_in_cpp(void) { test.m_float = 3.14f; }
main.cpp
#include "c_code.h" #include "cpp_code.h" #include <iostream> int main() { // 场景1 write_int_in_c(); float test1 = read_float_in_cpp(); std::cout << "test1 的值为:" << test1 << std::endl; // 场景3 write_int_in_cpp(); float test2 = read_float_in_c(); std::cout << "test2 的值为:" << test2 << std::endl; return 0; }
编译命令
cc -Wall -c c_code.c -o c_code.o c++ -Wall -c cpp_code.cpp -o cpp_code.o c++ -Wall main.cpp c_code.o cpp_code.o -o exe
四种场景的标准约束与实际行为
场景1:Union实例在C文件声明,C写入、C++读取
- 标准层面:C代码的写入操作符合C11规范,但C读取未被激活的union成员违反C对象生命周期规则,属于未定义行为。
- 编译器实际行为:GCC、Clang、MSVC等主流编译器通常会兼容C的行为,默认或优化模式下能得到预期的类型转换结果。
场景2:Union实例在C文件声明,C写入、C读取
- 标准层面:C写入操作合法,但C++读取非活跃成员仍属于未定义行为。
- 编译器实际行为:与场景1一致,主流编译器会正常处理,结果符合预期。
场景3:Union实例在C文件声明,C++写入、C读取
- 标准层面:C++写入仅激活
m_int成员,C读取m_float符合C11规范,但跨语言的对象状态未被双标准明确约束,整体仍存在模糊性。 - 编译器实际行为:主流编译器会将union视为连续内存块处理,能正确返回类型双关后的结果。
- 标准层面:C++写入仅激活
场景4:Union实例在C文件声明,C写入、C读取
- 标准层面:C写入激活
m_int,C读取m_float符合C11,但C标准不认可该跨成员读取行为,属于未定义行为。 - 编译器实际行为:与场景3一致,主流编译器会兼容该操作,得到预期结果。
- 标准层面:C写入激活
总结
C和C标准对union类型双关的规定存在差异,但主流编译器在混合编译场景下,通常会提供非标准扩展兼容此类操作。不过从严格标准角度,涉及C读取非活跃union成员的操作均为未定义行为,若追求可移植性,建议使用memcpy进行类型转换,这是双标准均认可的合法方式。
内容的提问来源于stack exchange,提问作者Derek McCants
相关产品推荐
相关产品推荐

