0

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..

hat
  • 781
  • 2
  • 14
  • 25
  • 1
    of course they are incompatible and the warning is correct. Declare daraArray as table of unions or cast it . – 0___________ Jul 24 '18 at 10:03
  • I can not change the array.. I want a struct or union or.. to easily change the uint16_t value of the array. How would I do a cast? – Wouter Thys Jul 24 '18 at 10:05

2 Answers2

1
  1. 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

0___________
  • 60,014
  • 4
  • 34
  • 74
  • But when I would change the values of the status struct, it would not change the value of the element in the array. – Wouter Thys Jul 24 '18 at 10:17
  • you can cast it as I wrote in the comment but it is not conforming C. BTW always check if your union has the size you think it has. See the ideone example in my answer – 0___________ Jul 24 '18 at 10:20
  • Yes I see the difference now! – Wouter Thys Jul 24 '18 at 10:21
  • Why is it not conforming? It certainly isn't *strictly* conforming, but only a tiny fraction of C programs are. The Standard explicitly says that portability is not a requirement for conformance. – supercat Sep 28 '18 at 16:40
0

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;
destr
  • 24
  • 2
  • It is wrong my fiend. try `status++` and see if if does what you think. – 0___________ Jul 24 '18 at 10:17
  • I assume the pointer would go the next address in the array? However for this particular case, the pointer will never change. It may be better to declare it like this: `static SupplyStatus_t * const status = (SupplyStatus_t*)&(dataArray[6]);` – Wouter Thys Jul 24 '18 at 10:24
  • 1
    Not only is this wrong, even if things were "correct" it would still be a [strict aliasing violation](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule). Making errors and warnings disappear by forcefully casting a value is a **really** bad idea. – Andrew Henle Jul 24 '18 at 10:25
  • 1
    Well, I just tried the code and it works. If you call status.value++ the value in dataArray will be increased. In this situation, the cast is necessary and not a bad idea - how else could you assign a union to a specific type without a cast? – destr Jul 24 '18 at 10:38
  • will be increased but by how many elements of the array (one or two - what do you think) – 0___________ Jul 24 '18 at 11:19
  • what do you mean? status.value++ increases the value of dataArray[6] by one – destr Jul 24 '18 at 11:26
  • by two. but this was not asked. dataArray represents the status and we want a pointer that points to that status. no need to increment the pointer itself - it would only point to the address where the pointer ends. – destr Jul 24 '18 at 13:22