16.9 – struct padding in C

by subbu on September 24, 2014

Accessing struct members by offset address

In some of the applications like TCP/IP, embedded and networking, we need to access the members of a struct variable by its first byte address and address arithmetic called accessing by offset

#include<stdio.h>
struct num
{
 short a;
 short b;
 float c;
};
int main()
{
 struct num x={45,56,12.45};
 char *p=(char*)&x;
 printf("a=%d",*((short*)p));
 p=p+sizeof(short);
 printf("\nb=%d",*((short*)p));
 p=p+sizeof(short);
 printf("\nc=%f",*((float*)p));
 return 0;
}

Output
x=45
b=56
c=12.450000

Here the address of struct num type of variable “x” is assigned to char type pointer “p”

struct num x={45,56,12.45};
char *p=(char*)&x;

Now by using pointer arithmetic we need to access all the members of struct variable through char type pointer “p”

accessing struct member by offset 1

Now as the pointer refers the first member “a”, it could access the value of “a” (45) using indirection

printf("a=%d",*((short*)p));

By incrementing the value of pointer by short size (2), we can make the pointer “p” to point the next member(b) of struct variable.

p=p+sizeof(short);

accessing struct member by offset 2

Now as the pointer refers the second member “b”, it could access the value of “b” (56) using indirection

printf("b=%d",*((short*)p));

By incrementing the value of pointer by short size(2), we can make the pointer “p” to point the next member(c) of struct variable.

p=p+sizeof(short);

accessing struct member by offset 3

Now as the pointer refers the third member “c”, it could access the value of “c” (12.450000) using indirection

printf("c=%f",*((float*)p));

Problem with the offset accessing

But always it may not be possible to access the members of struct variable by offset address because some times members of struct variable may not be allocated in adjacent memory allocations due to struct padding.

struct padding is aligning the members in the memory according to the addresses of members and their sizes for maximum performance called optimization. Alignment of member is allocating the memory allocation of members so that the addresses of members must be divisible by their sizes

Say for example in the above example the address of x.a must be divisible by 2, x.b must be divisible by 2 and x.c must be divisible by 4.

#include<stdio.h>
struct num
{
 short a;
 short b;
 float c;
};
int main()
{
 struct num x={45,56,12.45};
 printf("%u\n",(unsigned int)&x.a%sizeof(short));
 printf("%u\n",(unsigned int)&x.b%sizeof(short));
 printf("%u\n",(unsigned int)&x.c%sizeof(float));
 return 0;
}

Output
0
0
0

The output of above example proves that, members of struct variable are aligned properly for better performance.
So again it depends on the compiler. Some compilers do perform struct padding and some compilers don’t.
If compiler performs struct padding then the size of sruct variable may not be the sum of sizes of individual members

#include<stdio.h>
struct num
{
 short a;
 char ch;
 double b;
};
int main()
{
 struct num x={25,'A',12.50};
 printf("%d",sizeof(x));
 return 0;
}

Output in TurboC
11

Output in Ubuntu gcc
12

In the above example struct num type of variable “x” has to take 11 bytes. In turbo C taking 11 bytes so struct padding has not taken place and in Ubuntu gcc taking 12 bytes because of struct padding.

Hence we can successfully access every member of struct in Turbo C using offset address but, can’t access in Ubuntu gcc.

#include<stdio.h>
struct num
{
 short a;
 char ch;
 double b;
};
int main()
{
 struct num x={25,'A',12.50};
 char *p=(char*)&x;
 printf("a=%d",*((short*)p));
 p=p+sizeof(short);
 printf("\nch=%c",*((char*)p));
 p=p+sizeof(char);
 printf("\nb=%lf",*((double*)p));
 return 0;
}

Output in Turbo C
a=25
ch=A
b=12.500000

Output in Ubuntu gcc
a=25
ch=A
b=0.000000

As we expected, we could not access every member of struct using offset addressing in Ubuntu gcc.

_ _attribute_ _((packed))

We can instruct the gcc compiler to not perform struct padding using an external attribute instruction with the packed argument

struct num
{
 short a;
 char ch;
 double b;
}__attrubute__((packed));  /* to not pad */
int main()
{
 struct num x={25,'A',12.50};
 char *p=(char*)&x;
 printf("a=%d",*((short*)p));
 p=p+sizeof(short);
 printf("\nch=%c",*((char*)p));
 p=p+sizeof(char);
 printf("\nb=%lf",*((double*)p));
 return 0;
}

Output in Ubuntu gcc
a=25
ch=A
b=12.500000

Summery

Wile accessing struct members using offset address it is always needed to instruct the compiler to not perform struct padding using attribute instruction

Previous post:

Next post: