unions as bit fields inside structures - c

I have the following:
typedef enum {
green = 0;
orange = 1;
red = 2;
} color;
typedef enum {
proceed = 0;
prepare = 1;
stop = 2;
} state;
typedef union {
color a;
state b;
uint8_t reserved;
} status;
typedef struct {
u32 m : 8;
u32 n : 8;
status var : 8;
u32 res : 8;
} info;
I am seeing a compilation error when I define a structure variable:
error: bit-field 'var' has invalid type.
I would like to pack the structure within the 4 bytes, and make the union of enums as a bit field. Is this possible?

Bit fields are defined and restricted only to data types int, signed int, unsigned int but nit for union type as per C89 or C90 standard. Bit Field, applies both for C/C++, with _Bool type defined in C99

If you already know the layout you want, best to ask for it as directly as possible. Probably something like:
typedef struct {
uint8_t m;
uint8_t n;
uint8_t var;
uint8_t res;
} info;
Use an explicitly sized type if you want a particular size. An enum type or union containing a member of enum type is allowed to be (at least) the same size as int, so your desire for this specific layout rules that out.

Related

C union assignment

I have union that represent some structures that all uint32 type but with different bit-fields. can i do assignment of one union to other like this:
typedef union foo_u
{
// raw
uint32_t foo32;
// interpretation 1
struct
{
uint16_t a;
uint16_t b;
} foo_flavor1;
//interpretation 2
struct
{
uint32_t a : 16;
uint32_t b : 12;
uint32_t c : 4;
} foo_flavor2;
} foo;
foo a;
foo b;
a.foo32 = 10;
b.foo32 = 30;
b=a;
or i have to do the assignment like this:
b.foo32 = a.foo32;
b = a;
It's totally OK to do this.
For structures and unions, assigning one to another of the same type is well-defined by the standard, and it's guaranteed that after the assignment, they should contain exactly the same data (padding excluded, if present).

Storing similar unions in an array in C

I have one union defined like this:
typedef union opWord
{
struct
{
unsigned short ARE : 2;
unsigned short opDest : 2;
unsigned short opSource : 2;
unsigned short opcode : 4;
unsigned short group : 2;
unsigned short unused : 3;
} word;
unsigned short fullReg;
} opWord;
and another very similar one only differing by bitfield arrangement:
typedef union immediate
{
struct
{
unsigned short ARE : 2;
unsigned short imm : 13;
} word;
unsigned short fullReg;
} immediate;
Can these be stored in the same array somehow since they only vary by the arrangement of the bits?
Something like
opWord instructions[1000]?
If not, is there a way to do it as a linked list?
How would I declare pointers for that?
You cannot directly store objects of your types opWord and immediate in the same array. You cannot declare such an array, and if you try to wrangle it with pointer casting and / or dynamically-allocated space then you run up against the strict aliasing rule.
What you can do is create a union type with an opWord and an immediate as members, and make an array of that type, as #jxh suggested in comments. In its simplest, most concise form, that might look like this:
union {
opWord op;
immediate imm;
} instructions[1000];
You could also consider merging your two union types into one, and creating an array of that type:
union instruction {
struct {
unsigned short ARE : 2;
unsigned short opDest : 2;
unsigned short opSource : 2;
unsigned short opcode : 4;
unsigned short group : 2;
unsigned short unused : 3;
} opWord;
struct {
unsigned short ARE : 2;
unsigned short imm : 13;
} immediate;
unsigned short fullReg;
};
union instruction instructions[1000];
That reduces the number of layers involved, which may be useful, at the cost of losing separate types for your two kinds of instructions.

Is it possible and correct to form union from bit-field in C?

