8

I am using __attribute__((packed)) to avoid struct padding. The below code is working fine, but when I add one more int member inside the struct the compiler pads my struct.

#include <stdio.h>

struct test {

    int x;
    char c1;
    char c2;
    char c3;
    char c4;
    char c5;
    // int d; Pads if I uncomment

} __attribute__((packed)) obj = {50,'X','Y','Z','A','B'}; 

int main ()
{
    struct test *ptr= &obj;
    char *ind = (char *) &obj;

    printf("\nLet's see what is the address of obj %d", ptr);
    printf("\n Size of obj is : %d bytes ", sizeof(obj));
    printf("\nAddress of x is %d", &ptr->x);
    printf("\nAddress of c1 is %d", &ptr->c1);
    printf("\nAddress of c2 is %d", &ptr->c2);
    printf("\nValue  of x is %d", ptr->x);
    printf("\nAddress of x is %c", ptr->c1);
    printf("\nFetching value of c4 through offset %c", *(ind+7));

}

The above code is working as expected and the size of obj is 9 bytes (with padding it was 12 bytes).

However, when I uncomment int d in my struct the code outputs:

Size of obj is : 16 bytes

instead of the expected 13 (9 + 4) bytes.

What's wrong?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
theartist33
  • 470
  • 1
  • 6
  • 13
  • What CPU are you compiling for? – Drew Dormann Aug 14 '15 at 16:09
  • 1
    Have you tried enclosing the struct between `#pragma pack(push, 1)` and `#pragma pack(pop)` which I believe is part of gcc (instead of using attribute)? With MSVC, the sizes 9 and 13 are obtained. – Weather Vane Aug 14 '15 at 16:14
  • I'm mostly just watching this question to see how it develops, but this may be relevant: http://www.bttr-software.de/forum/mix_entry.php?id=11767 . Looks like GCC's idea of packing may in some ways be a lie. I didn't read the whole thread but maybe there are some compiler switches that will make this go away, depending on your exact compiler (incl. version), CPU, and OS. I also suspect that maybe the compiler still has a right to pad at the end, but not in the middle. What happens if you put 'd' after 'c1'? Would the padding still be at the end of the struct or between c1 and d? – Dmitri Aug 14 '15 at 16:17
  • 6
    If you really want this answered properly, please post your compiler, compiler version, OS, CPU (architecture the code is being built for), and any extra compiler options/switches you are using. This kind of stuff is highly system dependent. I have seen similar issues before but they were all on embedded systems, not sure how it works on win/x64 and similar. – Dmitri Aug 14 '15 at 16:24
  • 1
    20 minutes later, my crystal ball is still telling me that the CPU is one of the many for which unaligned access does not exist. :) – Drew Dormann Aug 14 '15 at 16:31
  • @DrewDormann I thought in that case, the compiler will emit a series of bitshifts to compensate. – David Zech Aug 14 '15 at 16:32
  • 2
    @Nighthawk441: [The documentation](https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#Common-Type-Attributes) says only that the memory consumed will be minimized; I don't think this amounts to a guarantee that kamikaze steps will be taken. – Lightness Races in Orbit Aug 14 '15 at 16:38
  • @Nighthawk441 I can't speak for every variant of gcc, but the one I last used on a restrictive CPU did not do that. – Drew Dormann Aug 14 '15 at 16:40
  • @LightnessRacesinOrbit which is why I suspect that the total size of the struct and the actual packing and alignment of its members could be two different issues. Maybe the size of the struct must be a multiple of 4 bytes, but the compiler will still take steps to prevent padding in the middle of it. IMO, those would be drastic steps to take, but not completely kamikaze, so not unreasonable. *sigh* where are the people who actually read that part of the manual? I'm disappointed with SO on this one. – Dmitri Aug 14 '15 at 16:48
  • 3
    @Dmitri Hard to read the manual when you don't know which one to read. – David Zech Aug 14 '15 at 16:49
  • True. My default assumption about people who don't give that info is "whatever comes with latest Eclipse CDT on wintel64". But that's just a guess. – Dmitri Aug 14 '15 at 16:58
  • 2
    See [https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991). – 4566976 Aug 14 '15 at 16:59
  • 1
    @Dmitri : OS -- windows 7 . x64 based . gcc - gcc version 5.1.0 (i686-posix- mingw-w64 – theartist33 Aug 14 '15 at 17:16
  • @Dmitri : Compiler in this case padding in middle instead at end , I had the same doubt and verified – theartist33 Aug 14 '15 at 17:19
  • @Dmitri : Also, I may agree to the fact that size of struct must be multiple of 4 bytes with padding . But after using attribute to avoid padding sometimes size is multiple of 4 sometimes not . ** As if we add one more char c6 , size is 17 bytes ( and after checking address of c6 it appears padding is happening in between c5 and intd ** ) – theartist33 Aug 14 '15 at 17:24
  • @Dmitri: CPUs don't care what size a struct has. They are not aware of structs. They care about access to integers and floats. – Lightness Races in Orbit Aug 14 '15 at 17:25
  • 1
    @theartist33 I compiled your code on a 64bit Linux machine using gcc 4.8.2. It gives me 13 bytes as you expected. – MiJo Aug 14 '15 at 19:06
  • 1
    @MiJo: Use `-mms-bitfields` option. – 4566976 Aug 14 '15 at 19:12
  • 1
    @4566976 Yeah. that results in 16 bytes. – MiJo Aug 14 '15 at 19:16
  • @LightnessRacesinOrbit I know the CPU doesn't care about structs, the reasoning was that a compiler implementation trying to emulate unaligned access might. But now we know it dosn't work as I thought. – Dmitri Aug 14 '15 at 19:21
  • @theartist33 did you try the link from @4565976? I think what they're saying there is -mms-bitfields is actually the default, but per daniel.c.klauer on that thread "In contrast, compiling with *-mno-ms-bitfields* makes the example programs work as expected under gcc 4.7.0." Not sure if all that applies to mingw64 since the bug report is for mingw32, but probably worth a try. – Dmitri Aug 14 '15 at 19:29

2 Answers2

1

In structures there are two types of padding involved. (1) Padding added to make the structure a multiple of a certain number (In your case the size of int) and (2) Padding added to position certain data types at an address divisible by certain number. For example, 4 bytes for int. So in your case while compiler is happy to remove the first type of padding I think it is still forcing the member int d to an address divisible by 4. Since there are 5 chars before d, 3 bytes padding is added to force d to an address divisible by 4.

So try moving the member int d above the chars. Then of course you have to change the offset used in fetching value of c4 through offset. You can even put it right above c5. Then you wouldn't have to change your fetching value of c4 through offset line. Example:

struct test {

    int x;
    int d;
    char c1;
    char c2;
    char c3;
    char c4;
    char c5;

} __attribute__((packed)) obj = {50,'X','Y','Z','A','B'}; 

OR

struct test {

    int x;
    char c1;
    char c2;
    char c3;
    char c4;
    int d;
    char c5;

} __attribute__((packed)) obj = {50,'X','Y','Z','A','B'}; 
MiJo
  • 569
  • 5
  • 19
0

Thanks to all who took time for getting into peculiarities .I would like to tell the behavior of compiler which I am using after doing different checks and would like to stick to the fact that in both scenario i.e with and without padding compiler wants integer member to fall on the address which is divisible by it's size, in this case 4 bytes,(pads in between) which although does not define 'use of attribute' functioning however still serve good purpose .As we know and I assume compiler rightfully more emphasize on speed instead of space and thus want to save time in performing instruction and saving machine cycle . Strictly Following above protocol, if compiler presumes that next member which it encounters is integer then it is best condition because then compiler won't have to worry about padding or no padding because next integer is definitely going to fall on address divisible by 4 (bytes) . It is even better if double is size of 4 bytes as in linux 32 bit arch . One thing I can assure is, in case of no padding (making use of attribute) compiler is avoiding pads at the end but in case of padding compiler pads bytes in between as well as at the end to make whole structure size divisble by 4.(because struct size which is divisble by 8 (double size on some machine) is divisible by 4 as well ).As question and functioning is totally compiler based so it wasn't fair to everyone that's why even more thanks.

theartist33
  • 470
  • 1
  • 6
  • 13