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

This article is the continuation of the Series on the C programming tutorial and carries the discussion on C language programming and its implementation. It aims to provide easy and practical examples for understanding the C program. In our last article, we have seen the Strings in C programming. In this article, we will see structures in C programming.

There are many built-in data types 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

Arrays and structures are two fundamental concepts in C programming. An array is a collection of similar data types and structures are a collection of dissimilar data types. Can we merge these two? Yes, we can. We can create an array of structures. Combining these two allows you to create an array of structures, where each element of the array is a structure. This can be useful when you need to store and manipulate multiple related pieces of data.

An array of structures in C can be defined as the collection of multiple structure 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

Using an array of structures can be helpful when you want to work with multiple related data entities simultaneously. You can easily iterate over the array, perform operations on the individual elements, or search for specific data based on certain conditions.

Remember to allocate enough memory for your array based on the number of elements you intend to store, and be careful to avoid accessing elements outside the valid range of the array.

This is a basic overview of using an array of structures in C programming. It’s a powerful technique that can simplify handling multiple pieces of related data.

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, total 8 bytes.

What about the structures below 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.

0x000000000x000000040x00000008
            

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 look like the below.

0x000000000x000000040x00000008
XXXX        

Now, the controller or processor will fetch the data easily with 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 one below.

0x000000000x000000040x00000008
ZXXXX       

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).

0x000000000x000000040x00000008
Z***XXXX    

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 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.

The need for a 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 digits 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.

Nested Structure in C programming

In C programming, a nested structure refers to the concept of having a structure within another structure. This allows you to create complex data structures that contain multiple layers of information.

Here’s an example to illustrate the concept of nested structures:

#include <stdio.h>

// Defining a nested structure
struct Date {
    int day;
    int month;
    int year;
};

struct Student {
    char name[50];
    int rollNo;
    struct Date dob;  // Nested structure as a member
};

int main() {
    // Creating an object of the nested structure
    struct Student student1;
    
    // Assigning values to the nested structure members
    student1.rollNo = 1;
    student1.dob.day = 10;
    student1.dob.month = 5;
    student1.dob.year = 2000;
    
    // Accessing and displaying the nested structure members
    printf("Student Roll No: %d\n", student1.rollNo);
    printf("Date of Birth: %02d-%02d-%04d\n", student1.dob.day, student1.dob.month, student1.dob.year);
    
    return 0;
}

In the above example, we define a nested structure called Date which represents a date with day, month, and year. Then, we define another structure called Student which includes a nested structure Date as one of its members. This allows us to store a student’s name, roll number, and date of birth in a structured manner.

Advantages of Nested Structure

Using a nested structure in the C programming language offers several advantages:

  1. Modularity: Nesting structures allow you to organize related data together, making your code more modular and readable. You can define a structure within another structure to represent complex real-world relationships.
  2. Data Hierarchy: Nested structures enable you to represent hierarchical data structures effectively. For example, you can define a structure to represent a car, and within that structure, define another structure to represent the engine. This allows you to access and manipulate nested data easily.
  3. Code Reusability: By using nested structures, you can reuse existing structures as members of other structures. This helps promote code reuse and avoids duplication, resulting in cleaner and more concise code.
  4. Improved Data Relationships: With nested structures, you can establish relationships between different data elements. This allows you to create complex data structures that accurately represent real-world relationships, making your code more powerful and expressive.
  5. Encapsulation: Using nested structures provides encapsulation, allowing you to hide the implementation details of nested structures from the outside world. This enhances code maintainability, as changes made inside a structure do not impact the rest of the codebase.
  6. Simpler Access and Manipulation: Nested structures simplify the access and manipulation of related data. Since the nested structures are accessed using a hierarchical notation, you can easily navigate through the structure members, accessing the desired data elements.

Overall, nested structures bring organization, modularity, and flexibility to your code, allowing you to represent complex relationships and hierarchies effectively. They improve code readability, reusability, and maintainability, making them a powerful tool in C programming.

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.

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
STM32F7 Bootloader TutorialsRaspberry PI Pico Tutorials
STM32F103 Bootloader TutorialsRT-Thread RTOS Tutorials
Zephyr RTOS Tutorials – STM32Zephyr RTOS Tutorials – ESP32
AUTOSAR TutorialsUDS Protocol Tutorials
Product ReviewsSTM32 MikroC Bootloader Tutorial
VHDL Tutorials
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
Table of Contents