I have the following union and it works correct:
#pragma pack(1)
...
union
{
uint8_t opcode;
struct
{
uint8_t z : 3;
uint8_t y : 3;
uint8_t x : 2;
};
}opcode;
The size of the union is exactly one byte, according to
printf ("%zu\n", sizeof opcode);
The problem takes place when I try to make a union from the bit-field there:
union
{
uint8_t opcode;
struct
{
uint8_t z : 3;
union
{
uint8_t y : 3;
struct
{
uint8_t p : 2;
uint8_t q : 1;
};
}y;
uint8_t x : 2;
};
}opcode;
The result of
printf ("%zu\n", sizeof opcode);
is 3 bytes. Of course I can workaround this with macros but it is it possible at all?
Your code will declare a 3 byte data structure because, as Klas pointed out already, a struct is always padded to the smallest addressable unit.
But it is possible to do what you want by embedding multiple top-level structs in the union and adding padding whenever necessary:
union {
uint8_t opcode;
struct {
uint8_t z:3;
uint8_t y:3;
uint8_t x:2;
};
struct {
uint8_t:3;
uint8_t p:2;
uint8_t q:1;
};
} opcode;
Note that it is possible to declare bit-fields without a name for padding. This is a standard C language feature.
No, it isn't possible to have structs that are fractions of a byte big.
Rationale:
It must be possible to make a pointer to a struct, and the smallest adressable unit is 1 byte.
Note:
This limitation exists in all compilers that I know. I don't know whether it is actually mandated by the C standard.

Typecasting variable with another typedef

typedef struct {
unsigned char a,
unsigned char b,
unsigned char c
}type_a;
typedef struct {
unsigned char e,
unsigned char f[2]
}type_b;
type_a sample;
sample.a = 1;
sample.b = 2;
sample.c = 3;
How do I typecast it and assign new value like:
sample = (type_b)sample; // syntax error
sample.f[1] = 'a';
You should really try it out yourself.
sample = (type_b)sample; /* Can't cast a structure to
another structure. */
sample.f[1] = 'a'; /* sample is still of type type_a,
and doesn't have an `f` field. */
No - C types are static, which means that sample will always remain of type type_a. However, you can achieve what you want using unions:
union {
type_a as_a;
type_b as_b;
} sample;
sample.as_a.a = 1;
sample.as_a.b = 2;
sample.as_a.c = 3;
sample.as_b.f[1] = 'a';
Note that it is not usual to create an object that is a bare union type like this; normally you would include the union within a struct, that includes a tag so that you know what type the object is at the present time:
struct {
enum { TYPE_A, TYPE_B } type;
union {
type_a as_a;
type_b as_b;
} data;
} sample;
/* sample is a TYPE_A right now */
sample.type = TYPE_A;
sample.data.as_a.a = 1;
sample.data.as_a.b = 2;
sample.data.as_a.c = 3;
/* sample is now a TYPE_B */
sample.type = TYPE_B;
sample.data.as_b.f[1] = 'a';
You can't cast one data type to another incompatible data type. However, the memory is open for you. You can access it as follows:
typedef struct
{
unsigned char a;
unsigned char b;
unsigned char c;
}type_a;
typedef struct
{
unsigned char e;
unsigned char f[2];
}type_b;
type_a sample;
sample.a = 1;
sample.b = 2;
sample.c = 3;
type_b *sample_b = (type_b *) ((void*) &sample);
Try out yourself accessing sample_b->e and sample_b->f and see what happens.
No. You can do it by casting pointers: value_b = *((value_b*)&value_a) or by creating union of those two types.
However you do it, be careful. Structures can have different data alignment and you may get unexpected results.
yes you can copy the value of the type_a into type_b by trying something like
type_b sample_b =*((type_b*)&sample);
or
memcpy(&sample_b,&sample,sizeof(type_a));
Typecasting is nothing but converting an expression of one type to another one. But you seem to be trying to convert the type itself, which is fixed at compile time(variable declaration)
Its not clear the idea behind trying something like this. If you can make it more clear, people would be able to give more insights

Union to unsigned long long int cast

