I have a question about bit packing in C++.
Lets say we have a struct defined in C++. Here it is below:
typedef struct
{
unsigned long byte_half : 4; //0.5
unsigned long byte_oneAndHalf : 12; //2
union
{
unsigned long byte_union_one_1 : 8; //3
unsigned long byte_union_one_2 : 8; //3
unsigned long byte_union_one_3 : 8; //3
};
unsigned long byte_one : 8; //4
}LongStruct;
It is a struct called LongStruct. From the looks of it, it occupies 4 bytes, and fits into a long. Now I execute the following line:
int size = sizeof(LongStruct);
I take a look at size, expecting it to have the value 4. Turns out I get 12 instead. In what way am I incorrectly visualizing my struct?
Thank you in advance for any help you can give me.
The union is expanded to a long, so its size is 4 bytes instead of 1 byte.
As a result, it is aligned to a 4-byte offset from the beginning of the structure.
In addition, the entire structure is expanded to be a multiple of 4 bytes in size.
So the actual structure looks like this:
unsigned long byte_half : 4; // bits 0 - 3
unsigned long byte_oneAndHalf : 12; // bits 4 - 15
unsigned long byte_padding_1 : 16; // bits 16 - 31 // align union
union
{
unsigned long byte_union_one_1 : 8; // bits 32 - 39
unsigned long byte_union_one_2 : 8; // bits 32 - 39
unsigned long byte_union_one_3 : 8; // bits 32 - 39
unsigned long byte_padding_2 : 24; // bits 40 - 63 // expand union
};
unsigned long byte_one : 8; // bits 64 - 71
unsigned long byte_padding_3 : 24; // bits 72 - 95 // expand structure
Hence the total size is 96 bits (12 bytes).
The anonymous union is not substituting its attributes, but is taking a four-byte chunk in the middle of your bit field struct. So your first two members are two bytes + two padding. Then your union is one byte plus three padding. Then your final member is one byte and three more padding. The total is the 12 you observe.
I'll try to dig into the standard to see exactly what it says about anonymous union bitfields. Alternately if you describe the real problem you're trying to solve we could look into answering that as well.
As an aside you have this tagged C++ so strongly prefer struct X {}; over typedef struct {} X;
Related
So. I am trying to convert a uint16_t (16 byte int) to class. To get the class member varaible. But it is not working as expected.
class test{
public:
uint8_t m_pcp : 3; // Defining max size as 3 bytes
bool m_dei : 1;
uint16_t m_vid : 12; // Defining max size as 12 bytes
public:
test(uint16_t vid, uint8_t pcp=0, bool dei=0) {
m_vid = vid;
m_pcp = pcp;
m_dei = dei;
}
};
int main() {
uint16_t tci = 65535;
test t = (test)tci;
cout<<"pcp: "<<t.m_pcp<<" dei: "<<t.m_dei<<" vid "<<t.m_vid<<"\n";
return 0;
}
Expected output:
pcp:1 dei: 1 vid 4095
The actual output:
pcp: dei: 0 vid 4095
Also,
cout<<sizeof(t)
returns 2. shouldn't it be 4?
Am I doing something wrong?
test t = (test)tci;
This line does not perform the cast you expect (which would be a reinterpret_cast, but it would not compile). It simply calls your constructor with the default values. So m_vid is assigned 65535 truncated to 12 bits, and m_pcp and m_dei are assigned 0. Try removing the constructor to see that it does not compile.
The only way I know to do what you want is to write a correct constructor, like so:
test(uint16_t i) {
m_vid = i & 0x0fff;
i >>= 12;
m_dei = i & 0x1;
i >>= 1;
m_pcp = i & 0x7;
}
Demo
Also I'm not sure why you would expect m_pcp to be 1, since the 3 highest bits of 65535 make 7.
Also, cout<<sizeof(t) returns 2. shouldn't it be 4?
No, 3+1+12=16 bits make 2 bytes.
Your bitfields have 16 bits in total, so 2 bytes is correct for size. (compiler will pack together adjacent bitfields -- but be wary since may vary across compilers) Your constructor on a single uint16_t value assigns just 12 bits of the value to m_vid and 0 to the other members. The first 12 bits of 65535 are 4095, so the output is correctly as you note (NOTE: your comments on bitfields being bytes should read "bits"), but your expectation for the others is off. The constructor clearly says to provide a 0 value for them if not specified.
I have a 8 byte CAN Bus message
15 E0 7F 34 17 5C 2 33
There is for example MCU_SelfCheckStatus at bit 52 and one bit long.
Or MCU_MotorTemp at bit 47 and 8 bits long.
The endianess is Motorola.
My readers endianess is little endian.
What would be an easy way to get them as a labeled data structure? Like:
bool isOk = msg.MCU_SelfCheckStatus;
uint8_t temp = msg.MCU_MotorTemp;
I thought about unions but I don't know if they allow such things.
I would simply apply logical AND operation.
bool isOk = msg & 0x20'0000'0000'0000;
bool temp = msg & 0x3F'8000'0000'0000;
Explanation:
why & with 0x20'0000'0000'0000 for MCU_SelfCheckStatus? Well if you flip only the 52th bit of 8byte long type, you will get that number.
why & with 0x3F'8000'0000'0000 for MCU_MotorTemp? Similarly, if you flip only the 47th bit and next 8 bits of 8byte long type, you will get that number.
Hope it helps.
I got it working with unions:
typedef union
{
struct
{
unsigned int MCU_DC_Link_Volt : 16;
unsigned int MCU_DC_Link_Curr : 16;
unsigned int MCU_InverterTemp : 8;
unsigned int MCU_MotorTemp : 8;
unsigned int MCU_SelfCheckStatus : 1;
unsigned int MCU_IgnitionSts : 1;
unsigned int MCU_VCU_InverterCtrlSts : 1;
unsigned int MCU_2_MessageCounter : 4;
unsigned int MCU_2_Checksum : 8;
};
uint8_t raw[8];
} MCU_2_415;
The LSB (dark blue) for the first value is 8
While the MSB (orange) is 7.
I want to set bit in a long long int number on a 64 bit machine.
Example, i want to set bit at element 18 19, i use the following code:
A1 |= 1 << 2 * i; // i = 9 , set bit 18 =1, A1 long long int
A1 &= ~(1 << 2 * i + 1); //clear bit 19 = 0
but it doesn't work. If i do it for long int, it works fine.
The literal 1 has type int, which is probably smaller than long long. You'll get undefined behaviour (typically a value of zero) if you shift by enough bits to overflow the int type; and the second line will (probably) clear any bits present in A1 but not in an int value.
Use 1LL to specify a long long type, or decltype(A1)(1) to specify a type that matches A1.
In general, it's usually best to use unsigned types (unsigned long long or perhaps uint64_t in this case) for bit-wrangling. Use 1ULL to get a literal of type unsigned long long.
I have a requirement, where 3 bytes (24 bits) need to be populated in a binary protocol. The original value is stored in an int (32 bits). One way to achieve this would be as follows:-
Technique1:-
long x = 24;
long y = htonl(x);
long z = y>>8;
memcpy(dest, z, 3);
Please let me know if above is the correct way to do it?
The other way, which i dont understand was implemented as below
Technique2:-
typedef struct {
char data1;
char data2[3];
} some_data;
typedef union {
long original_data;
some_data data;
} combined_data;
long x = 24;
combined_data somedata;
somedata.original_data = htonl(x);
memcpy(dest, &combined_data.data.data2, 3);
What i dont understand is, how did the 3 bytes end up in combined_data.data.data2 as opposed to first byte should go into combined_data.data.data1 and next 2 bytes should go into
combined_data.data.data2?
This is x86_64 platform running 2.6.x linux and gcc.
PARTIALLY SOLVED:-
On x86_64 platform, memory is addressed from right to left. So a variable of type long with value 24, will have following memory representation
|--Byte4--|--Byte3--|--Byte2--|--Byte1--|
0 0 0 0x18
With htonl() performed on above long type, the memory becomes
|--Byte4--|--Byte3--|--Byte2--|--Byte1--|
0x18 0 0 0
In the struct some_data, the
data1 = Byte1
data2[0] = Byte2
data2[1] = Byte3
data4[2] = Byte4
But my Question still holds, Why not simply right shift by 8 as shown in technique 1 ?
A byte takes 8 bits :-)
int x = 24;
int y = x<<8;
moving by 0 you are changing nothing. By 1 - *2, by 2 - *4, by 8 - *256.
if we are on the BIG ENDIAN machine, 4 bytes are put in memory as so: 2143. And such algorythms won't work for numbers greater than 2^15. On the other way, on the BIG ENDIAN machine you should define, what means " putting integer in 3 bytes"
Hmm. I think, the second proposed algorythm will be ok, but change the order of bytes:
You have them as 2143. You need 321, I think. But better check it.
Edit: I checked on wiki - x86 is little endian, they say, so algorythms are OK
I am using ms c++. I am using struct like
struct header {
unsigned port : 16;
unsigned destport : 16;
unsigned not_used : 7;
unsigned packet_length : 9;
};
struct header HR;
here this value of header i need to put in separate char array.
i did memcpy(&REQUEST[0], &HR, sizeof(HR));
but value of packet_length is not appearing properly.
like if i am assigning HR.packet_length = 31;
i am getting -128(at fifth byte) and 15(at sixth byte).
if you can help me with this or if their is more elegant way to do this.
thanks
Sounds like the expected behaviour with your struct as you defined packet_length to be 9 bits long. So the lowest bit of its value is already within the fifth byte of the memory. Thus the value -128 you see there (as the highest bit of 1 in a signed char is interpreted as a negative value), and the value 15 is what is left in the 6th byte.
The memory bits look like this (in reverse order, i.e. higher to lower bits):
byte 6 | byte 5 | ...
0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0
packet_length | not_used | ...
Note also that this approach may not be portable, as the byte order inside multibyte variables is platform dependent (see endianness).
Update: I am not an expert in cross-platform development, neither did you tell much details about the layout of your request etc. Anyway, in this situation I would try to set the fields of the request individually instead of memcopying the struct into it. That way I could at least control the exact values of each individual field.
struct header {
unsigned port : 16;
unsigned destport : 16;
unsigned not_used : 7;
unsigned packet_length : 9;
};
int main(){
struct header HR = {.packet_length = 31};
printf("%u\n", HR.packet_length);
}
$ gcc new.c && ./a.out
31
Update:
i now that i can print that value directly by using attribute in struct. But i need to send this struct on network and their i am using java.
In that case, use an array of chars (length 16+16+7+9) and parse on the other side using java.
Size of array will be less than struct, and more packing could be possible in a single MTU.