2

The following code;

struct s1 {
    void *a;
    char b[2];
    int c;
};

struct s2 {
    void *a;
    char b[2];
    int c;
}__attribute__((packed));

if s1 has a size of 12 bytes and s2 has a size of 10 bytes, is this due to data being read in 4 byte chunks and }__attribute__((packed)); reduces the size of void*a; to only 2 bytes?

A little confused as to what }__attribute__((packed)); does.

Many thanks

Daniel
  • 406
  • 1
  • 4
  • 14
CBreeze
  • 2,925
  • 4
  • 38
  • 93

3 Answers3

5

It is due to alignment, a process in which the compiler adds hidden "junk" between the fields to make sure they have optimal (for performance) starting addresses.

Using packed forces the compiler to not do that, which often means that accessing the structure becomes slower (or simply impossible, causing e.g. a bus error) if the hardware has problems doing e.g. 32-bit accesses on addresses that are not multiples of 4.

unwind
  • 391,730
  • 64
  • 469
  • 606
2

On Intel processors, the fetches of 32-bit aligned data is considerably faster than unaligned; on many other processors unaligned fetches might be illegal altogether, or need to be simulated using 2 instructions. Thus the first structure would have the c always on these 32-bit architectures aligned to a byte address divisible by 4. This however requires that 2 bytes will be wasted in storage.

struct s1 {
    void *a;
    char b[2];
    int c;
};

// Byte layout in memory (32-bit little-endian):
// | a0 | a1 | a2 | a3 | b0 | b1 | NA | NA | c0 | c1 | c2 | c3 |
// addresses increasing ====>

On the other hand, sometimes you absolutely need to map some unaligned datastructures (like file formats, or network packets), as is, into C structures; there you can use the __attribute__((packed)) to specify that you want everything without padding bytes:

struct s2 {
    void *a;
    char b[2];
    int c;
} __attribute__((packed));

// Byte layout in memory (32-bit little-endian):
// | a0 | a1 | a2 | a3 | b0 | b1 | c0 | c1 | c2 | c3 |
// addresses increasing ====>
0

This is due to data structure alignment, a combination of two processes: data alignment and data padding. The first structure will be aligned to the word as you said, however the second structure is packed and forces the compiler to not pad the structure to the word.

The second structure is 10 bytes because the character array is 2 bytes, not the void pointer (it remains 4 bytes, as all pointers are). This can hinder performance as the trade off of 2 bytes of space is not worth the efficiency lost by the hardware (under most circumstances) and could lead to undefined behaviour.

Jacob Pollack
  • 3,703
  • 1
  • 17
  • 39
  • 1
    "it remains 4 bytes, as all pointers are" is fairly sweeping. I presently work with C where pointers are 2-bytes and 4-bytes. Also have compiled with 8-byte pointers. – chux - Reinstate Monica Aug 13 '13 at 12:15
  • @chux, can you show an example of that? I am actually curious why this can happen. – Jacob Pollack Aug 13 '13 at 16:18
  • http://www.microchip.com/pagehandler/en-us/family/16bit/ Old DOS PCs: various modes had 16/32 data pointers and independently 16/32 function pointers. Intel 64 bit http://www.intel.com/content/www/us/en/architecture-and-technology/microarchitecture/intel-64-architecture-general.html – chux - Reinstate Monica Aug 13 '13 at 17:29