您可以在同一个联合中同时使用固定大小的类型和指针吗?

Can you use a fixed size type and pointers together in the same union?

我正在研究为数据包定义结构的可能性。我想在数据包中设置 header 变量,然后设置一个指向数据包数据部分的指针。我的最终目标是能够将此数据包发送到只需要 uint8_t* 的低级库。我创建了这个快速程序来测试可行性,但它似乎不起作用。

#include <iostream>
#include <cstdint>
#include <stdlib.h>

typedef union {
    struct {
        uint8_t header;
        uint8_t* data;
    };
    uint8_t* packet;
} sometype;

int main() {
    sometype s;
    s.header = 3;
    s.data = (uint8_t *) malloc(sizeof(uint8_t) * 2);
    s.data[0] = 1;
    s.data[1] = 2;

    for (unsigned int i = 0; i < 3; i++) {
        std::cout << s.packet[i] << std::endl;
    }

    std::cout << std::endl;

    std::cout << s.header << std::endl;
    std::cout << s.data[0] << std::endl;
    std::cout << s.data[1] << std::endl;
}

我的输出是

�
�
�




这让我意识到我的代码中存在某种类型的错误(我以前从未使用过 union)。但是,当我调试程序时,我可以看到联合中的数据。查看数据包,我发现此方法似乎不起作用。数据包中的数据不是3、1、2,而是300、221、020。

(gdb) print s
 = {{header = 3 '[=12=]3', data = 0x613c20 "[=12=]1[=12=]2"}, packet = 0x400903 <main()+125> "01ƿ`0`"}

我尝试的这个方法有效吗?从 google 搜索中我看到有人说你可以使用任何你想要的数据类型。我是否必须使用 pragma 打包结构才能使其工作,或者这种方法不可行?

是的,您必须使用 #pragma pack(1) 才能获得大多数工程师期望的行为。是的,这就是大多数通信低级软件的工作方式。

否则,出于性能和兼容性原因,编译器倾向于将每个元素与其数据大小对齐。

#pragma pack() 跨编译器具有巨大的交叉兼容性。参见 this for gcc

不正常的输出是因为您试图使用 << 来打印 uint8_t

通常(尽管 C++ 标准没有指定),uint8_t 会触发 << 的字符重载,因此您打印出对应于该字符代码的字形,而不是整数.为了避免这个问题,你可以做 std::cout << static_cast<int>(s.header); 等等


请注意,在标准 C++ 中,不允许写入联合的一个成员然后读取另一个成员,即您只能读取上次写入的同一个成员。您尝试使用的技术称为 union aliasing 并且在标准 C++ 中是不允许的,尽管编译器似乎支持它作为扩展。

然而,即使您使用的编译器确实提供联合别名,您仍然无法 s.packet[i] 使用当前的结构定义。这是因为 packetheaderdata 重叠。 header 的字节值不应是 packet 指向的地址的一部分,但您的代码会按原样对待它。

我猜你心里有一个单一指针的模型,你可以将指针指向的内存解释为一个 char 数组,或者解释为一个 char 后跟一个 char 数组。但是您的代码并没有反映出这一点(实际上您根本不能这样做,除非数组的长度在编译时已知)。

因为 headerdata[0] 不在连续的内存中,所以你不可能有一个指针指向一些虚构的内存块,其中这两个字节是邻近的。我建议放弃这整条调查线;只有一个内存块,您可以创建访问它的特定部分的函数。