C union pointer to array - c

I have an array of unsigned 16-bit integers:
static uint16_t dataArray[7];
The bits of the 7th element of the array represents some kind of status. I want to get and set the values of this status in an easy way, without bit shifting and without having to copy a new value to the array every time the status changes. So I created a union with a struct, and a pointer:
typedef struct {
unsigned statusCode : 4;
unsigned errorCode : 4;
unsigned outputEnabled : 1;
unsigned currentClip : 1;
unsigned : 6;
} SupplyStruct_t;
typedef union {
SupplyStruct_t s;
uint16_t value;
} SupplyStatus_t;
static SupplyStatus_t * status;
My initialisation routine wants the status pointer to point to the 7th element of the array, so I tried:
status = &(dataArray[6]);
Although this works, I get a warning: assignment from incompatible pointer type
Is there a better way to do this? I cannot change the array, but I am free to change the structure, the union or the pointer to the array..

Change unsigned to uint16_t
why? - test the difference:
https://ideone.com/uHLzpV
#include <stdio.h>
#include <stdint.h>
typedef struct {
uint16_t statusCode : 4;
unsigned errorCode : 4;
unsigned outputEnabled : 1;
unsigned currentClip : 1;
unsigned : 6;
} SupplyStruct_t;
typedef struct {
uint16_t statusCode : 4;
uint16_t errorCode : 4;
uint16_t outputEnabled : 1;
uint16_t currentClip : 1;
uint16_t : 6;
} SupplyStruct_t1;
typedef union {
SupplyStruct_t s;
uint16_t value;
} SupplyStatus_t;
typedef union {
SupplyStruct_t1 s;
uint16_t value;
} SupplyStatus_t1;
int main(void) {
printf("%zu %zu\n", sizeof(SupplyStatus_t), sizeof(SupplyStatus_t1));
return 0;
}
The most correct way is to declare the table as table of structs.
If not :
If you want too work on the bitfields you do not actually have to declare the pointer.
static SupplyStatus_t status;
status.value = dataArray[6];
and it is almost portable and safe way
you can also cast it explicitly

The warning says that uint16_t* is not compatible with SupplyStatus_t*.
If you want to get rid of this warning, cast it to SupplyStatus_t*:
status = (SupplyStatus_t*)&(dataArray[6]);
I also would put the union and struct together:
typedef union
{
struct
{
unsigned statusCode : 4;
unsigned errorCode : 4;
unsigned outputEnabled : 1;
unsigned currentClip :1;
unsigned unused : 6;
} s;
uint16_t value;
} SupplyStatus_t;

Related

Why is this UNION like a struct?

