Table of contents
1.
Introduction
2.
C Structure Declaration:
3.
Syntax
4.
C Structure Definition
4.1.
1. Structure Variable Declaration with Structure Template
4.2.
2. Structure Variable Declaration after Structure Template
5.
Access Structure Members
5.1.
Syntax
6.
Initialize Structure Members
6.1.
Using Assignment Operator
6.2.
Using Initializer List
6.3.
Using Designated Initializer List (C99 and later)
7.
Example of Structure in C
8.
typedef for Structures
9.
Nested Structures
10.
Structure Pointer in C
11.
Self-Referential Structures
12.
C Structure Padding and Packing
13.
Bit Fields
14.
Uses of Structure in C
15.
Limitations of C Structures
16.
Frequently Asked Questions
16.1.
Can structures in C have member functions?
16.2.
Do structures in C support inheritance?
16.3.
Can you hide data members in C structures?
17.
Conclusion
Last Updated: Dec 6, 2024
Easy

Types of Structures in C

Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Structures in C are a way to group related data items under one name. They allow you to create user-defined data types that can hold different types of data, like integers, floats, arrays, & other structures. Structures help organize complex data and make code more readable and efficient. 

Types of Structures in C

In this article, we will discuss the different types of structures in C, their declaration, definition, initialization, & use cases. We will also look into topics like structure pointers, nested structures, bit fields, & the limitations of structures in C.

C Structure Declaration:

To use a structure in C, you must first declare it. The structure declaration defines the blueprint or template of the structure. It specifies the name of the structure & the data types of its members. 

The syntax for declaring a structure is :

struct structure_name {
    data_type member1;
    data_type member2;
    ...
};


For example, let's declare a structure called "student" that has three members: name (a string), roll_number (an integer), & gpa (a float).

struct student {
    char name[50];
    int roll_number;
    float gpa;
};


This declaration creates a new data type called "struct student". We can now use this data type to create structure variables that can hold information about a student.

Syntax

The syntax for declaring a structure in C is:

struct structure_name {
    data_type member1;
    data_type member2;
    ...
};


Here, "structure_name" is the name you give to the structure, & "member1", "member2", etc., are the names of the structure members. Each member is defined by specifying its data type, such as int, float, char, or even other structures.

It's important to note that the structure declaration does not create any variables. It only defines the template for the structure. To use the structure, you need to create structure variables.

C Structure Definition

Once you have declared a structure, you can define structure variables using the structure template. There are two ways to define structure variables:

1. Structure Variable Declaration with Structure Template

You can declare a structure variable along with the structure template. This approach is useful when you want to create only one or a few variables of the structure type.

struct student {
    char name[50];
    int roll_number;
    float gpa;
} s1, s2;


In this example, we declare the structure template "student" and create two structure variables, "s1" and "s2", of the "student" type.

2. Structure Variable Declaration after Structure Template

You can also declare structure variables separately, after the structure template has been defined. This approach is helpful when you want to create multiple variables of the same structure type in different parts of your code.

struct student {
    char name[50];
    int roll_number;
    float gpa;
};

struct student s1, s2;


Here, we first define the structure template "student" and then create two structure variables, "s1" and "s2", using the "struct student" type.

Access Structure Members

To access the members of a structure variable, you use the dot (.) operator. The syntax for accessing structure members is :

structure_variable.member_name


For example, let's say we have a structure variable "s1" of the "student" type, and we want to access its members:

s1.name;        // Accesses the "name" member of s1
s1.roll_number; // Accesses the "roll_number" member of s1
s1.gpa;         // Accesses the "gpa" member of s1


You can use these member accesses to assign values to the structure members or to read their values.

// Assigning values to structure members
strcpy(s1.name, "Harsh Singh");
s1.roll_number = 101;
s1.gpa = 3.5;

// Reading values from structure members
printf("Name: %s\n", s1.name);
printf("Roll Number: %d\n", s1.roll_number);
printf("GPA: %.2f\n", s1.gpa);


In this example, we use the "strcpy()" function to assign a string value to the "name" member of "s1", and we directly assign values to the "roll_number" and "gpa" members. Then, we use "printf()" to read and display the values of the structure members.

Syntax

The syntax for accessing structure members is:

structure_variable.member_name


Here, "structure_variable" is the name of the structure variable, and "member_name" is the name of the member you want to access.

