Structure, Structure Padding, Packing, Bit fields in C Programming

There are many built-in data types are available in the C programming language. User also can create their own data types using Structures, Unions, Bit-Fields, typedef, and enum. In this article, we will see the Structure, Structure Pointers, Structure padding, and Bit fields in C Programming.

Structures, Structure Pointers, Structure padding, Bit fields in C Programming

Structures in C Programming

What is a structure in C?

A structure is a collection of one or more variables (possibly of different types) grouped together under a single name for easy to handling. In other words, structures in C programming are the collection of dissimilar data types. We can group the related parameters or variables to the unit. So, the Structures in C help to organize complicated data, particularly in large programs.

An example of a structure is the employee record: an employee is described by a set of attributes such as name, designation, salary, ID, address, sex, etc.

struct employee
{
  char Name[50];
  char designation[50];
  char address[100];
  int Id;
  float Salary;
} info;

The keyword struct introduces a structure declaration. An optional name called a structure tag may follow the word struct (as with employee here). The variables named inside a structure are called members.

How do we access the structure’s members?

We can access the members of the structure in two ways.

  1. . (dot operator)
  2. -> (structure pointer operator)

If we want to assign the values to the members, we assign them as below.

info.Id = 12;
info.Salary = 30000;

What is a structure pointer in C programming?

As you know pointer is the variable that points to the address of another variable, structure pointer is the variable that stores the address of the structure variable.

The structure pointer can be defined as below.

struct emp *ptr;

Initialization

struct emp *ptr = &structure_variable;

Example

#include <stdio.h>   

struct Subject  
{  
    char sub_Name[30];
    int sub_Marks;
};  

int main()  
{  
    struct Subject sub;   // declare the Subject variable  
    struct Subject *ptr;  // create a pointer variable (*ptr)   
    ptr = &sub;           // ptr variable pointing to the address of the structure variable sub
      
    strcpy (sub.sub_Name, "C Programming EmbeTronicX");  
    sub.sub_Marks =  350; //out of 500 
     
  
    // print the details of the Subject (Structure pointer) 
    printf (" Subject Name: %s\n ", ptr->sub_Name);  
    printf (" Subject Marks: %d\n\n ", ptr->sub_Marks);
    
    // print the details of the Subject (Structure) 
    printf (" Subject Name: %s\n ", sub.sub_Name);  
    printf (" Subject Marks: %d\n ", sub.sub_Marks); 
    
    return 0;  
    
}

Output

Subject Name: C Programming EmbeTronicX
Subject Marks: 350

Subject Name: C Programming EmbeTronicX
Subject Marks: 350

An array of structures in C programming

An array is a collection of similar data types and structures are a collection of dissimilar data types. Can we merge this two? Yes, we can. We can create an array of structures.

An array of structures in C can be defined as the collection of multiple structures variables where each variable contains information about different entities. The array of structures in C is used to store information about multiple entities of different data types. The array of structures is also known as the collection of structures.

Example

The below example will help you to understand an array of structures.

#include<stdio.h>  
#include <string.h>

struct student
{    
  int rollno;    
  char name[10];    
}; 

int main()
{    
  int i;    
  struct student st[3];    
  
  printf("Enter Records of 3 students\n");    
  for(i=0;i<3;i++)
  {    
    printf("Enter Rollno: ");    
    scanf("%d",&st[i].rollno);    
    printf("\nEnter Name: ");    
    scanf("%s",&st[i].name);    
  } 
  printf("\n\nStudent Information List:\n");    
  
  for(i=0;i<3;i++)
  {    
    printf("Rollno:%d, Name:%s\n",st[i].rollno,st[i].name);    
  }    
  
  return 0;    
}    

Output

Enter Records of 3 students
Enter Rollno: 10
Enter Name: Arun
Enter Rollno: 11
Enter Name: John
Enter Rollno: 12
Enter Name: Raj

Student Information List:
Rollno:10, Name:Arun
Rollno:11, Name:John
Rollno:12, Name:Raj

Structure padding in C programming

Till now we have seen some basics of the structures in C programming. Now we will go a little bit deeper into the structure.

Assume we are using the typical 32-bit machine, where char is 1byte, short int is 2 bytes, int is 4 bytes, and double is 8 bytes.

Now tell me, what is the size of the below structure?

typedef struct 
{
  int a;  // 4 Bytes
  int b;  // 4 Bytes
} temp;

Yes, totally 8 bytes.

What about the below structures temp1 and temp2?

typedef struct 
{
  char a;   // 1 byte
  int  b;   // 4 bytes
} temp1;

typedef struct 
{
  char a;   // 1 byte
  int  b;   // 4 bytes
  char c;   // 1 byte
} temp2;

