You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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视为连续内存块处理,能正确返回类型双关后的结果。
  • 场景4:Union实例在C文件声明,C写入、C读取

    • 标准层面:C写入激活m_int,C读取m_float符合C11,但C标准不认可该跨成员读取行为,属于未定义行为。
    • 编译器实际行为:与场景3一致,主流编译器会兼容该操作,得到预期结果。

总结

C和C标准对union类型双关的规定存在差异,但主流编译器在混合编译场景下,通常会提供非标准扩展兼容此类操作。不过从严格标准角度,涉及C读取非活跃union成员的操作均为未定义行为,若追求可移植性,建议使用memcpy进行类型转换,这是双标准均认可的合法方式。

内容的提问来源于stack exchange,提问作者Derek McCants

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 20:35:21