Deep Dive into C Structures: Memory Layout, Nested Structs and Typedef

Table of Contents

What Are Structures in C?

In C, a structure is a user-defined data type that allows you to group variables of different data types under a single name. This capability is crucial when you want to represent real-world entities such as students, employees, books, vehicles or network packets—each of which naturally contains multiple attributes of different types.

Instead of managing many separate variables, structures let you manage related data as a single unit, making programs cleaner, more readable and easier to maintain.

A structure is defined using the struct keyword.

struct Student {
    int id;
    char name[50];
    float marks;
};

This definition creates a template named ‘Student’. No memory is allocated at this stage only the structure blueprint is created.

Declaring and Accessing the Structure

 Decaring a structure means creating a blueprint (or template) for a new user-defined data type. It tells the compiler what variables the structure will contain, but it does not allocate any memory yet. We declare the structure by using the keyword struct.

To access members of a structure variable, the dot (.) operator is used. The dot operator directly accesses individual members, making the code intuitive and readable.

				
					#include<stdio.h>
// Declaring the structure
struct Student {
    char name[50];
    int rollNumber;
    float marks;
};

int main() {
    struct Student s1; 
    
    // Assigning values
    s1.rollNumber = 101;
    s1.marks = 95.5;
    
    // accessig values
    printf("Roll Number: %d\nMarks: %.2f", s1.rollNumber, s1.marks);
    return 0;
}
// Roll Number: 101
// Marks: 95.50


				
			

Initializing Structures

Structures can be initialized in multiple ways.

