C Structures Explained: Organize Related Data with Custom Types

Structures are a fundamental feature of C that allow multiple related values of different data types to be grouped into a single object. They are commonly used for records, configuration data, linked lists, and larger application models.

What is a Structure in C?

A structure is a user-defined data type that combines multiple variables, called members, under one name. Each member can have a different data type.

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

Creating a Structure Variable

After defining a structure, you can create variables using the struct type and access their members with the dot operator.

C
struct Student student;

student.id = 101;
student.marks = 85.5f;

printf("ID: %d\n", student.id);
printf("Marks: %.2f\n", student.marks);

Initializing a Structure

Structure variables can be initialized when they are declared. Values are assigned to members in the order they appear in the structure definition.

C
struct Student student = {
    101,
    "Alice",
    92.5f
};

printf("%d\n", student.id);
printf("%s\n", student.name);
printf("%.2f\n", student.marks);

Designated Initializers

C also supports designated initializers, which allow specific members to be initialized by name. This can make initialization clearer.

C
struct Student student = {
    .id = 101,
    .name = "Alice",
    .marks = 92.5f
};

Accessing Structure Members

Use the dot operator to access a member of a structure object.

C
struct Product
{
    int id;
    char name[50];
    double price;
};

struct Product product = {1, "Keyboard", 2500.0};

printf("Product: %s\n", product.name);
printf("Price: %.2f\n", product.price);

Array of Structures

An array of structures can store multiple records that share the same structure definition.

C
struct Student students[3] = {
    {101, "Alice", 90.0f},
    {102, "Bob", 85.5f},
    {103, "Charlie", 88.0f}
};

for (int i = 0; i < 3; i++)
{
    printf("%d - %s - %.1f\n",
           students[i].id,
           students[i].name,
           students[i].marks);
}

Structures and Functions

Structures can be passed to functions by value. The function receives a copy of the structure.

C
struct Point
{
    int x;
    int y;
};

void printPoint(struct Point point)
{
    printf("(%d, %d)\n", point.x, point.y);
}

int main(void)
{
    struct Point point = {10, 20};

    printPoint(point);

    return 0;
}

Passing Structures by Pointer

Passing a pointer to a structure avoids copying the entire structure and allows a function to modify the original object.

C
struct Counter
{
    int value;
};

void increment(struct Counter *counter)
{
    counter->value++;
}

int main(void)
{
    struct Counter counter = {10};

    increment(&counter);

    printf("%d\n", counter.value);

    return 0;
}

The Arrow Operator

When accessing a structure member through a pointer, C provides the arrow operator, written as ->.

C
struct User
{
    int id;
    char name[50];
};

struct User user = {1, "Alice"};
struct User *ptr = &user;

printf("%d\n", ptr->id);
printf("%s\n", ptr->name);

Dot vs Arrow Operator

OperatorUsed WithExample
.Structure objectuser.id
->Pointer to structureptr->id

Nested Structures

A structure can contain another structure as one of its members. This is useful for representing related groups of information.

C
struct Address
{
    char city[50];
    int zipCode;
};

struct Person
{
    char name[50];
    struct Address address;
};

struct Person person = {
    "Alice",
    {"Bengaluru", 560001}
};

printf("%s\n", person.name);
printf("%s\n", person.address.city);

Structures with typedef

The typedef keyword can create a shorter name for a structure type, making declarations easier to read.

C
typedef struct
{
    int id;
    char name[50];
} Student;

Student student = {
    101,
    "Alice"
};

printf("%d - %s\n", student.id, student.name);

Structures and Dynamic Memory

Structures can be dynamically allocated using malloc. This is useful when the number of objects is determined at runtime.

C
#include <stdio.h>
#include <stdlib.h>

struct User
{
    int id;
    char name[50];
};

int main(void)
{
    struct User *user = malloc(sizeof *user);

    if (user == NULL)
    {
        return 1;
    }

    user->id = 101;

    free(user);
    user = NULL;

    return 0;
}

Structure Size with sizeof

The sizeof operator can determine the amount of storage occupied by a structure object. The size can include padding inserted by the implementation for alignment.

C
struct Example
{
    int id;
    double value;
    char flag;
};

printf("Size: %zu bytes\n", sizeof(struct Example));

Structures and Data Records

Structures are especially useful when an application needs to represent records containing multiple related fields.

C
typedef struct
{
    int id;
    char name[50];
    float salary;
} Employee;

Employee employee = {
    .id = 1001,
    .name = "John",
    .salary = 55000.0f
};

printf("Employee: %s\n", employee.name);
printf("Salary: %.2f\n", employee.salary);

Structure vs Array

FeatureStructureArray
Data TypesCan contain different typesNormally contains one element type
PurposeGroup related fieldsStore multiple similar elements
AccessMember namesIndexes
Examplestruct Studentint numbers[10]

Structures and Linked Lists

Structures combined with pointers are the foundation of many dynamic data structures, including linked lists.

C
struct Node
{
    int data;
    struct Node *next;
};

struct Node first;
struct Node second;

first.data = 10;
first.next = &second;

second.data = 20;
second.next = NULL;

printf("%d -> %d\n",
       first.data,
       first.next->data);

Common Mistakes to Avoid

  • Accessing a structure through an invalid pointer
  • Forgetting to allocate memory before using a structure pointer
  • Using -> with a structure object instead of a pointer
  • Using . with a structure pointer instead of ->
  • Returning pointers to local structure variables
  • Ignoring buffer limits when storing strings in structure members
  • Forgetting to free dynamically allocated structures

Structure Best Practices

  • Group fields that logically belong together
  • Use typedef when it improves readability
  • Pass large structures by pointer when appropriate
  • Use const with structure pointers when the function should not modify the object
  • Keep structure responsibilities clear
  • Validate dynamically allocated structure pointers before use

Real-World Applications

  • Student and employee records
  • Configuration objects
  • Network packet representations
  • File metadata
  • Linked lists and trees
  • Embedded systems
  • Operating system data structures

Practice Exercises

  • Create a Student structure
  • Store multiple students in an array
  • Pass a structure to a function
  • Modify a structure through a pointer
  • Create a nested Address structure
  • Allocate a structure dynamically
  • Build a simple linked list using structures

Conclusion

Structures make it possible to organize related data into meaningful custom types. Combined with pointers, arrays, functions, and dynamic memory, they provide the foundation for many important C programming techniques and data structures.

Note: Note: Keep structure designs focused and always ensure that pointers, strings, and dynamically allocated members are accessed within their valid lifetimes and boundaries.