1

I have an array

uint8_t b[size];

and i'm currently using

struct data_status ds[size / sizeof(struct data_status)];
memcpy(ds, b, size);

to copy the content of the b array into an array of structures. By doing so i have the problem that i need to allocate 2x the bytes i need. Is it safe to cast the b array assuming ds is "packed" by doing so?

ds = (struct data_status*)&b;

Do i have to pay attention to something in particular?

The code above to cast has not been tested, i'm still in the thinking phase.

Am i really saving space by doing so? I guess no since ds must be declared, the best idea would be to use the following: wherever i use

ds[i].a;

i use

(struct data_status*)&b.a;

EDIT1: size is indeed a multiple of sizeof(struct data_status)

EDIT2: Thank you for the answer but i noticed i haven't fully explain you the purpose of this.

What i want to achieve is to read from a flash memory the content between some addresses, the API forces me to use uint8_t pointers (or array). What is written in the flash memory is what i've written previously using a predefined data structure. The code above has just to retrieve the content of the memory and then i have to interpretate them approprietly (using data_status). Clearly the array "b" can die after it reaches its purpose, the best thing would be if "b" is either removed to begin with or it becomes a data_status directly.

Luigi
  • 376
  • 3
  • 16
  • If `size` is not an exact multiple of `sizeof(struct data_status)` then when you copy bytes, you can overflow the struct array, because its length is a truncation of the division. – Weather Vane Jan 04 '18 at 18:56
  • size is indeed a multiple of sizeof(struct data_status), i have update the question since i figure out something – Luigi Jan 04 '18 at 19:01
  • 1
    `ds = (struct data_status*)&b;` is not safe for various reasons like those answered in [the recent post](https://stackoverflow.com/q/48099028/2410359) – chux - Reinstate Monica Jan 04 '18 at 19:25
  • Instead of `uint8_t b[size];`, use a union of `struct data_status` and `uint8_t b[sizeof (struct data_status)]`. – chux - Reinstate Monica Jan 04 '18 at 19:27
  • Oh that is an amazing suggestion, let me try it. Can you confirm the union is just an interpretation and it is not allocating memory for all the fields? I guess it is the purpose of union – Luigi Jan 04 '18 at 19:32
  • What if i don't know the size at the compilation time? Is it better to have a union of an array of strctures and an array of uint8_t or is it better to have an array of union between an uint8_t array of size of a single structure and a single structure? – Luigi Jan 04 '18 at 19:37
  • @Luigi a `union xyz` is a type. That in itself allocates no memory. – chux - Reinstate Monica Jan 04 '18 at 19:42
  • "if i don't know the size at the compilation time? " If using C99 or many C11, use a VLA (variable length array) or allocate with `*alloc()` Definitely better to have "an uint8_t array of size of a single structure". – chux - Reinstate Monica Jan 04 '18 at 19:43

2 Answers2

3

Avoid the casting. Use a union to store data in "byte" form and struct data_status form.

_Static_assert(size % sizeof(struct data_status) == 0, "bad size");

union {
  struct data_status ds;
  uint8_t b[sizeof (struct data_status)];
} u[size / sizeof(struct data_status)];
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Really no need for a `union`. Casting from any data type *to* a character type does it. – Jens Gustedt Jan 04 '18 at 20:01
  • @JensGustedt OP is not going to a character pointer with `ds = (struct data_status*)&b;`, but from one. Hence the trouble with alignment, anti-aliasing, etc. It would have been even better had OP posted sample code uses too. – chux - Reinstate Monica Jan 04 '18 at 20:04
  • Yes, he is doing it the wrong way around. The question is an XY question, I think. He wants to manipulate data that he wants to see as an array of `struct` and as bytes. So better start from the array of `struct` and look at it as bytes. Please see my answer. – Jens Gustedt Jan 04 '18 at 20:07
  • @JensGustedt The `union` approach does not favor an order in assigning one member and looking at it with another. Either order "works" provided the value is not some trap - which `unsigned char` has none`. – chux - Reinstate Monica Jan 04 '18 at 20:19
  • I don't say that your answer is wrong. It is just a bit too complicated, as there are tools to handle byte manipulations directly. – Jens Gustedt Jan 04 '18 at 20:24
  • 1
    I'd say that a union + static assert just like this is the de facto standard way to handle cases of serialization/deserialization, so this answer is fine. – Lundin Jan 08 '18 at 13:31
2

You are doing it the wrong way around. Casting a character array to a different type is dangerous, because it may e.g be aligned differently than your target type. Therefore this generally not allowed and the behavior of your program is undefined. Bad things can will happen.

The other way around, casting from the other type to a character type is ok, character types are special. So just do:

struct data_status ds[size / sizeof(struct data_status)];
unsigned char* b = (unsigned char*)ds;

and do whatever you think you have to do character by character.

BTW, if uint8_t exists, it must have the same representation as unsigned char.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • The AA concern is that if code changes an element in `ds[]`, then `b[]` must see that change, but with AA, it is not the other way around. A compiler can assume `ds[]` does not changed even if `b[]` changed. A `union` prevents that. – chux - Reinstate Monica Jan 04 '18 at 20:08
  • @chux, no, the aliasing rule does not apply if the object is manipulated on the level of bytes. Otherwise `memcpy` wouldn't work. – Jens Gustedt Jan 04 '18 at 20:10
  • [However this won't work the other way: there's no assumption that your struct aliases a buffer of chars.](https://stackoverflow.com/a/99010/2410359) expresses it well for me. That applies here IMO. – chux - Reinstate Monica Jan 04 '18 at 20:14
  • @chux, did you see that at the end of that answer that there is an exception for character types? These are exempted from the effective type rule. The standard is very carefully formulated such that such byte manipulations and `memcpy` will always work. `memcpy` is nothing else than converting the pointers to `void*` and copying the bytes seen as `unsigned char` one by one. The result is then well visible in the original object. – Jens Gustedt Jan 04 '18 at 20:22
  • Yes there is an exception for character types. `b[]` benefits from that. `ds[]` is not a character type and so does not benefit from the exception. – chux - Reinstate Monica Jan 04 '18 at 20:26
  • 1
    @chux, I really have difficulties to understand you. The "aliasing rule" makes only sense when two different types are involved. If the type of the lvalue through which the access is made is a character type, the aliasing rule doesn't apply. So changing a byte is a legitimate modification of the underlying object. The compiler has to take care that such a modification is seen by the application, because he can't pretend that the access didn't happen. (which he could if the other type where `unsigned int`, e.g) – Jens Gustedt Jan 04 '18 at 20:32
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/162549/discussion-between-chux-and-jens-gustedt). – chux - Reinstate Monica Jan 04 '18 at 20:35
  • @chux the answer you link to is different because it uses malloc'd space, in which writing to it sets its effective type. In this question the objects have a declared type, and if there is a declared type, the effective type is always the declared type – M.M Jan 04 '18 at 20:49