For example, if we have a structure variable "s1" of the "student" type, we can access its members like this:

s1.name;        // Accesses the "name" member of s1
s1.roll_number; // Accesses the "roll_number" member of s1
s1.gpa;         // Accesses the "gpa" member of s1


The dot operator can be used to access structure members, both to read their values and to assign new values to them.

Initialize Structure Members

There are several ways to initialize the members of a structure variable:

Using Assignment Operator

You can use the assignment operator (=) to assign values to structure members individually, after the structure variable has been declared.

struct student s1;
strcpy(s1.name, "Harsh Singh");
s1.roll_number = 101;
s1.gpa = 3.5;

Using Initializer List

You can initialize structure members at the time of structure variable declaration using an initializer list. The values in the initializer list should be in the same order as the members in the structure definition.

struct student s1 = {"Harsh Singh", 101, 3.5};

Using Designated Initializer List (C99 and later)

From C99 onwards, you can use designated initializers to initialize structure members by specifying their names along with the values. This allows you to initialize members in any order and skip members if needed.

struct student s1 = {.roll_number = 101, .gpa = 3.5, .name = "Harsh Singh"};

Example of Structure in C

Let's look at a complete example that shows the declaration, definition, and usage of structures in C:

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

struct student {
    char name[50];
    int roll_number;
    float gpa;
};

int main() {
    struct student s1 = {"Harsh Singh", 101, 3.5};
    struct student s2;
    strcpy(s2.name, "Sinki Kumari");
    s2.roll_number = 102;
    s2.gpa = 4.0;
    printf("Student 1:\n");
    printf("Name: %s\n", s1.name);
    printf("Roll Number: %d\n", s1.roll_number);
    printf("GPA: %.2f\n\n", s1.gpa);
    printf("Student 2:\n");
    printf("Name: %s\n", s2.name);
    printf("Roll Number: %d\n", s2.roll_number);
    printf("GPA: %.2f\n", s2.gpa);
    return 0;
}
You can also try this code with Online C Compiler
Run Code


Output

Student 1:
Name: Harsh Singh
Roll Number: 101
GPA: 3.50

Student 2:
Name: Sinki Kumari
Roll Number: 102
GPA: 4.00


In this example, we declare a structure named "student" with three members: "name", "roll_number", and "gpa". We then define two structure variables, "s1" and "s2".

We initialize "s1" using an initializer list and assign values to the members of "s2" using the assignment operator and the "strcpy()" function for the string member.

Finally, we print the values of the structure members for both "s1" and "s2" using "printf()".

typedef for Structures

In C, you can use the typedef keyword to create an alias or a new name for a structure. This can make your code more readable and easier to maintain. The syntax for using typedef with structures is:

typedef struct {
    data_type member1;
    data_type member2;
    ...
} new_structure_name;


Here, "new_structure_name" is the alias you want to give to the structure.

For example, let's create a typedef for the "student" structure:

typedef struct {
    char name[50];
    int roll_number;
    float gpa;
} Student;


Now, you can use "Student" instead of "struct student" to declare variables of the structure type:

Student s1 = {"Harsh Singh", 101, 3.5};
Student s2;


Using typedef makes the code cleaner and more concise, especially when you have to use the structure type multiple times in your program.

Nested Structures

C allows you to nest structures within other structures. This means that you can have a structure member that is itself a structure. Nested structures are useful when you want to group related data items hierarchically.

Let’s look at an example of a nested structure:

struct date {
    int day;
    int month;
    int year;
};


struct student {
    char name[50];
    int roll_number;
    float gpa;
    struct date birthday;
};


In this example, we have two structures: "date" and "student." The "student" structure has a member called "birthday," which is of the "struct date" type. This allows us to store a student's birthday information within the "student" structure.

To access the members of a nested structure, you use the dot operator multiple times:

struct student s1;
s1.birthday.day = 1;
s1.birthday.month = 1;
s1.birthday.year = 2000;
printf("Birthday: %d/%d/%d\n", s1.birthday.day, s1.birthday.month, s1.birthday.year);


Output:

Birthday: 1/1/2000


Nested structures provide a way to organize complex data hierarchically and make the code more modular and readable.

Structure Pointer in C

Just like you can have pointers to basic data types, you can also have pointers to structures. Structure pointers allow you to dynamically allocate memory for structures and work with structures more efficiently.

