您可以在同一个联合中同时使用固定大小的类型和指针吗?
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]
使用当前的结构定义。这是因为 packet
与 header
和 data
重叠。 header
的字节值不应是 packet
指向的地址的一部分,但您的代码会按原样对待它。
我猜你心里有一个单一指针的模型,你可以将指针指向的内存解释为一个 char 数组,或者解释为一个 char 后跟一个 char 数组。但是您的代码并没有反映出这一点(实际上您根本不能这样做,除非数组的长度在编译时已知)。
因为 header
和 data[0]
不在连续的内存中,所以你不可能有一个指针指向一些虚构的内存块,其中这两个字节是邻近的。我建议放弃这整条调查线;只有一个内存块,您可以创建访问它的特定部分的函数。
我正在研究为数据包定义结构的可能性。我想在数据包中设置 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]
使用当前的结构定义。这是因为 packet
与 header
和 data
重叠。 header
的字节值不应是 packet
指向的地址的一部分,但您的代码会按原样对待它。
我猜你心里有一个单一指针的模型,你可以将指针指向的内存解释为一个 char 数组,或者解释为一个 char 后跟一个 char 数组。但是您的代码并没有反映出这一点(实际上您根本不能这样做,除非数组的长度在编译时已知)。
因为 header
和 data[0]
不在连续的内存中,所以你不可能有一个指针指向一些虚构的内存块,其中这两个字节是邻近的。我建议放弃这整条调查线;只有一个内存块,您可以创建访问它的特定部分的函数。