The size of the temp1 is 5 bytes and the size of the temp2 is 6 bytes, right? But sadly no. The original answer is 8 bytes and 12 bytes respectively. These extra 3 bytes and 6 bytes are called structure padding.

How come? Okay, we will see the concept. First, you need to know about memory alignment and memory access.

Memory Alignment

Computers commonly address their memory in word-sized chunks. A word is a computer’s natural unit for data. Its size is defined by the computer’s architecture. On a 32-bit machine, the word size will be 4 bytes. So, the processor does not read 1 byte at a time. It reads 1 word (4 bytes) at a time.

If an integer of 4 bytes is allocated on the address that is a multiple of 4 (eg. 0, 4, 8, 12, 16, 20,…. etc), the processor needs only one memory cycle to read the entire integer. Whereas, if the integer is allocated at an address other than a multiple of 4 (eg.1, 5, 7, 10, … etc), it requires two memory read cycles to fetch the data. Confused? Okay, we will explain it in a different way.

The three word-sized memory cells in a 32-bit machine are given below.

0x00000000 0x00000004 0x00000008

If you are adding a 4-byte integer X to the memory with proper alignment (starts from the address which is multiplied by 4), it should be looks like the below.

0x00000000 0x00000004 0x00000008
X X X X

Now, the controller or processor will fetch the data easily with the one memory cycle without doing any special work. Because the int X is aligned properly.

If we decided to put char Z in the memory address before the int X, it will look like the below.

0x00000000 0x00000004 0x00000008
Z X X X X

Now, the memory is not aligned properly. What will happen if the processor or controller needs to fetch the data of int X?

It has to read the first 4 bytes from the memory address 0x00000000 in one memory cycle and then read the memory address 0x00000004 in the second memory cycle. And it has to do some bit shifting also. If the processor or controller does this for multiple misaligned data, then it will take more time to process.

Effectively that means it will take at least two times as long as it would if the data were properly aligned. For this reason, computer scientists came up with the idea of adding padding to the data memory. So, it would be properly aligned.

In our example, adding padding after the first byte, the char would ensure that the last part of the data would be properly aligned in memory. See the below example. (* is padding).

0x00000000 0x00000004 0x00000008
Z * * * X X X X

Now, the processor or controller will directly read the 0x00000004 in one memory cycle and no bit shifting is required.

We will check this with the below example.

Example

#include <stdio.h>

typedef struct 
{
  char a;   // 1 byte
  int  b;   // 4 bytes
} temp1;

typedef struct 
{
  char a;   // 1 byte
  int  b;   // 4 bytes
  char c;   // 1 byte
} temp2;

int main()
{
  printf("sizeof(structure temp) = %lu\n", sizeof(temp1));
  printf("sizeof(structure temp) = %lu\n", sizeof(temp2));
  return 0;
}

Output

sizeof(structure temp1) = 8
sizeof(structure temp2) = 12

What do we need to do if we want to disable the structure padding?

Structure Packing in C programming

The compilers will automatically add correct padding for the target platform unless this feature is deliberately switched off. Sometimes it is required to avoid the structure padding in C. There are two ways to do that.

  1. Using #pragma pack(1) directive
  2. Using attribute

Using pragma

We can use the #pragma pack(1) directive to avoid the structure padding. The below example disables the structure padding and reduces the wastage of memory.

#include <stdio.h>

#pragma pack ( 1 )
typedef struct
{
  char a;   // 1 byte
  int  b;   // 4 bytes
} temp1;

typedef struct 
{
  char a;   // 1 byte
  int  b;   // 4 bytes
  char c;   // 1 byte
} temp2;

int main()
{
  printf("sizeof(structure temp1) = %lu\n", sizeof(temp1));
  printf("sizeof(structure temp2) = %lu\n", sizeof(temp2));
  return 0;
}

Output

sizeof(structure temp1) = 5
sizeof(structure temp2) = 6

Using attribute

We can use the __attribute__((packed)) in the structure declaration. Refer to the below example.

#include <stdio.h>

typedef struct
{
  char a;   // 1 byte
  int  b;   // 4 bytes
}__attribute__((packed)) temp1;

typedef struct 
{
  char a;   // 1 byte
  int  b;   // 4 bytes
  char c;   // 1 byte
}__attribute__((packed)) temp2;

int main()
{
  printf("sizeof(structure temp1) = %lu\n", sizeof(temp1));
  printf("sizeof(structure temp2) = %lu\n", sizeof(temp2));
  return 0;
}

Output

sizeof(structure temp1) = 5
sizeof(structure temp2) = 6

Best practice

If you don’t want to use the structure packing and still want to reduce the memory wastage, then you need to correctly order the structure. We need to declare members in decreasing or increasing order of size.

In the below example, we have given two structures called temp1 (members are not ordered) and temp2 (members are ordered). Check the below program and its output.

