你能在同一个联盟中使用固定大小的类型和指针吗?

时间:2017-07-07 02:00:47

标签: c++ unions

我正在调查为数据包定义结构的可能性。我想在数据包中设置标头变量,然后设置指向数据包数据部分的指针。我的最终目标是能够将此数据包发送到只接受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
$1 = {{header = 3 '\003', data = 0x613c20 "\001\002"}, packet = 0x400903 <main()+125> "\300\211ƿ`\020`"}

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

2 个答案:

答案 0 :(得分:2)

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

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

编译器与#pragma pack()存在巨大的交叉兼容性。请参阅this for gcc

答案 1 :(得分:2)

异常输出是因为您尝试使用<<打印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]不在连续内存中,因此您无法使用指向某些虚拟内存块的单个指针字节是相邻的。我建议放弃整个调查线;只有一个内存块,你可以创建访问它的特定部分的函数。