0

I find a lot of ambiguity while working on structure padding in C.

I need the following :

  1. Trustworthy resource or url to understand structure padding in c.
  2. How memory is allocated to structures, by individual members of by pages (like 1 page=4 bytes for 32 bit machines and 8 bytes for 64 bit machines).
#include <stdio.h>

typedef struct
{
    unsigned short      Flag;
    char                DebounceSamples;
    unsigned short      Device;
    char               *BufferPtr;
    char               *OverridePtr;
} IoHwAb_DIN_DescriptorType;

int main()
{
    IoHwAb_DIN_DescriptorType tt;
    printf("%lu",sizeof(tt));
    return 0;
}

When I execute this I see the output is 24 for the size of struct. I kind of understand why, but also I have a confusion. I need a clear explanation on this.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • 2
    There's lots of posts on this site explaining it. https://stackoverflow.com/questions/4306186/structure-padding-and-packing?rq=1 – Lundin Feb 14 '23 at 09:39
  • 3
    You mention "page" several times in a way that doesn't make sense. Did you mean word? – Tom Karzes Feb 14 '23 at 09:41

1 Answers1

1

Each object type has some alignment requirement. Any object of that type is required to start at an address that is a multiple of the alignment requirement. Each C implementation may determine its own alignment requirements for its object types, but alignment requirements are usually set for hardware efficiency. Most notably, the requirements are set so that any correctly aligned object will not cross a memory boundary that requires an extra load or store operation.

For example, consider hardware that can perform transfers of data between the processor and memory in four-byte chunks starting at addresses that are multiples of four bytes. The hardware can transfer bytes addressed 0-3 in one operation, bytes addressed 4-7 in one operation, bytes address 8-11 in one operation, and so on. However, if we wanted bytes 6-9, that would require two memory transfer operations, one to get byes 4-7 and one to get bytes 8-11. In this hardware, we would set the alignment requirement of a four-byte int to be four bytes, so that all four bytes of the int are always inside one of these aligned groups.

For a two-byte short, we could allow a short object to be in bytes 5-6 in memory, because we can fetch both bytes in one operation (loading bytes 4-7). However, if we have an array of short with the first in bytes 5-6, then the next would be in 7-8, and we cannot fetch that short in one operation; we would have to get bytes 4-7 and 8-11. So, to ensure that short objects always have good alignment, we require them to start at multiples of two bytes.

Once the alignment requirements are set, the algorithm usually used to lay out a structure is:

  • Each member in the structure has some size s and some alignment requirement a.
  • The compiler starts with a size S set to zero and an alignment requirement A set to one (byte).
  • The compiler processes each member in the structure in order:
  1. Consider the member’s alignment requirement a. If S is not currently a multiple of a, then add just enough bytes to S so that it is a multiple of a. This determines where the member will go; it will go at offset S from the beginning of the structure (for the current value of S).
  2. Set A to the least common multiple1 of A and a.
  3. Add s to S, to set aside space for the member.
  • When the above process is done for each member, consider the structure’s alignment requirement A. If S is not currently a multiple of A, then add just enough to S so that it is a multiple of A.

The size of the structure is the value of S when the above is done.

Additionally:

  • If any member is an array, its size is the number of elements multiplied by the size of each element, and its alignment requirement is the alignment requirement of an element.
  • If any member is a structure, its size and alignment requirement are calculated as above.
  • If any member is a union, its size is the size of its largest member plus just enough to make it a multiple of the least common multiple1 of the alignments of all the members.

The rules above follow largely from logic; they put each member where it must be to satisfy alignment requirements without using more space than necessary. The C standard allows implementations to add more padding between elements or at the end of the structure, but this is generally not done. (It could be done for special purposes, such as adding padding during debugging to test whether some bug manifests without padding but not with it, to give clues about the nature of the bug.)

In your structure, char is one byte with alignment requirement one byte, unsigned short is probably two bytes with requirement two bytes, and char * is probably eight bytes with requirement eight bytes. Then the structure layout proceeds:

  • S is zero and A is one byte.
  • No padding is needed, since S is a multiple of the two-byte requirement for unsigned short Flag.
  • unsigned short Flag adds two to S and makes A two bytes.
  • No padding is needed, since S is a multiple of the one-byte requirement for char DebounceSamples.
  • char DebounceSamples adds one to S, making it three, and does not change A, leaving it two bytes.
  • One byte of padding is needed, because S is not a multiple of the two-byte requirement for unsigned short Device, so S is increased to four.
  • unsigned short Device adds two to S, making it six, and does not change A, leaving it two bytes.
  • Two bytes of padding are needed, because S is not a multiple of the eight-byte requirement for char *BufferPtr, so S is increased to eight.
  • char *BufferPtr adds eight to S, making it 16, and changes A to eight bytes.
  • No padding is needed because S is a multiple of the eight-byte requirement for char *OverridePtr.
  • char *OverridePtr adds eight to S, making it 24, and does not change A.

At the end, the size of the structure is 24 bytes, and its alignment requirement is eight bytes.

Footnote

1 I have worded this for a general case as using the least common multiple of alignment requirements. However, since alignment requirements are always powers of two, the least common multiple of any set of alignment requirements is the largest of them.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • *Requirement* is a strong word. You can get away with very weak alignment on x86-64 that will run very poorly on old x86 processors and crash on ARM (I'm sure Peter Cordes has a lot to say about it). So what the compiler chooses is dependent on *compatibility* requirements (should run without penalty on all binary compatibles) and compiler settings (size or speed) but less on actual architecture "requirements". – Peter - Reinstate Monica Feb 14 '23 at 13:10
  • 1
    @Peter-ReinstateMonica: This answer says nothing about architectural requirements; all its references to alignment requirements are within the C implementation. It says the alignment requirements in a C implementation are usually chosen for hardware efficiency. “Alignment requirements” is the term used in the C standard, and it refers to the alignment that is required for the behavior to be defined by the C standard. – Eric Postpischil Feb 14 '23 at 13:17
  • Of course; but these requirements do not lead to the shown structure size in the case of an x86-64 architecture. If you optimize for size the structure can likely be smaller. It's a *choice.* – Peter - Reinstate Monica Feb 14 '23 at 13:21
  • OK, I understand that you say "an implementation chooses to impose certain alignment requirements on the types, partly for efficiency". Valid use of "requirement". The *specific alignment* is a choice though. The *chosen requirements* are to be distinguished from the (hard) *architecture requirements* which may be more lenient. – Peter - Reinstate Monica Feb 14 '23 at 13:26