C struct vs union in regard to bitfields - c

Admittedly this is a very silly question, but one that I was thinking about the other day. Sometimes in source I see registers defined like so:
union
{
uint8_t byte;
struct
{
uint8_t bit1 :1;
uint8_t bit2 :1;
uint8_t bit3_4 :2;
uint8_t rsvd :4;
} bitfield;
} SomeReg_t;
Other times I see just a standard struct:
struct
{
uint8_t bit1 :1;
uint8_t bit2 :1;
uint8_t bit3_4 :2;
uint8_t rsvd :4;
} SomeReg_t;
I wrote a quick example to show what I mean:
https://onlinegdb.com/r1H3Xuqe4
The question is, in regard to bitfields, what are the differences in these two definitions. When would you use one vs the other for bitfields.

It is easy to explain
Union is handy if you want to read or assign the whole byte as well.
for example
typedef union
{
uint8_t byte;
struct
{
uint8_t bit1 :1;
uint8_t bit2 :1;
uint8_t bit3_4 :2;
uint8_t rsvd :4;
} bitfield;
} SomeReg_t;
SomeReg_t reg;
reg.bitfield.bit1 = 1;
/*...*/
printf("The reg value is %hhx\n", reg.byte);
reg.byte = ReadFormPeriph();
if(reg.bitfield.rsvd == 0b1011)
{
/* do something */
}

Related

Size of struct dependent of the position a union is placed within

I have following problem: In my code example, the too_long_CommandFrameStruct shows a size of 5, wheras all members added together have the size of 4. If I change the position of the union member to be first (like in shorter_CommandFrameStruct), the size is, like expected, 4.
Why does this happen and how can I prevent this? I'd like to have the order of the struct members as-is, because the struct gets byte-by-byte written to a (4-byte long) SPI buffer frame.
With the 5-byte long struct I have problems, because a nibble gets added between the unsigned int crc member and the union Commands cmdData. This nibble is probably causing the struct to grow to 4.5 and thus 5 bytes.
The code can be executed in an online compiler, like e.g. https://www.tutorialspoint.com/compile_c_online.php
#include <stdio.h>
#include <stddef.h>
union UnspecificFrame_t
{
unsigned short RegValue;
struct /* Single Bits */
{
unsigned bit0 :1;
unsigned bit1 :1;
unsigned bit2 :1;
unsigned bit3 :1;
unsigned bit4 :1;
unsigned bit5 :1;
unsigned bit6 :1;
unsigned bit7 :1;
unsigned bit8 :1;
unsigned bit9 :1;
unsigned bit10 :1;
unsigned bit11 :1;
unsigned bit12 :1;
unsigned bit13 :1;
unsigned bit14 :1;
unsigned bit15 :1;
} __attribute__((packed, aligned(1))) Bits;
};
union Commands
{
union UnspecificFrame_t unSpecFrame;
};
/* This one should be 4 bytes long, but actually is 5 bites long */
struct too_long_CommandFrameStruct
{
unsigned int crc :4;
union Commands cmdData;
unsigned int cmdType :4;
unsigned int id :8;
}__attribute__((packed, aligned(1))) too_long_CommandFrameStruct_t;
/* When switching the first members, the size is right */
struct shorter_CommandFrameStruct
{
union Commands cmdData;
unsigned int crc :4;
unsigned int cmdType :4;
unsigned int id :8;
}__attribute__((packed, aligned(1))) shorter_CommandFrameStruct_t;
int main()
{
printf("why is this size: %d\n", sizeof(too_long_CommandFrameStruct_t)); // returns 5
printf("and this one size: %d?\n", sizeof(shorter_CommandFrameStruct_t)); // returns 4
return 0;
}
In C, objects other than bit-fields are represented with whole numbers of bytes. And the members of a structure are required to be in order in memory in the same order they are declared in the structure.
When unsigned int crc :4;, it does not have to fill a full byte, because it is a bit-field, but it does have to be inside one or more bytes that are used for it. Then union Commands cmdData follows that and takes two bytes. Then unsigned int cmdType :4; and unsigned int id :8; require 12 more bits, so that requires at least two bytes (presuming bytes are eight bits, as is common). So declaring the members requires at least five bytes.
Note that alignment is not even consider in the above; five bytes are required simply because you need one byte, then two, then two more.
When union Commands cmdData is first, it requires two bytes. Then the three bit-fields that follow it take 4+4+8 = 16 bits, which only need two more bytes. So the total is four bytes.

Why does this Union take up 2 bytes even though I'm aligning to one

I'm modeling a LCD hitachi display parallel port connection (for those interested HD44780, in 4 bit mode).
Even though alignment is set to 1,this union still takes up 2 bytes. If I get rid of the msnibble struct (directly put db4 to db7 in the struct in the union) it takes up 1 byte. Can structs take up less than 1 byte if they're inside a union? Is the 2 bytes the result of the msnibble taking up 1 byte plus the nibble (the enable bit,rs bit and the two blank bits), thus making the size 2 bytes?
Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef uint8_t byte_t;//could have used char
#pragma pack(push,1)
typedef union
{
byte_t whole_port :8;
struct
{
byte_t enable :1;
byte_t rs :1;
byte_t :1;
byte_t :1;
struct
{
byte_t db4 :1;
byte_t db5 :1;
byte_t db6 :1;
byte_t db7 :1;
}msnibble;
};
} para_port_t;
#pragma pack(pop)
int main(int argc, char** argv) {
printf("%u\n",sizeof(para_port_t));
return (EXIT_SUCCESS);
}
I'm working from netbeans in ubuntu compiling with gcc. As pragma is a compiler directive I wanted to know if it's the same in Microsoft Visual Studio?
Your final question asks if MSVC has 2 bytes for the union: yes it does.
Your union has one variant as struct nested within struct. How could that be 1 byte? Without the union,
#pragma pack(push,1)
typedef struct {
byte_t enable :1;
byte_t rs :1;
byte_t :1;
byte_t :1;
struct
{
byte_t db4 :1;
byte_t db5 :1;
byte_t db6 :1;
byte_t db7 :1;
}msnibble;
} thatstruct;
#pragma pack(pop)
has a size of 2. Where does it say that nested structs can be packed into 1 byte?
Can structs take up less than 1 byte if they're inside a union?
Nope. Your nested struct takes a full byte, and your outer struct rounds up to two bytes.