I came across some code (it's in a library from Microchip) which has a union.
All was good, until I saw it assigned values to different members of the union right after each other. My immediate thought was "They are over writing the same location..."
Then I decided to do a test. By every measure I thought I understood, this union should be one byte (8 bits). But it's not... it's 4 bytes.
#pragma pack(1)
typedef union _STATUS
{
BYTE Val;
struct {
unsigned BC8 : 1;
unsigned BC9 : 1;
unsigned BSTALL : 1;
unsigned DTSEN : 1;
unsigned INCDIS : 1;
unsigned KEN : 1;
unsigned DTS : 1;
unsigned UOWN : 1;
};
struct {
unsigned BC8 : 1;
unsigned BC9 : 1;
unsigned PID0 : 1;
unsigned PID1 : 1;
unsigned PID2 : 1;
unsigned PID3 : 1;
unsigned : 1;
unsigned UOWN : 1;
};
struct {
unsigned : 2;
unsigned PID : 4;
unsigned : 2;
};
} STATUS;
void PrintIt() {
printf("Size of UNION is %d \n", sizeof(STATUS));
}
It should be the largest of any member, which each member is only 8 bits.
The code that caught my eye and made me investigate this is:
STAT.BC9 = 0;
STAT.BC8 = 0;
STAT.Val |= byteToSend;
Which the third line merges into the values from the first and second.
So I wanted to test it, it's coming out as 4 bytes, not one. I even tested it in a few different compilers (hence the #pragma usage for MS Visual C).
Each member is exactly 8 bits, and the last two struct overlap to place the PID values in the same memory location. And yet this is 4 bytes every way I use a compiler to evaluate it.
Is there something in the behavior of adding structs to unions?
Any explanation is appreciated.
While not explicitly specified in the C standard, bitfield will typically occupy a unit corresponding to the base type they are declared with.
In this case all of the bitfields are declared as unsigned. This type is probably 4 bytes on your system so the bitfields occupy a unit of that type.
If you change the types of the fields to unsigned char or uint8_t they should take up only one byte. Note that this assumes your compiler allows using these types for bitfields, although most do.
C has the concept of implicit type. So unsigned will declare an unsigned int. But it gets weirder. The same goes if you only use const or static/auto.
const x = 5; // Declares a const int variable
static x; // Declares a static int variable
const static unsigned x = 5; // Declares a const static unsigned int variable
What you want is an unsigned char.
It should be the largest of any member, which each member is only 8 bits.
It should be at least that size. Nothing prevents it from being bigger.
It will not compile as you have duplicate member names in anonymous structures.
You do need to pack it if you use 8 bits type for your bitfields.
typedef union
{
unsigned char Val;
struct {
unsigned char BC8 : 1;
unsigned char BC9 : 1;
unsigned char BSTALL : 1;
unsigned char DTSEN : 1;
unsigned char INCDIS : 1;
unsigned char KEN : 1;
unsigned char DTS : 1;
unsigned char UOWN : 1;
};
struct {
unsigned char BC81 : 1;
unsigned char BC91 : 1;
unsigned char PID0 : 1;
unsigned char PID1 : 1;
unsigned char PID2 : 1;
unsigned char PID3 : 1;
unsigned char : 1;
unsigned char UOWN1 : 1;
};
struct {
unsigned char : 2;
unsigned char PID : 4;
unsigned char : 2;
};
} STATUS;
int main(void) {
printf("Size of UNION is %d \n", (int)sizeof(STATUS));
}
https://godbolt.org/z/zsfhnGeWd
Remember that some compilers will pack to the type you specify (chibicc for example).
While dbush was answering, I was adding my own answer as well, after digging deeper.
I would describe it as:
The "unsigned" implies an "int" So you are defining the first 8 bits of an integer storage.
typedef union _STATUS {
BYTE Val;
struct {
unsigned char BC8 : 1;
unsigned char BC9 : 1;
unsigned char BSTALL : 1;
unsigned char DTSEN : 1;
unsigned char INCDIS : 1;
....
} STATUS;
When limiting the size to unsigned char, the union is now actually 1 byte.

Can a bit field structure be instantiated as an immediate?

Suppose I have the following (made up) definition:
typedef union {
struct {
unsigned int red: 3;
unsigned int grn: 3;
unsigned int blu: 2;
} bits;
uint8_t reg;
} color_t;
I know I can use this to initialize a variable that gets passed to a function, such as :
color_t white = {.red = 0x7, .grn = 0x7, .blu = 0x3};
printf("color is %x\n", white.reg);
... but in standard C, is it possible to instantiate a color_t as an immediate for passing as an argument without assigning it first to a variable?
[I discovered that yes, it's possible, so I'm answering my own question. But I cannot promise that this is portable C.]
Yes, it's possible. And the syntax more or less what you'd expect. Here's a complete example:
#include <stdio.h>
#include <stdint.h>
typedef union {
struct {
unsigned int red: 3;
unsigned int grn: 3;
unsigned int blu: 2;
} bits;
uint8_t reg;
} color_t;
int main() {
// initializing a variable
color_t white = {.bits={.red=0x7, .grn=0x7, .blu=0x3}};
printf("color1 is %x\n", white.reg);
// passing as an immediate argument
printf("color2 is %x\n", (color_t){.bits={.red=0x7, .grn=0x7, .blu=0x3}}.reg);
return 0;
}

Why does packing not work across sibling unions or structs

In the following example I expect the size of complex_t to be the same as uint16_t: 2 bytes, however it's 3 bytes.
Removing the second union ("proximity_unsafe") reduces the size to 2 bytes, but I can't figure out the model of the packing rules.
#include <stdint.h>
#include <stdio.h>
typedef union {
uint16_t unsafe;
struct {
uint16_t backwardmotion_unsafe : 1;
uint16_t batteryvoltage_unsafe : 1;
union {
uint16_t dropoff_unsafe : 4;
struct {
uint16_t dropofffrontleft_unsafe : 1;
uint16_t dropofffrontright_unsafe : 1;
uint16_t dropoffsideleft_unsafe : 1;
uint16_t dropoffsideright_unsafe : 1;
}__attribute__((__packed__));
}__attribute__((__packed__));
union {
uint16_t proximity_unsafe : 3;
struct {
uint16_t proximityfront_unsafe : 1;
uint16_t proximityleft_unsafe : 1;
uint16_t proximityright_unsafe : 1;
}__attribute__((__packed__));
}__attribute__((__packed__));
} __attribute__((__packed__));
} __attribute__((__packed__)) complex_t;
int main()
{
printf("sizeof(complex_t): %i", sizeof(complex_t));
printf("sizeof(uint16_t): %i", sizeof(uint16_t));
}
Because it is legal to take the address of any named struct member that is not a bitfield, such non-bitfield members are required to start at byte boundaries. While it is not possible to take the address of anonymous members, and it would thus theoretically be possible for a compiler to allow such objects to start at arbitrary bit boundaries, that would imply that the layout of a structure would vary based upon whether its members were named.

C error: dereferencing pointer to incomplete type, struct

I know this question is asked tons of times, but I cannot seem to link it to my problem.
My problem is something to do with filling out a web of structs
Here is my buggy code
src\fpu.c:17:7: error: dereferencing pointer to incomplete type
cpu->instr.fpu.control = 0x37F;
^
This is the buggy code in a function
void fpuInit(struct emu_cpu *cpu) {
cpu->instr.fpu.control = 0x37F;
cpu->instr.fpu.status = 0;
cpu->instr.fpu.tag = 0xFFFF;
cpu->instr.fpu.lastDataPointer = 0;
cpu->instr.fpu.lastDataSeg = 0;
cpu->instr.fpu.lastIP = 0;
cpu->instr.fpu.lastIPseg = 0;
cpu->instr.fpu.opcode = 0;
}
Here is how the struct web looks like
cpu
struct emu_cpu
{
struct emu *emu;
struct emu_memory *mem;
uint32_t debugflags;
uint32_t eip;
uint32_t eflags;
uint32_t reg[8];
uint16_t *reg16[8];
uint8_t *reg8[8];
struct emu_instruction instr;
struct emu_cpu_instruction_info *cpu_instr_info;
uint32_t last_fpu_instr[2];
char *instr_string;
bool repeat_current_instr;
struct emu_track_and_source *tracking;
};
instruction
struct emu_instruction
{
uint16_t prefixes;
uint8_t opcode;
uint8_t is_fpu : 1;
union
{
struct emu_cpu_instruction cpu;
struct emu_fpu_instruction fpu;
};
struct
{
struct emu_tracking_info init;
struct emu_tracking_info need;
} track;
struct
{
uint8_t has_cond_pos : 1;
uint32_t norm_pos;
uint32_t cond_pos;
} source;
};
fpu struct
union FpuMmxRegister {
long double fp;
unsigned char b[10]; //only use 8 of these for mmx
unsigned short s[4];
unsigned int i[2];
unsigned long long ll;
};
struct emu_fpu_instruction
{
uint16_t prefixes;
uint8_t fpu_opcode;
//uint8_t fpu_modrm;
struct /* mod r/m data */
{
union
{
uint8_t mod : 2;
uint8_t x : 2;
};
union
{
uint8_t reg1 : 3;
uint8_t opcode : 3;
uint8_t sreg3 : 3;
uint8_t y : 3;
};
union
{
uint8_t reg : 3;
uint8_t reg2 : 3;
uint8_t rm : 3;
uint8_t z : 3;
};
struct
{
uint8_t scale : 2;
uint8_t index : 3;
uint8_t base : 3;
} sib;
union
{
uint8_t s8;
uint16_t s16;
uint32_t s32;
} disp;
uint32_t ea;
} fpu_modrm;
//uint32_t ea;
uint16_t control;
uint16_t status;
uint16_t tag;
uint32_t lastIP;
uint32_t lastIPseg;
uint32_t lastDataPointer;
uint32_t lastDataSeg;
uint16_t opcode;
uint32_t last_instr;
union FpuMmxRegister r[8];
};
Tell me what I am doing wrong thank you
Just so you know they are all wrong in that starting function
src\fpu.c:17:7: error: dereferencing pointer to incomplete type
cpu->instr.fpu.control = 0x37F;
^
src\fpu.c:18:7: error: dereferencing pointer to incomplete type
cpu->instr.fpu.status = 0;
^
src\fpu.c:19:7: error: dereferencing pointer to incomplete type
cpu->instr.fpu.tag = 0xFFFF;
^
src\fpu.c:20:7: error: dereferencing pointer to incomplete type
cpu->instr.fpu.lastDataPointer = 0;
^
src\fpu.c:21:7: error: dereferencing pointer to incomplete type
cpu->instr.fpu.lastDataSeg = 0;
^
src\fpu.c:22:7: error: dereferencing pointer to incomplete type
cpu->instr.fpu.lastIP = 0;
^
src\fpu.c:23:7: error: dereferencing pointer to incomplete type
cpu->instr.fpu.lastIPseg = 0;
^
src\fpu.c:24:7: error: dereferencing pointer to incomplete type
cpu->instr.fpu.opcode = 0;
^
Your definition of struct emu_cpu is not visible at the point of definition of function fpuInit. For this reason all references to struct emu_cpu in fpuInit definition are seen as forward declarations of a new, incomplete type.
If your struct emu_cpu is defined in a header file, make sure it is included into the file that defines fpuInit.
Anonymous unions are only allowed at the end of a structure tree:
In
a->b.c.d =
d can be an anonymous ( without name ) struct or union,
b and c can't, and need to have names.
Reference: **Are "anonymous structs" standard? And, really, what *are* they? **

Casting struct into int

Is there a clean way of casting a struct into an uint64_t or any other int, given that struct in <= to the sizeof int?
The only thing I can think of is only an 'ok' solution - to use unions. However I have never been fond of them.
Let me add a code snippet to clarify:
typedef struct {
uint8_t field: 5;
uint8_t field2: 4;
/* and so on... */
}some_struct_t;
some_struct_t some_struct;
//init struct here
uint32_t register;
Now how do i cast some_struct to capture its bits order in uint32_t register.
Hope that makes it a bit clearer.
I've just hit the same problem, and I solved it with a union like this:
typedef union {
struct {
uint8_t field: 5;
uint8_t field2: 4;
/* and so on... */
} fields;
uint32_t bits;
} some_struct_t;
/* cast from uint32_t x */
some_struct_t mystruct = { .bits = x };
/* cast to uint32_t */
uint32_t x = mystruct.bits;
HTH,
Alex
A non-portable solution:
struct smallst {
int a;
char b;
};
void make_uint64_t(struct smallst *ps, uint64_t *pi) {
memcpy(pi, ps, sizeof(struct smallst));
}
You may face problems if you, for example, pack the struct on a little-endian machine and unpack it on a big-endian machine.
you can use pointers and it will be easy
for example:
struct s {
int a:8;
int b:4;
int c:4;
int d:8;
int e:8; }* st;
st->b = 0x8;
st->c = 1;
int *struct_as_int = st;
hope it helps
You can cast object's pointer to desired type and then resolve it. I assume it can be a little bit slower than using unions or something else. But this does not require additional actions and can be used in place.
Short answer:
*(uint16_t *)&my_struct
Example:
#include <stdio.h>
#include <stdint.h>
typedef struct {
uint8_t field1;
uint8_t field2;
} MyStruct;
int main() {
MyStruct my_struct = {0xFA, 0x7D};
uint16_t num_my_struct = *(uint16_t *)&my_struct;
printf("%X \n", num_my_struct); // 7DFA
return 0;
}

Resources