0

Frankly, is such a code valid or does it invoke undefined behavior?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct two_values
{
    int some;
    char value;
};

int main(void) {
    int some = 5;
    char value = 'a';
    unsigned char *data = malloc(sizeof(struct two_values));
    memcpy(data, &some, sizeof(int));
    memcpy(data+sizeof(int), &value, sizeof(char));
    struct two_values dest;
    memcpy(&dest, data, sizeof(struct two_values));
    printf("some = %d, value = %c\n", dest.some, dest.value);
    return 0;
}

http://ideone.com/4JbrP9

Can I just put the binary representation of two struct field together and reinterpret this as the whole struct?

  • 2
    The short answer is: NO. If it's not told otherwise, the compiler uses paddings to get the fields on memory addresses that are multiple of the register size of the destination CPU (this is called "alignment"). – axiac May 02 '17 at 09:10
  • What is the basis of your assumption? – Sourav Ghosh May 02 '17 at 09:14
  • Put `value` in front of `some` in your structure, update the code to use the new order of fields and see for yourself that it doesn't work. – axiac May 02 '17 at 09:16
  • I think that on _Win_ you could use [\[MSDN\]: `#pragma pack 1`](https://msdn.microsoft.com/en-us/library/2e70t5y1(v=vs.140).aspx) or (maybe [\[MSDN\]: `__declspec(align(1))`](https://msdn.microsoft.com/en-us/library/83ythb65.aspx)) to force 1byte alignment, but I'd recommended against. – CristiFati May 02 '17 at 12:07
  • It should be noted that in most cases where you need to calculate struct member offsets, your design is flawed. There is almost always a better way to do things. – user694733 May 03 '17 at 06:14

4 Answers4

1

The padding is determined by the compiler. The order is guaranteed. If you need something similar to your code above, I would recommend the offsetof-macro in <stddef.h>.

memcpy(data + offsetof(struct two_values, value), &value, sizeof(char));

Or without explicitly adding the offset at all:

memcpy(&data->value, &value, sizeof(char));
Mats
  • 8,528
  • 1
  • 29
  • 35
  • Is it necessary to do `memcpy(data+offsetof(struct two_values, some), &some, sizeof(int))`, or is it sufficient to do `memcpy(data, &some, sizeof(int))` if `some` is the first field? –  May 02 '17 at 09:21
  • @mats, What's the `offsetof` macro and where is it defined? thanks :) – Luis Colorado May 05 '17 at 05:19
  • @LuisColorado There are many explanations of it on stack overflow. For example http://stackoverflow.com/a/7180389/909655 – Mats May 05 '17 at 06:22
  • 1
    I don't need the explanation. I'm trying to recommend you to include the `#include ` to make your response complete. Macros need the inclusion of header files to be available in the language. Now it is complete!!! thanks! :) – Luis Colorado May 05 '17 at 06:32
1

You had better to not disturb the internal compiler doings in your code, as it would lead you to incorrect code and undefined behaviour. You can switch compilers, or just updating the version of your favourite, and run into trouble.

The best way to solve the thing you show of having two variables and to store them properly in the struct fields is to use properly the types provided by C, and use a pointer typed to the proper type. If you use

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct two_values
{
    int some;
    char value;
};

int main(void) {
    int some = 5;
    char value = 'a';
    /* next instead of unsigned char *data = malloc(sizeof(struct two_values)); */
    struct two_values *data = malloc(sizeof(struct two_values));
    /* next instead of memcpy(data, &some, sizeof(int)); */
    data->some = some;
    /* next instead of memcpy(data+sizeof(int), &value, sizeof(char)); */
    data->value = value;
    struct two_values dest;
    /* next instead of memcpy(&dest, data, sizeof(struct two_values)); */
    dest = *data;

    printf("some = %d, value = %c\n", dest.some, dest.value);
    return 0;
}

You'll avoid all compiler alignment issues. It is always possible to do it with the language operators & (address of) and * (points to) or -> (field of struct pointed to).

Anyway, if you prefer the memcpy approach (no idea of why, but you are on your way, anyway) you can substitute:

data->some = some;
...
data->value = value;
...
dest = *data;

by

memcpy(&data->some, &some, sizeof data->some);
...
memcpy(&data->value, &value, sizeof data->value);
...
memcpy(&dest, data, sizeof dest);

And that will take internally the alignments that the compiler could make by itself.

All compilers have defined some pragma, or keyword, to control alignment. This is also nonportable, as you can switch compilers and get to the issue of having to change the way you expressed things. C11 has some standard means to control for packed structs and use no alignment in the compiler. This is done mainly when you have to serialize some structure and don't want to deal with holes on it. Look at the C11 specs for that.

Serializing structs is not completely solved by just making them packed, as normally you have to deal with the serialized representations of integer, floating point or char data (which can or cannot coincide with the internal representation used by the compiler) so you again face the problem of being compiler agnostic and have to think twice before using externally the internal representation of data.

My recomendation anyway, is never trust how the compiler stores data internally.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
0

It depend on how your structure is aligned. You can check by verifying sizeof(two_values), if it comes 5(assuming sizeof int is 4), you probably are ok. If its more than that it implies filler bytes are inserted in your structure to align each element of your structure at correct byte boundry

Pras
  • 4,047
  • 10
  • 20
  • it's fairly strange that you get `5` as a result from `sizeof` operator, as this should make array alignment a pain for anything than an array of 5 chars. – Luis Colorado May 03 '17 at 05:43
0

May I assume that struct fields are placed in order

Yes, this is guaranteed by the standard. C11 6.2.5/20:

  • a structure is a type consisting of a sequence of members, whose storage is allocated in an ordered sequence

and with no padding?

No, you cannot assume this. C11 6.7.1/15:

Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. /--/ There may be unnamed padding within a structure object, but not at its beginning.

Padding and alignment are implementation-defined behavior.

You are however guaranteed that two structs of the same type have the same padding. Copying from a struct to another struct of same type, as in your example, is safe and well-defined.

Lundin
  • 195,001
  • 40
  • 254
  • 396