Reputation: 374
I'm investigating the possibility of defining a structure for a packet. I would like to set header variables in the packet and then set a pointer to the data part of the packet. My end goal is to be able to send this packet to a low level library that takes only a uint8_t*. I created this quick program to test the feasibility and it does not seem to work.
#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;
}
My output is
�
�
�
Which makes me realize I have some type of error in my code (I've never worked with union before). However, when I debug the program I can see the data in the union. Looking at the packet, I can see that this method does not appear to be working. The data in the packet is not 3, 1, 2. It is 300, 221, 020 instead.
(gdb) print s
$1 = {{header = 3 '\003', data = 0x613c20 "\001\002"}, packet = 0x400903 <main()+125> "\300\211ƿ`\020`"}
Is this method that I am attempting valid? From google searches I saw someone say you can use whatever datatypes you want. Do I have to pack the structure using a pragma to get this to work or is this method not feasible?
Upvotes: 0
Views: 56
Reputation: 141648
The unusual output is because you attempt to use <<
to print a uint8_t
.
Usually (although the C++ standard doesn't specify this), uint8_t
triggers the character overload of <<
, so you print out the glyph corresponding to that character code, instead of the integer. To avoid this hiccup you could do std::cout << static_cast<int>(s.header);
etc.
Note that in Standard C++ it is not permitted to write one member of a union and then read a different member, i.e. you may only read the same member that was last written. The technique you are trying to use is called union aliasing and is not allowed in Standard C++, although compilers may seem to support it as an extension.
However, even if you're on a compiler that does offer union aliasing, you still won't be able to do s.packet[i]
with your current struct definition. This is because packet
overlaps with header
and data
. The byte value of header
should not be a part of the address packet
is pointing to, but your code treats it like it is.
I guess you mentally had a model of a single pointer and you can interpret the memory being pointed to by the pointer as either a char array, or as a char followed by a char array. But your code doesn't reflect that (and in fact you can't do that at all, unless the lengths of the arrays are known at compile-time).
Since header
and data[0]
are not in contiguous memory, there's no way you are going to be able to have a single pointer that points to some fictitious memory block in which those two bytes are adjacent. I would recommend giving up on this entire line of enquiry; just have a single memory block, and you can make functions that access particular parts of it.
Upvotes: 2
Reputation: 57784
Yes, you have to use #pragma pack(1)
to get the behavior most engineers expect. And yes, this is how most communication low level software works.
Otherwise, compilers tend to align each element to its data size for performance and compatibility reasons.
There is enormous cross-compatibility with #pragma pack()
across compilers. See this for gcc.
Upvotes: 2