I have a union as follows:
typedef unsigned long GT_U32;
typedef unsigned short GT_U16;
typedef unsigned char GT_U8;
typedef union
{
GT_U8 c[8];
GT_U16 s[4];
GT_U32 l[2];
} GT_U64;
I want to cast this union into the following:
typedef unsigned long long int UINT64;
The casting function I wrote is as follows:
UINT64 gtu64_to_uint64_cast(GT_U64 number_u)
{
UINT64 casted_number = 0;
casted_number = number_u.l[0];
casted_number = casted_number << 32;
casted_number = casted_number | number_u.l[1];
return casted_number;
}
This function is using the l member to perform the shifting and bitwise or. What will happen if the s or c members of the union are used to set its values?
I am not sure if this function will always cast the values correctly. I suspect it has something to do with the byte ordering of long and short. Can any body help?
Full example program is listed below.
#include <stdio.h>
typedef unsigned long GT_U32;
typedef unsigned short GT_U16;
typedef unsigned char GT_U8;
typedef union
{
GT_U8 c[8];
GT_U16 s[4];
GT_U32 l[2];
} GT_U64;
typedef unsigned long long int UINT64;
UINT64 gtu64_to_uint64_cast(GT_U64 number_u)
{
UINT64 casted_number = 0;
casted_number = number_u.l[0];
casted_number = casted_number << 32;
casted_number = casted_number | number_u.l[1];
return casted_number;
}
int main()
{
UINT64 left;
GT_U64 right;
right.s[0] = 0x00;
right.s[1] = 0x00;
right.s[2] = 0x00;
right.s[3] = 0x01;
left = gtu64_to_uint64_cast(right);
printf ("%llu\n", left);
return 0;
}
That's really ugly and implementation-dependent - just use memcpy, e.g.
UINT64 gtu64_to_uint64_cast(GT_U64 number_u)
{
UINT64 casted_number;
assert(sizeof(casted_number) == sizeof(number_u));
memcpy(&casted_number, &number_u, sizeof(number_u));
return casted_number;
}
First of all, please use the typedefs from "stdint.h" for such a purpose. You have plenty of assumptions of what the width of integer types would be, don't do that.
What will happen if the s or c members
of the union are used to set its
values?
Reading a member of a union that has been written to through another member may cause undefined behavior if there are padding bytes or padding bits. The only exception from that is unsigned char that may always be used to access the individual bytes. So access through c is fine. Access through s may (in very unlikely circumstances) cause undefined behavior.
And there is no such thing like a "correct" cast in your case. It simply depends on how you want to interpret an array of small numbers as one big number. One possible interpretation for that task is the one you gave.
This code should work independantly of padding, endianess, union accessing and implicit integer promotions.
uint64_t gtu64_to_uint64_cast (const GT_U64* number_u)
{
uint64_t casted_number = 0;
uint8_t i;
for(i=0; i<8; i++)
{
casted_number |= (uint64_t) number_u->c[i] << i*8U;
}
return casted_number;
}
If you can't change the declaration of the union to include an explicit 64-bit field, perhaps you can just wrap it? Like this:
UINT64 convert(const GT_U64 *value)
{
union {
GT_U64 in;
UINT64 out;
} tmp;
tmp.in = *value;
return tmp.out;
}
This does violate the rule that says you can only read from the union member last written to, so maybe it'll set your hair on fire. I think it will be quite safe though, don't see a case where a union like this would include padding but of course I could be wrong.
I mainly wanted to include this since just because you can't change the declaration of the "input" union doesn't mean you can't do almost the same thing by wrapping it.
Probably an easier way to cast is to use union with a long long member:
typedef unsigned long long int UINT64;
typedef unsigned long GT_U32;
typedef unsigned short GT_U16;
typedef unsigned char GT_U8;
typedef union
{
GT_U8 c[8];
GT_U16 s[4];
GT_U32 l[2];
UINT64 ll;
} GT_U64;
Then, simply accessing ll will get the 64-bit value without having to do an explicit cast. You will need to tell your compiler to use one-byte struct packing.
You don't specify what "cast the values correctly" means.
This code will cast in the simplest possible way, but it'll give different results depending on your systems endianness.
UINT64 gtu64_to_uint64_cast(GT_U64 number_u) {
assert(sizeof(UINT64) == sizeof(GT_U64));
return *(UINT64 *) &number_u;
}

Resources