0

I have the following structure:

typdef struct {
    char a[4];
    int b;
    char c;
    int d;
} varT; // Total size is 13 bytes, without padding

which shows the desired memory layout in RAM for variables of type 'varT';

Which means that in RAM, the following layout of data is required:

From address 1000 to 1013 (excluding 1013):

[ field 'a', 4 bytes | field 'b', 4 bytes | field 'c', 1 byte | field 'd', 4 bytes ]

From address 1013 to 1026 (excluding 1026):

[ field 'a', 4 bytes | field 'b', 4 bytes | field 'c', 1 byte | field 'd', 4 bytes ], ...

But the compiler adds padding of 3 bytes between field c to field d.

How would you recommend to bypass that padding in order to conveniently deal with this type of variable, that is stored in RAM?

What I'd like to do is similar to the following:

varT var;
varT *list[5];

var.a[0] = 'A'; var.a[1] = 'B'; var.a[2] = 'C'; var.a[3] = 'D';
var.b = 9876;
var.c = 'X';
var.d = 1234;

memcpy((void*)(list), (void*)(&var), 13);

var.a[0] = 'E'; var.a[1] = 'F'; var.a[2] = 'G'; var.a[3] = 'H';
var.b = 6543;
var.c = 'V';
var.d = 8887;

memcpy((void*)(list + 13), (void*)(&var), 13);

But this is not possible due to the padding that the compiler adds to the struct.

Dor
  • 7,344
  • 4
  • 32
  • 45
  • It depends on your toolchain. See solutions for some common compilers at http://stackoverflow.com/questions/11130109/c-struct-size-alignment or http://stackoverflow.com/questions/4306186/structure-padding-and-structure-packing – Andreas Fester Jun 02 '15 at 08:27
  • 1
    By the way, do you really want to use an array of 5 pointers? Aren't you looking for an array of structures? Please also note that structures can be assigned directly without using memcpy: `v2 = v1` where both are of the `varT` type. – dlask Jun 02 '15 at 08:33
  • Note that `list` contains pointers and you're overwriting these pointers with the contents of a `varT`, which is very unlikely to be valid. And `list + 13` is equivalent to `&list[13]`, which is also invalid. Also, you never need to cast a pointer *into* a `void*`. – molbdnilo Jun 02 '15 at 08:47
  • `memcpy((void*)(list), (void*)(&var), 13);` - I have no idea what you are tring to do here but that line doesn't do it – M.M Jun 02 '15 at 08:53
  • It would be better to serialize your structure into a buffer when you need that buffer, instead of attempting to map your structure directly to memory. You've already discovered one problem with the direct mapping and there are several other problems. – M.M Jun 02 '15 at 08:54
  • This looks suspiciously like a [X-Y problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). _Why_ do you need this "desired memory layout"? Standard C does not let you influence padding, so achieving a given layout is painful and best avoided. If you tell us what you are ultimately trying to do, you'll probably get better answers. – sleske Jun 02 '15 at 09:51
  • Also, if OP _really_ wants a struct size of 13, this is just a special case of [Avoiding Structure Padding in C](http://stackoverflow.com/questions/13468068/avoiding-structure-padding-in-c) or other similar questions. ) – sleske Jun 02 '15 at 09:56

3 Answers3

3

I think one of these could be done.

  • Rearrange the elements order so that there is no padding or manually introduce padding using dummy variables.
  • Use compiler specific features. Like in gcc , __attribute__((packed)), __attribute__((aligned))
  • Serialize the elements and then copy to the array manually with a pre-fixed and well known format.

Some notes. Do not use hard coded structure size, also do not use sizeof to compute the size for this kind of applications where you are copying a structure to an array which may be used to communicate to another machine through a network or written to a file which can be loaded later by another binary. On different systems the same code can generate different padding and therefore loading the stored data from the array as you have shown and also by transferring the array through network like this can go real bad. This is because the two binaries communicating can have different padding amount resulting in a different layout which will break things in a pretty bad way and often very difficult to debug.

On the other hand if you are just copying structure to structure then this may work, but for that the = does the work.

phoxis
  • 60,131
  • 14
  • 81
  • 117
1

The code should be independent on such padding. So use sizeof(varT) instead of 13.

dlask
  • 8,776
  • 1
  • 26
  • 30
  • 1
    Using sizeof instead of hardcoded constants is good, but that does not answer the question - OP states that `the following layout of data is required` (so, sizeof(varT) **must** return 13. I am usually adding an assertion in similar cases to check if this condition is fulfiled) – Andreas Fester Jun 02 '15 at 08:33
  • Yes, but if the structure is not serialized and the array contents is used to send data through the network and the same program binary on another platform happens to have a different padding, then there will be big trouble. – phoxis Jun 02 '15 at 08:33
  • @phoxis Right, but using `sizeof()` on both sides does not solve it - if the padding is different, you could get `sizeof(varT) == 16` on one side and `sizeof(varT) == 14` on the other side, but the sender would still send 16 bytes with a different **layout**. The **size** and the **layout** (the offsets to each element) of the structure must be the same on both sides. – Andreas Fester Jun 02 '15 at 08:40
  • @Andreas. `sizeof()` on both side does not solve the problem, if the layout is different it is a big trouble. Faced it before :D – phoxis Jun 02 '15 at 08:42
  • 1
    @phoxis Thats what I said - probably we misunderstood - you correctly answered it in your answer, which I upvoted earlier :-) – Andreas Fester Jun 02 '15 at 08:43
  • 1
    The original question requires the packed data layout but doesn't specify why. We don't have any information about the "other side". Perhaps it doesn't exist. – dlask Jun 02 '15 at 08:45
0

Why don't you write sane, idiomatic C instead, for a change:

typedef struct {
  char a[4];
  int b;
  char c;
  int d;
} varT;

Can be easily handled like this:

varT vlist[n];
varT x = {.a = {'A', 'B', 'C', 'D'}, .b = 9876, .c = 'X', .d = 1234};
vlist[0] = x;

structs can simply be assigned to. Also, this avoids your errors with indirection.

EOF
  • 6,273
  • 2
  • 26
  • 50