Initialization at Declaration: It is the shortcut method to assign values to a structure variable at the exact moment you create it. Instead of writing three separate lines of code you pass all values in a single list { }. The values inside the curly braces {…}  must match the exact order of the variables defined in the struct.

 
struct Student {
    int rollNumber;  
    char name[50];  
    float marks;    
};
int main() {
    struct Student s1 = {101, "Reena", 89.5};

In the above code 101 goes to rollNumber, “Reena” goes to name and 89.5 goes to marks

Designated Initialization: It is a feature introduced in C99 that allows you to initialize structure members by name rather than by position. This makes your code safe because you don’t have to remember if id came before name or after.

 
struct Student {
    int id;
    char name[50];
    float marks;
};
int main() {
    // Order doesn't matter here!
    struct Student s1 = {
        .marks = 89.5,
        .id = 101,          
        .name = "Rakshath"  
    };

Array of Structures

Array of structures is used when you need to store multiple records of the same type. Just like an integer array ( int arr[5] ) stores 5 numbers a structure array (struct Student class[5] ) stores 5 separate student records.

The Syntax

 
struct StructName ArrayName[Size]; 

You use the index to find the specific record, and the dot operator to find the specific field.

  • students[0].name (First student’s name)

  • students[2].marks (Third student’s marks)

				
					#include<stdio.h>
struct Student {
    int id;
    char name[20];
    float marks;
};
int main() {
    // Array of 3 Structures
    struct Student class[3] = {
        {1, "Richard", 90.5},
        {2, "Bob", 85.0},
        {3, "Mike", 92.0}
    };

    // Accessing elements
    for (int i = 0; i < 3; i++) {
        printf("ID: %d | Name: %s | Marks: %.2f\n", 
               class[i].id, class[i].name, class[i].marks);
    }

    return 0;
}

// ID: 1 | Name: Richard | Marks: 90.50
// ID: 2 | Name: Bob | Marks: 85.00
// ID: 3 | Name: Mike | Marks: 92.00

				
			

Structures and Functions

Structures become even more powerful when combined with functions.

1. Passing Structure Members

You can pass individual members of a structure to a function just like any other normal variable. The function does not need to know they belong to a structure. It simply receives the specific data value you extracted.
 
#include<stdio.h>
struct Student {
    char name[50];
    int marks;
};
void printMarks(int m) {
    printf("Marks: %d", m);
}
int main() {
    struct Student s1 = {"Alice", 90};
                 // Passing only the 'marks' member
    printMarks(s1.marks); 
    return 0;
}

// Marks: 90

This code demonstrates how to pass a single structure member to a function. Only the integer value (s1.marks) is extracted and passed, behaving exactly like a standard variable. However, this method has a limitation: the function receives only that specific number and has no access to the rest of the structure’s data (such as the name).

s1 acts as the structure variable (or instance). Since marks is encapsulated within the structure, it cannot be accessed directly. You must explicitly reference the container (s1) to retrieve the value inside it.

2. Passing Entire Structures (Call by Value)

In this method we pass the entire structure into the function. This creates a complete copy of the structure inside the function.

The problem with this method is that if the structure is very larege then copying takes time and CPU memory.

 
#include<stdio.h>
struct Student {
    char name[50];
    int id;
};
      // Receives a COPY of the structure
void display(struct Student s) {
    printf("Name: %s, ID: %d", s.name, s.id);
}
int main() {
    struct Student s1 = {"Bob", 101};
    display(s1);
    return 0;
}
     // Name: Bob, ID: 101

This code demonstrates passing a structure by value, where the entire content of the variable s1 is copied into the function parameter s, allowing the function to read the data without affecting the original variable.

3. Passing Structures by Reference (The Efficient Way)

Instead of copying the whole heavy structure, you pass its address (pointer). The function accesses the original structure directly.

				
					#include<stdio.h>
struct Student {
    char name[50];
    int marks;
};
// Receives a POINTER to the structure
void updateMarks(struct Student *s) {
    // Use '->' to access members via pointer
    s->marks = 100; 
}
int main() {
    struct Student s1 = {"Charlie", 80};
    updateMarks(&s1);        // Pass the address
    printf("Updated Marks: %d", s1.marks);
    return 0;
}

// Updated Marks: 100

				
			

This code demonstrates passing by reference, where the function receives the memory address of the structure instead of a copy allowing it to directly modify the original data (updating the marks to 100) using a pointer.

Nested Structures

A Structure containing another structure within it is called a nested structure. This is useful for grouping complex data together, like an “Address” inside an “Employee” record.

The Syntax

 
outerVariable.innerVariable.member

Write a C program to access a structure inside another structure (Nested Structure).

				
					#include<stdio.h>
// Inner Structure
struct Address {
    char city[20];
    int pinCode;
};
// Outer Structure
struct Employee {
    char name[20];
    int salary;
    struct Address addr; // Nested Structure Variable
};
int main() {
    struct Employee e1 = {"John", 50000, {"New York", 10001}};
    printf("Name: %s\n", e1.name);
    // Accessing nested members using double dot (.)
    printf("City: %s\n", e1.addr.city);
    printf("Pin: %d", e1.addr.pinCode);
    return 0;
}

// Name: John
// City: New York
// Pin: 10001

				
			

Structures using typedef

Using typedef with Structures : typedef (Type Definition) allows you to create an alias or “nickname” for a data type. When used with structures, it simplifies your code by removing the need to type the keyword struct every time you declare a variable.

Instead of writing struct Student s1;  you can simply write Student s1;

				
					#include<stdio.h>
// Define the structure with a typedef alias 'Point'
typedef struct {
    int x;
    int y;
} Point;
int main() {
    // No need to write 'struct Point p1'
    Point p1 = {10, 20};

    printf("Coordinates: (%d, %d)", p1.x, p1.y);
    return 0;
}

// Coordinates: (10, 20)
				
			

Applications of Structures

  • Representing Real-World Entities
    Structures group related attributes of real-life objects (like students or employees) into a single logical unit.

  • Database and Record Management Systems
    Structures store and manage multiple records efficiently by representing each record as one structured data type.

  • File Handling Applications
    Structures allow complete records to be written to and read from files in a single operation.

  • Passing Complex Data to Functions
    Structures enable multiple related values to be passed to functions using a single parameter.

  • Implementation of Data Structures
    Structures form the foundation for building dynamic data structures such as linked lists, stacks, queues and trees.

  • Dynamic Memory Management
    Structures combined with dynamic memory allocation support flexible and scalable data storage at runtime.

  • Embedded Systems and Device Drivers
    Structures are used to represent hardware registers, configurations, and sensor data in memory-efficient ways.

  • Network Programming
    Structures define packet formats and protocol headers for organized data transmission over networks.

  • Operating System Development
    Structures manage critical OS components such as processes, memory and file systems.

  • Game Development
    Structures represent game elements like players, enemies, and game states in an organized manner.

 

Frequently Asked Questions (FAQs)

 

1) How to initialize a structure?

A structure can be initialized at declaration by listing values sequentially or by using designated initializers (member names) to prevent ordering errors.

 

2) Can a structure contain another structure?

Yes, a structure can contain another structure and is called a nested structure. It is used in complex group data storage.

 

3) Can structures be passed to functions?

Structures can be supplied to functions either as complete copies or through their memory addresses, with address-based passing being preferred for better performance.

 

4) Why are pointers used with structures?

Pointers are used with structures to enable dynamic memory handling, reduce unnecessary data copying and allow direct modification of structured data inside functions.