我正在调查为数据包定义结构的可能性。我想在数据包中设置标头变量,然后设置指向数据包数据部分的指针。我的最终目标是能够将此数据包发送到只接受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打包结构才能使其正常工作或者这种方法不可行?
答案 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]
。这是因为packet
与header
和data
重叠。 header
的字节值不应该是packet
指向的地址的一部分,但是您的代码会像对待它一样对待它。
我猜你在精神上有一个单指针的模型,你可以将指针指向的内存解释为char数组,或者作为char后跟char数组。但是你的代码并没有反映出来(事实上你根本无法做到这一点,除非在编译时知道数组的长度)。
由于header
和data[0]
不在连续内存中,因此您无法使用指向某些虚拟内存块的单个指针字节是相邻的。我建议放弃整个调查线;只有一个内存块,你可以创建访问它的特定部分的函数。