To declare a pointer to a structure, you use the asterisk (*) symbol along with the structure type:

struct student *ptr;


Here, "ptr" is a pointer variable that can hold the address of a "struct student" variable.

You can allocate memory for a structure using the "malloc()" function and assign the allocated memory address to the structure pointer:

ptr = (struct student*)malloc(sizeof(struct student));


To access the members of a structure using a structure pointer, you use the arrow (->) operator instead of the dot (.) operator:

ptr->name;
ptr->roll_number;
ptr->gpa;

 

Let’s take an example that shows the use of structure pointers:

struct student *ptr;
ptr = (struct student*)malloc(sizeof(struct student));

strcpy(ptr->name, "Harsh Singh");
ptr->roll_number = 101;
ptr->gpa = 3.5;

printf("Name: %s\n", ptr->name);
printf("Roll Number: %d\n", ptr->roll_number);
printf("GPA: %.2f\n", ptr->gpa);

free(ptr);


Output:

Name: Harsh Singh
Roll Number: 101
GPA: 3.50


In this example, we declare a structure pointer "ptr" and allocate memory for a "struct student" using "malloc()". We then assign values to the structure members using the arrow operator. Finally, we print the values of the structure members and free the allocated memory using the "free()" function.

Note: Structure pointers are very useful when you are working with arrays of structures or when passing structures to functions.

Self-Referential Structures

In C, a structure can contain a pointer to itself as a member. This is known as a self-referential structure. Self-referential structures are commonly used to implement linked lists, trees, and other data structures where each element points to another element of the same type.

For example: 

struct node {
    int data;
    struct node *next;
};


In this example, the "node" structure has two members: "data" (an integer) and "next" (a pointer to another "struct node"). The "next" member allows each node to point to the next node in a linked list.

To create a linked list using the self-referential structure, you can do something like this:

struct node *head = NULL;
struct node *new_node;

// Create first node
new_node = (struct node*)malloc(sizeof(struct node));
new_node->data = 1;
new_node->next = NULL;
head = new_node;

// Create second node
new_node = (struct node*)malloc(sizeof(struct node));
new_node->data = 2;
new_node->next = NULL;
head->next = new_node;

// Create third node
new_node = (struct node*)malloc(sizeof(struct node));
new_node->data = 3;
new_node->next = NULL;
head->next->next = new_node;


In this example, we create three nodes and link them together to form a simple linked list. The "head" pointer points to the first node, and each node's "next" pointer points to the next node in the list. The last node's "next" pointer is set to NULL to indicate the end of the list.

Note: Self-referential structures provide a powerful way to create dynamic data structures in C. However, to avoid memory leaks and other issues, it's important to manage memory carefully when working with self-referential structures.

C Structure Padding and Packing

C structures can have unused spaces between members, which is called padding. The compiler adds padding to ensure that each member is aligned properly in memory for efficient access. The amount of padding depends on the size and alignment requirements of the structure members.

For example, consider the following structure:

struct example {
    char a;
    int b;
    char c;
};


In most systems, an int is 4 bytes, and a char is 1 byte. The compiler will add padding to ensure that the int member is aligned on a 4-byte boundary. The resulting memory layout of the structure would be:

| a | (padding) | (padding) | (padding) | b | b | b | b | c | (padding) | (padding) | (padding) |


The structure's total size would be 12 bytes, even though the sum of the members' sizes is only 6 bytes.

To minimize memory wastage due to padding, you can use the #pragma pack directive to control the structure packing. This directive tells the compiler to pack the structure members with a specific alignment.

#pragma pack(1)
struct example {
    char a;
    int b;
    char c;
};
#pragma pack()


By using #pragma pack(1), the compiler will align the members on a 1-byte boundary, effectively removing the padding. The resulting memory layout would be:

| a | b | b | b | b | c |


The structure's total size is now 6 bytes, which is the sum of the members' sizes.

However, it's important to note that packing structures can have performance implications, as accessing unaligned members may be slower on some systems. It's generally recommended to use packing only when necessary, such as when working with external data formats or when memory usage is a critical concern.

Bit Fields

C allows you to specify the number of bits for structure members using bit fields. Bit fields are used to optimize memory usage when you have structure members that require only a small number of bits.

To declare a bit field, you use a colon (:) followed by the number of bits for the member:

