Reputation: 708
What is the difference between packing a struct vs. union vs. enum?
Regarding packed struct vs. packed union, there seems to be a minor difference in the assembly code generated on armv7l (but not on x86_64):
#include <stdio.h>
struct uint18_struct {
unsigned int var:18;
} __attribute__((packed));
typedef struct uint18_struct uint18_struct_t;
union uint18_union {
unsigned int var:18;
} __attribute__((packed));
typedef union uint18_union uint18_union_t;
int main (void)
{
unsigned int bar = ~0U;
volatile uint18_struct_t foo = *((uint18_struct_t *) &bar);
printf ("max of uint18_t = %u\n", foo.var);
volatile uint18_union_t baz = *((uint18_union_t *) &bar);
printf ("max of bf_u18_t = %u\n", baz.var);
return 0;
}
Generated assembly for foo and baz:
volatile uint18_struct_t foo = *((uint18_struct_t *) &bar);
10358: f107 020c add.w r2, r7, #12
1035c: f107 0308 add.w r3, r7, #8
10360: 8811 ldrh r1, [r2, #0]
10362: 7892 ldrb r2, [r2, #2]
10364: 8019 strh r1, [r3, #0]
10366: 709a strb r2, [r3, #2]
volatile uint18_union_t baz = *((uint18_union_t *) &bar);
1037a: f107 020c add.w r2, r7, #12
1037e: 1d3b adds r3, r7, #4
10380: 8811 ldrh r1, [r2, #0]
10382: 7892 ldrb r2, [r2, #2]
10384: 8019 strh r1, [r3, #0]
10386: 709a strb r2, [r3, #2]
Notice how the first two lines in the generated assembly (above ldrh
) differs.
System details:
$ uname -a
Linux beaglebone 4.19.19-bone-rt-r21 #1stretch Wed Feb 6 11:01:34 UTC 2019 armv7l GNU/Linux
$ gcc --version
gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Cmdline I used:
$ gcc -g -O0 --save-temps main.c
Which one would you prefer to represent a uint18_t type - packed struct or packed union? Why?
EDIT: Imagine an embedded controller needing to process raw data of 9-bits (but CHAR_BIT is still 8) each (hence, the need for uint18_t) and does not provide stdint.h, limits.h.
Upvotes: 1
Views: 136
Reputation: 153456
Which one would you prefer to represent a uint18_t type (?)
As
struct uint18_struct {
uint_least32_t var:18;
}
without the __attribute__((packed))
. It is most portable that way.
On a 16-bit int
system, hope that it accepts 32-bit int types in bit fields.
Otherwise consider the following and handle the sub-range needs myself.
struct uint18_struct {
uint_least32_t var;
}
IMO, OP has not made a good case yet for needing the packing. Perhaps once the larger issue is stated, we can work to solve the real problem.
Upvotes: 0