# in C header file

Reading a header file, which implements the mapping of I/O devices for a microcontroller, I stumbled about the following code snippet:
typedef union{
unsigned char Byte;
struct
{
unsigned char BIT0 :1;
unsigned char BIT1 :1;
...
unsigned char BIT7 :1;
}Bits;
}COP;
extern volatile COP _COP #(0x0000 + 0x000000006FUL);
I cannot make any sense of the last line since I do not know the # Operator.

Address bits within uint8_t as members of struct

I am currently writing code for a PIC micro-controller and I would like to structure some of my code using a uint8_t as a "state counter" for a part of my code. This involves a lot of bitwise operations. What I'd like to do is create a struct for this uint8_t in a similar vein to the SFR bits structs that are within the header flies, I have included an example of one from the header file that I'm using below.
The header file allows access to the bits within the SFR using a notation similar to accessing elements within a struct, eg U1STAbits.UTXBF and as a uint16_t this is what I'd like to implement in my code as it will allow me to use a switch statement as the main structure of my code.
#define U1STA U1STA
extern volatile unsigned int U1STA __attribute__((__sfr__));
__extension__ typedef struct tagU1STABITS {
union {
struct {
unsigned URXDA:1;
unsigned OERR:1;
unsigned FERR:1;
unsigned PERR:1;
unsigned RIDLE:1;
unsigned ADDEN:1;
unsigned URXISEL:2;
unsigned TRMT:1;
unsigned UTXBF:1;
unsigned UTXEN:1;
unsigned UTXBRK:1;
unsigned :1;
unsigned UTXISEL0:1;
unsigned UTXINV:1;
unsigned UTXISEL1:1;
};
struct {
unsigned :6;
unsigned URXISEL0:1;
unsigned URXISEL1:1;
};
};
} U1STABITS;
extern volatile U1STABITS U1STAbits __attribute__((__sfr__));
Update:
I have made a potential solution based from this:
typedef struct {
union {
struct{
unsigned breakDetected :1;
unsigned overrunError :1;
unsigned framingError :1;
unsigned startDetected :1;
unsigned dmxMode :1;
unsigned rdmMode :1;
};
uint8_t UartFlags;
};
} UartFlagsBits;
UartFlagsBits uartFlags;
By defining a union between a bitfield struct and a uint8_t I am able to access the bitfield as a number to perform actions on the whole bitfield as suggested by Alex.
typedef struct {
union {
struct{
uint8_t breakDetected :1;
uint8_t overrunError :1;
uint8_t framingError :1;
uint8_t startDetected :1;
uint8_t dmxMode :1;
uint8_t rdmMode :1;
};
uint8_t UartFlags;
};
} UartFlagsBits;
UartFlagsBits uartFlags;

structs and referring to them two different ways

I am reading someone's code I do not understand why he is referencing inside each union below differently.
-On the first one, he is able to reference inside by typing pwr_cmd.EN1
-On the second one, he is able to reference inside by typing errors.Bits.ERR1
-If I remove the Bits from the second one, will I be able to reference inside the the same way as the first one: errors.ERR1? -What is the difference between the two and why would you do one over the other? Thank you.
typedef union {
byte Byte;
union {
struct {
byte EN1 :1;
byte EN2 :1;
byte EN3 :1;
byte EN4 :1;
byte :4;
};
struct {
byte :2;
byte EN34 :2;
byte :4;
};
struct {
byte :2;
byte EN3r:1;
byte EN4r:1;
byte :4;
};
};
} pwr_cmd;
typedef union {
word Word;
union {
struct {
byte ERR1 :1;
byte ERR2 :1;
byte ERR3 :1;
byte ERR4 :1;
byte ERR5 :1;
byte ERR6 :1;
byte ERR7 :1;
byte ERR8 :1;
byte ERR9 :1;
byte DET1 :1;
byte DET2 :1;
byte FAIL1 :1;
byte PWR1 :1;
byte PWR2 :1;
byte PWR3 :1;
byte PWR4 :1;
} Bits;
nibble4 Nibble4;
};
} errors;
This is an example of a bitfield - each variable with a ":" represents a certain number of bits of an integer. This bitfield is unionized to allow the same bit positions to be named differently depending on context.
You would access one union member over another for clarity and to denote the intention of your code - this is a good way to self-document code, by creating meaningful names instead of something meaningless like "bit 4".
Re-reading your question - the difference between the two is that one uses named structs and the other doesn't. In the first, the members of the structs can be accessed without doing a struct member reference ("."), because they have no name. In the second, he declares a struct with a name, so the struct member reference is used. In my opinion the second is more clear, but the first is more concise. It's a matter of style and preference.

Resources