#include <stdio.h>

// Members are not ordered
typedef struct
{
  char a;   // 1 byte
  int  b;   // 4 bytes
  char c;   // 1 byte
} temp1;

//Members are ordered
typedef struct 
{
  int  b;   // 4 bytes
  char a;   // 1 byte
  char c;   // 1 byte
} temp2;

int main()
{
  printf("sizeof(structure temp1) = %lu\n", sizeof(temp1));
  printf("sizeof(structure temp2) = %lu\n", sizeof(temp2));
  return 0;
}

Output

sizeof(structure temp1) = 12
sizeof(structure temp2) = 8

We have saved 4 bytes without using the structure packing and just changing the order of the members. But still, the last two bytes are padded and we cannot avoid that. We can use this method also to reduce memory wastage.

Bit Fields in C programming

A bit field is a data structure that allows the programmer to allocate memory to structures and unions in bits in order to utilize computer memory in an efficient manner. With the implementation of bit fields, memory management becomes easy and efficient.

Need of bit field in c programming structure is,

  1. Easy to implement.
  2. Used to reduce memory consumption.
  3. Provides flexibility to the code

Declaration of bit fields

struct
{
  data_type variable_name : size_in_bits;
};

We will take the below example.

#include <stdio.h>

struct Date
{
  unsigned int Day;
  unsigned int Month;
  int Year;
}; 
 
int main()
{
  struct Date c = {01, 05, 2022}; 
  printf("The date is %d / %d / %d\n", c.Day, c.Month, c.Year);
  printf("The size of Date is %ld bytes.\n", sizeof(struct Date));
  return 0; 
}

The output of the program is given below.

The date is 1 / 5 / 2022
The size of Date is 12 bytes.

We know that, for a Date calendar, the range of days should be from 1 to 31, Month 1 to 12.

Day: Since the range is from 1-31, we consider 5 bits as 31 = 32 which is the nearest larger bit than the upper limit.

Month: Since the range is from 1-12, we consider 4 bits as 12 = 16 which is the nearest larger bit than the upper limit.

Year: Since the range is in four-digit like 2022, we consider 12 bits as 2022 = 2048 which is the nearest larger bit than the upper limit. You can increase this if you want to cover more years.

If we use bit fields, we can reduce some memory. Refer to the below example.

#include <stdio.h>

struct Date
{
  unsigned int Day   : 5;
  unsigned int Month : 4;
  unsigned int Year  : 12;
}; 
 
int main()
{
  struct Date c = {01, 05, 2022}; 
  printf("The date is %d / %d / %d\n", c.Day, c.Month, c.Year);
  printf("The size of Date is %ld bytes.\n", sizeof(struct Date));
  return 0; 
}

Output

The date is 1 / 5 / 2022
The size of Date is 4 bytes.

Limitations of bits fields

  • In C programming, bit fields can not be declared as static.
  • An array of bit fields does not exist and hence can not be implemented.

Limitation of structures

  • Any function can access structure members with no data hiding.
  • We can not declare a function inside the structure.
  • Static members won’t be allowed to declare inside the structure.

Advantages of structure

  • Using structure we are able to store data in the same memory location.
  • It is helpful to access multiple data defined in structure from a single variable or pointer or pass all the structure to the function as well.
  • Using an array of structures, they store more records with similar types.

Structure vs Union

  • All members declared in the structure get a unique memory location while in the union a stored memory is shared between members.
  • Any Change in the structure member will not affect other members in the structure whereas any change in member then affect other because they store in the same memory location.
  • Using structure we can store various data types while storing one of the many data types.
  • Structure takes more memory while union takes less.

Can we have a variable size of the structure where we can change the size of the structure? Yes, using flexible array members in a structure we can use the variable size. If you don’t know, please refer to this article.

Hope you have understood the concepts and in our next article, we will discuss the Union in C programming.

You can also read the below tutorials.

Linux Device Driver TutorialsC Programming Tutorials
FreeRTOS TutorialsNuttX RTOS Tutorials
RTX RTOS TutorialsInterrupts Basics
I2C Protocol – Part 1 (Basics)I2C Protocol – Part 2 (Advanced Topics)
STM32 TutorialsLPC2148 (ARM7) Tutorials
PIC16F877A Tutorials8051 Tutorials
Unit Testing in C TutorialsESP32-IDF Tutorials
Raspberry Pi TutorialsEmbedded Interview Topics
Reset Sequence in ARM Cortex-M4BLE Basics
VIC and NVIC in ARMSPI – Serial Peripheral Interface Protocol
Bootloader TutorialsRaspberry PI Pico Tutorials
Zephyr RTOS Tutorials - STM32Zephyr RTOS Tutorials - ESP32
VHDL Tutorials
5 2 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments