c++ - 将 union 字段中的位解释为 C/C++ 中的不同数据类型

标签 c++ c bit-manipulation unions

我正在尝试以不同的数据类型访问 union 位。例如:

    typedef union {
    uint64_t x;
    uint32_t y[2];
    }test;

    test testdata;
    testdata.x = 0xa;
    printf("uint64_t: %016lx\nuint32_t: %08x %08x\n",testdata.x,testdata.y[0],testdata.y[1]);
    printf("Addresses:\nuint64_t: %016lx\nuint32_t: %p %p\n",&testdata.x,&testdata.y[0],&testdata.y[1]);

输出是

uint64_t: 000000000000000a
uint32_t: 0000000a 00000000
Addresses:
uint64_t: 00007ffe09d594e0
uint32_t: 0x7ffe09d594e0 0x7ffe09d594e4

y指向的起始地址与x的起始地址相同。由于两个字段使用相同的位置,x 的值不应该是 00000000 0000000a 吗?

为什么这没有发生?在具有不同数据类型的不同字段的 Union 中如何进行内部转换?

需要做什么才能使用 union 以与 uint64_t 中相同的顺序检索 uint32_t 中的确切原始位?

编辑: 如评论中所述,C++ 给出了未定义的行为。 它在 C 中是如何工作的?我们真的可以做到吗?

最佳答案

我将首先解释您的实现过程中发生了什么。

您正在 uint64_t 值和 2 个 uint32_t 值的数组之间进行类型双关。根据结果​​,您的系统是小字节序的,并且很乐意通过简单地重新解释字节表示来接受这种类型的双关语。 0x0a 的字节表示为小端 uint64_t 是:

Byte number  0    1    2    3    4    5    6    7  
Value        0x0a 0x00 0x00 0x00 0x00 0x00 0x00 0x00

little endian 中的最低有效字节具有最低地址。现在很明显为什么 uint32_t[2] 表示是 { 0x0a, 0x00 }

但是你所做的只是在C语言中是合法的。

C语言:

C11 如 6.5.2.3 结构和 union 成员所述:

3 A postfix expression followed by the . operator and an identifier designates a member of a structure or union object. The value is that of the named member,95) and is an lvalue if the first expression is an lvalue.

95) 注释明确指出:

If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called ‘‘type punning’’). This might be a trap representation.

因此,即使注释不是规范的,它们的目的是明确解释标准的方式 => 你的代码是有效的,并且在定义 uint64_tuint32_t 类型。

C++ 语言:

C++ 在这方面更为严格。 C++17 的 n4659 草案在 [basic.lval] 中说:

8 If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:56
(8.1) — the dynamic type of the object,
(8.2) — a cv-qualified version of the dynamic type of the object,
(8.3) — a type similar (as defined in 7.5) to the dynamic type of the object,
(8.4) — a type that is the signed or unsigned type corresponding to the dynamic type of the object,
(8.5) — a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
(8.6) — an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
(8.7) — a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
(8.8) — a char, unsigned char, or std::byte type.

注释 56 明确表示:

The intent of this list is to specify those circumstances in which an object may or may not be aliased.

由于 双关 在 C++ 标准中从未被引用,并且由于结构/union 部分不包含 C 的重新解释 的等价物,这意味着读入C++ 不是最后写入的成员的值调用未定义的行为。


当然,常见的编译器实现同时编译 C 和 C++,并且大多数甚至在 C++ 源代码中也接受 C 惯用语,原因与 gcc C++ 编译器乐于在 C++ 源文件中接受 VLA 的原因相同。毕竟,未定义的行为 包含预期的结果...但是您不应该依赖它来获取可移植代码。

关于c++ - 将 union 字段中的位解释为 C/C++ 中的不同数据类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51130932/

相关文章:

c++ - 在 C++ 中从 vector<int> 生成子列表

c++ - C++ 中的 Qt3d 输入

c - 多请求单响应同步

algorithm - 如何计算32位整数中的设置位数?

c - 为什么这个大于函数起作用?

algorithm - 这是如何运作的?河内奇怪的塔解决方案

c++ - 输出字符串/字符时出错 (C++)

c++ - 将 matlab 函数转换为 C/C++

c - 使用 glTimerFunc 的 GLUT 动画

c - 通过 UNIX 域套接字发送文件描述符和 select()