struct example {
    unsigned int a : 1;
    unsigned int b : 2;
    unsigned int c : 3;
};


In this example, the structure "example" has three bit field members: "a" with 1 bit, "b" with 2 bits, and "c" with 3 bits. The total size of the structure would be 1 byte (assuming an unsigned int is at least 6 bits).

Bit fields can be used to represent flags, small integers, or other data that requires only a few bits. However, there are a few things to keep in mind when using bit fields:

1. The size of a bit field cannot exceed the size of the underlying data type (usually int or unsigned int).
 

2. The layout of bit fields within a structure is implementation-defined and may vary across compilers and systems.
 

3. Bit fields are not portable across different systems, as the layout and alignment of bit fields may differ.
 

For example: 

struct flags {
    unsigned int is_valid : 1;
    unsigned int is_readonly : 1;
    unsigned int is_hidden : 1;
};

struct flags f;
f.is_valid = 1;
f.is_readonly = 0;
f.is_hidden = 1;

printf("Valid: %d\n", f.is_valid);
printf("Read-only: %d\n", f.is_readonly);
printf("Hidden: %d\n", f.is_hidden);
You can also try this code with Online C Compiler
Run Code


Output:

Valid: 1
Read-only: 0
Hidden: 1


In this example, we define a structure "flags" with three bit field members, each occupying 1 bit. We then set the values of the bit fields and print them.

When it can be used: Bit fields can be useful in situations where memory optimization is crucial, such as in embedded systems or when working with large arrays of structures. However, they should be used carefully, as they can make the code less readable and less portable.

Uses of Structure in C

1. Representing complex data entities: Structures allow you to represent real-world entities that have multiple attributes. For example, you can use a structure to represent a student, an employee, or a book, each with distinct properties.
 

2. Organizing data: Structures help organize data logically and coherently by grouping related data elements. This enhances code modularity, readability, and maintainability.
 

3. Passing multiple arguments to functions: Instead of passing multiple variables to a function, you can pass a single structure containing all necessary data, simplifying function calls and reducing errors.
 

4. Implementing abstract data types (ADTs): Structures are fundamental for creating ADTs like linked lists, stacks, queues, trees, and graphs, promoting reusable and modular code through structure-function combinations.
 

5. Memory optimization: Structures group frequently used data elements together, enhancing memory locality and cache performance. This reduces memory fragmentation and improves overall efficiency.
 

6. Interoperability with other languages: C structures are compatible with similar constructs in languages like C++ and Java, facilitating easy interaction between C code and external libraries or codebases.
 

7. Defining custom data formats: Structures are used to define custom data formats for file I/O, network protocols, or other data exchange needs, allowing structured data reading and writing.

Limitations of C Structures

1. No data hiding: Structures do not support encapsulation. All members are public by default, meaning they can be accessed and modified directly, which limits control over data integrity.
 

2. No member functions: Unlike classes in C++, structures cannot contain member functions. Functions that work on structures must be defined separately and are not inherently tied to the structure.
 

3. No inheritance: Structures lack inheritance capabilities, which means you cannot derive new structures from existing ones, limiting reusability and hierarchy-based designs.
 

4. Limited type checking: C compilers perform limited type checking on structures. Accessing members through incompatible structure pointers can cause undefined behavior, potentially leading to errors.
 

5. No operator overloading: C structures do not support operator overloading, meaning operators like +, -, or [] cannot be redefined to work with structures, requiring functions for operations.
 

6. No constructors or destructors: C structures do not have built-in constructors or destructors to automate initialization and cleanup. Manual handling is required, increasing the risk of errors such as memory leaks.

Frequently Asked Questions

Can structures in C have member functions?

No, structures in C cannot have member functions like classes in C++. However, you can define functions that take structures as arguments or return structures.

Do structures in C support inheritance?

No, C structures do not support inheritance, which is a fundamental concept in object-oriented programming.

Can you hide data members in C structures?

No, C structures do not provide any built-in mechanism for data hiding or encapsulation. All members of a structure are public by default.

Conclusion

In this article, we discussed the various aspects of structures in C. We learned about structure declaration, definition, initialization, and accessing structure members. We also explained topics like typedef, nested structures, structure pointers, self-referential structures, bit fields, padding, and packing. Lastly, we discussed the uses and limitations of structures in C. 

You can also check out our other blogs on Code360.

Live masterclass