Reputation: 297
I have a predefined struct (actually several) where variables span across 32-bit word boundary. In Linux (and Windows using GCC) I am able to get my structs to pack to the correct size using 'attribute((packed))'. However I cannot get it to work the same way using VC++ and #pragma pack.
Using GCC this returns a correct size of 6 bytes:
struct
{
unsigned int a : 3;
unsigned int b : 1;
unsigned int c : 15;
unsigned int troubleMaker : 16;
unsigned short padding : 13;
} __attribute__((packed)) s;
Using VC++ this returns an incorrect size of 8 bytes
#pragma pack(push)
#pragma pack(1)
struct
{
unsigned int a : 3;
unsigned int b : 1;
unsigned int c : 15;
unsigned int troubleMaker : 16;
unsigned short padding : 13;
} s;
#pragma pack(pop)
I can get things to work by splitting 'troubleMaker' across the boundary manually but I'd prefer not to. Any ideas?
Upvotes: 9
Views: 31581
Reputation: 146201
struct unpacked { // apparently my other example was too long and confusing
uint32_t a; // ...here is a much shorter example with only the conforming
uint32_t b; // ...code. (The other program had the gcc-specific declaration,
uint32_t c; // but only for test code. Still, it was a bit long.)
uint32_t troubleMaker;
};
struct unpacked su;
char *bits = "Lorem ipsum dolor";
void f(void) {
uint32_t x;
memcpy(&x, bits, 4);
su.a = x & 7;
su.b = x >> 3 & 1;
su.c = x >> 4 & 0x7fff;
memcpy(&x, bits + 2, 4);
su.troubleMaker = x >> 3 & 0xffff;
return 0;
}
Upvotes: 0
Reputation: 146201
I would suggest not using vendor-specific C language extensions to match device or network bit formats. Even if you get the fields to line up using a series of one-per-vendor language extensions, you still have byte order to worry about, and you still have a struct layout that requires extra instructions to access.
You can write a C99 conforming program that will work on any architecture or host and at maximum speed and cache efficiency by using the standardized C API string and memory copy functions and the Posix hton and ntoh functions.
A good practice is to use the following functions for which there exist published standards:
C99: memcpy(), Posix: htonl(), htons(), ntohl(), ntohs()
Update: here is some code that should work the same everywhere. You may need to get <stdint.h>
from this project if Microsoft still hasn't implemented it for C99, or just make the usual assumptions about int sizes.
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <arpa/inet.h>
struct packed_with_bit_fields { // ONLY FOR COMPARISON
unsigned int a : 3;
unsigned int b : 1;
unsigned int c : 15;
unsigned int troubleMaker : 16;
unsigned short padding : 13;
} __attribute__((packed)); // USED ONLY TO COMPARE IMPLEMENTATIONS
struct unpacked { // THIS IS THE EXAMPLE STRUCT
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t troubleMaker;
}; // NOTE NOT PACKED
struct unpacked su;
struct packed_with_bit_fields sp;
char *bits = "Lorem ipsum dolor";
int main(int ac, char **av) {
uint32_t x; // byte order issues ignored in both cases
// This should work with any environment and compiler
memcpy(&x, bits, 4);
su.a = x & 7;
su.b = x >> 3 & 1;
su.c = x >> 4 & 0x7fff;
memcpy(&x, bits + 2, 4);
su.troubleMaker = x >> 3 & 0xffff;
// This section works only with gcc
memcpy(&sp, bits, 6);
printf( sp.a == su.a
&& sp.b == su.b
&& sp.c == su.c
&& sp.troubleMaker == su.troubleMaker
? "conforming and gcc implementations match\n" : "huh?\n");
return 0;
}
Upvotes: 17
Reputation: 5050
Alignment and ordering of bitfields are notoriously implementation-specific. It is much safer to declare a normal integer field and manipulate the "bitfields" within using masks and bitwise (| & ^) operators .
Upvotes: 7
Reputation: 62333
If it absoloutely defnitely needs to be 6 bytes then define it as 3 shorts and get the data out yourself ... it won't slow things down ... the compiler is just doing this anyway ...
Upvotes: 0
Reputation: 755317
I don't believe this behavior is supported in Visual Studio. In addiction to the pack macro I tried using __declspec(align(1))
and got the same behavior. I think you are stuck with 12 bytes or re-ordering your structure a bit.
Upvotes: 2
Reputation: 20086
I believe VC++ doesn't support this, and I have grave doubts whether GCC's behaviour in this respect is actually standard.
Upvotes: 0