Docs

README

Array of Structures in C

Table of Contents

  1. •Introduction
  2. •What is an Array of Structures?
  3. •Declaring Array of Structures
  4. •Initializing Array of Structures
  5. •Accessing Elements
  6. •Iterating Through Array
  7. •Passing to Functions
  8. •Searching and Sorting
  9. •Dynamic Array of Structures
  10. •Memory Layout
  11. •Common Patterns
  12. •Best Practices
  13. •Summary

Introduction

Arrays of structures combine the power of arrays (storing multiple items) with structures (storing different data types). This creates a powerful way to manage collections of complex data records.

Real-World Analogy

Think of a class roster:

  • •Each student is a structure (name, ID, GPA, etc.)
  • •The roster is an array of student structures
Student Array:
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  Student 0  │  Student 1  │  Student 2  │    ...      │
│ name, ID,   │ name, ID,   │ name, ID,   │             │
│ GPA, etc.   │ GPA, etc.   │ GPA, etc.   │             │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Benefits

BenefitDescription
OrganizationKeep related records together
ScalabilityEasily handle multiple records
UniformityAll elements have same structure
Easy AccessIndex-based access to any record
IterationLoop through all records easily

What is an Array of Structures?

An array of structures is an array where each element is a complete structure. It allows you to store multiple records of the same type.

Conceptual View

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

struct Student class[30];  // Array of 30 students

Visual Representation

struct Student class[5]:

Index:    [0]          [1]          [2]          [3]          [4]
       ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
       │ name     │ name     │ name     │ name     │ name     │
       │ id       │ id       │ id       │ id       │ id       │
       │ gpa      │ gpa      │ gpa      │ gpa      │ gpa      │
       ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
        "Alice"    "Bob"      "Carol"    "Dave"     "Eve"
         101        102        103        104        105
         3.8        3.5        3.9        3.2        3.7

Declaring Array of Structures

Method 1: Separate Definition

// Define structure first
struct Student {
    char name[50];
    int id;
    float gpa;
};

// Then declare array
struct Student class[30];
struct Student graduates[100];

Method 2: Combined Definition

struct Student {
    char name[50];
    int id;
    float gpa;
} class[30];  // Array declared with structure

Method 3: Using typedef

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

Student class[30];  // Cleaner syntax
Student graduates[100];

Size of Array

struct Student class[30];

printf("Size of one student: %zu bytes\n", sizeof(struct Student));
printf("Size of array: %zu bytes\n", sizeof(class));
printf("Number of elements: %zu\n", sizeof(class) / sizeof(class[0]));

Initializing Array of Structures

Method 1: Complete Initialization

struct Student class[3] = {
    {"Alice", 101, 3.8},
    {"Bob", 102, 3.5},
    {"Carol", 103, 3.9}
};

Method 2: Designated Initializers

struct Student class[3] = {
    {.name = "Alice", .id = 101, .gpa = 3.8},
    {.name = "Bob", .id = 102, .gpa = 3.5},
    {.name = "Carol", .id = 103, .gpa = 3.9}
};

Method 3: Partial Initialization

struct Student class[5] = {
    {"Alice", 101, 3.8},
    {"Bob", 102, 3.5}
    // Elements 2-4 are zero-initialized
};

Method 4: Member-by-Member

struct Student class[3];

strcpy(class[0].name, "Alice");
class[0].id = 101;
class[0].gpa = 3.8;

strcpy(class[1].name, "Bob");
class[1].id = 102;
class[1].gpa = 3.5;

Method 5: Loop Initialization

struct Student class[30];

for (int i = 0; i < 30; i++) {
    sprintf(class[i].name, "Student%d", i + 1);
    class[i].id = 100 + i;
    class[i].gpa = 0.0;
}

Zero Initialization

struct Student class[30] = {0};  // All members zeroed

Accessing Elements

Array Index + Dot Operator

struct Student class[3] = {
    {"Alice", 101, 3.8},
    {"Bob", 102, 3.5},
    {"Carol", 103, 3.9}
};

// Access specific element and member
printf("Name: %s\n", class[0].name);
printf("ID: %d\n", class[1].id);
printf("GPA: %.2f\n", class[2].gpa);

// Modify
class[0].gpa = 3.9;
strcpy(class[1].name, "Robert");

Using Pointer

struct Student *ptr = class;  // Points to first element

// Access using pointer
printf("First student: %s\n", ptr->name);
printf("Second student: %s\n", (ptr + 1)->name);

// Same as
printf("First student: %s\n", ptr[0].name);
printf("Second student: %s\n", ptr[1].name);

Relationship Between Array and Pointer

class[i].member    // Array notation
(*(class + i)).member  // Pointer arithmetic
(class + i)->member    // Pointer with arrow

Iterating Through Array

Using for Loop

struct Student class[3] = {
    {"Alice", 101, 3.8},
    {"Bob", 102, 3.5},
    {"Carol", 103, 3.9}
};

int n = sizeof(class) / sizeof(class[0]);

for (int i = 0; i < n; i++) {
    printf("Student %d: %s (ID: %d, GPA: %.2f)\n",
           i + 1, class[i].name, class[i].id, class[i].gpa);
}

Using Pointer

struct Student *ptr;
struct Student *end = class + 3;  // One past last element

for (ptr = class; ptr < end; ptr++) {
    printf("%s: %.2f\n", ptr->name, ptr->gpa);
}

Calculate Statistics

// Find average GPA
float total = 0;
for (int i = 0; i < n; i++) {
    total += class[i].gpa;
}
float average = total / n;
printf("Average GPA: %.2f\n", average);

// Find highest GPA
float max_gpa = class[0].gpa;
int max_index = 0;
for (int i = 1; i < n; i++) {
    if (class[i].gpa > max_gpa) {
        max_gpa = class[i].gpa;
        max_index = i;
    }
}
printf("Highest GPA: %s with %.2f\n", class[max_index].name, max_gpa);

Passing to Functions

Pass Entire Array

void print_students(struct Student arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%s: %.2f\n", arr[i].name, arr[i].gpa);
    }
}

// Or with pointer notation
void print_students_ptr(struct Student *arr, int size) {
    for (int i = 0; i < size; i++) {
        printf("%s: %.2f\n", arr[i].name, arr[i].gpa);
    }
}

// Call
print_students(class, 3);

Pass Single Element (by Value)

void print_student(struct Student s) {
    printf("%s (ID: %d) - GPA: %.2f\n", s.name, s.id, s.gpa);
}

// Call - copies the entire structure
print_student(class[0]);

Pass Single Element (by Pointer)

void update_gpa(struct Student *s, float new_gpa) {
    s->gpa = new_gpa;
}

// Call - passes address
update_gpa(&class[0], 4.0);

Return Array Element

struct Student find_by_id(struct Student arr[], int size, int id) {
    for (int i = 0; i < size; i++) {
        if (arr[i].id == id) {
            return arr[i];  // Returns copy
        }
    }
    // Return empty student if not found
    struct Student empty = {"", 0, 0.0};
    return empty;
}

Return Pointer to Element

struct Student* find_by_id_ptr(struct Student arr[], int size, int id) {
    for (int i = 0; i < size; i++) {
        if (arr[i].id == id) {
            return &arr[i];  // Returns pointer to actual element
        }
    }
    return NULL;  // Not found
}

// Usage
struct Student *found = find_by_id_ptr(class, 3, 102);
if (found != NULL) {
    printf("Found: %s\n", found->name);
}

Searching and Sorting

Linear Search

int find_student(struct Student arr[], int size, const char *name) {
    for (int i = 0; i < size; i++) {
        if (strcmp(arr[i].name, name) == 0) {
            return i;  // Found at index i
        }
    }
    return -1;  // Not found
}

// Usage
int index = find_student(class, 3, "Bob");
if (index >= 0) {
    printf("Found at index %d\n", index);
}

Bubble Sort by GPA

void sort_by_gpa(struct Student arr[], int size) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = 0; j < size - i - 1; j++) {
            if (arr[j].gpa < arr[j + 1].gpa) {  // Descending
                // Swap
                struct Student temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

Sort by Name (Alphabetical)

void sort_by_name(struct Student arr[], int size) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = 0; j < size - i - 1; j++) {
            if (strcmp(arr[j].name, arr[j + 1].name) > 0) {
                struct Student temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

Using qsort (Standard Library)

#include <stdlib.h>

// Comparison function for qsort
int compare_by_gpa(const void *a, const void *b) {
    const struct Student *sa = (const struct Student *)a;
    const struct Student *sb = (const struct Student *)b;

    if (sa->gpa < sb->gpa) return 1;   // Descending
    if (sa->gpa > sb->gpa) return -1;
    return 0;
}

int compare_by_name(const void *a, const void *b) {
    const struct Student *sa = (const struct Student *)a;
    const struct Student *sb = (const struct Student *)b;
    return strcmp(sa->name, sb->name);
}

// Usage
qsort(class, 3, sizeof(struct Student), compare_by_gpa);
qsort(class, 3, sizeof(struct Student), compare_by_name);

Dynamic Array of Structures

Using malloc

int n = 5;
struct Student *students = malloc(n * sizeof(struct Student));

if (students == NULL) {
    printf("Memory allocation failed!\n");
    return 1;
}

// Initialize
for (int i = 0; i < n; i++) {
    sprintf(students[i].name, "Student%d", i + 1);
    students[i].id = 100 + i;
    students[i].gpa = 3.0 + (i * 0.1);
}

// Use
for (int i = 0; i < n; i++) {
    printf("%s: %.2f\n", students[i].name, students[i].gpa);
}

// Free
free(students);

Using calloc (Zero-Initialized)

struct Student *students = calloc(n, sizeof(struct Student));
// All members are initialized to 0

Resizing with realloc

int capacity = 5;
int count = 0;
struct Student *students = malloc(capacity * sizeof(struct Student));

// Function to add student
void add_student(const char *name, int id, float gpa) {
    if (count >= capacity) {
        capacity *= 2;
        students = realloc(students, capacity * sizeof(struct Student));
        if (students == NULL) {
            printf("Reallocation failed!\n");
            exit(1);
        }
    }
    strcpy(students[count].name, name);
    students[count].id = id;
    students[count].gpa = gpa;
    count++;
}

Dynamic Array with Variable Count

struct StudentDatabase {
    struct Student *students;
    int count;
    int capacity;
};

struct StudentDatabase* create_database(int initial_capacity) {
    struct StudentDatabase *db = malloc(sizeof(struct StudentDatabase));
    db->students = malloc(initial_capacity * sizeof(struct Student));
    db->count = 0;
    db->capacity = initial_capacity;
    return db;
}

void add_to_database(struct StudentDatabase *db, struct Student s) {
    if (db->count >= db->capacity) {
        db->capacity *= 2;
        db->students = realloc(db->students,
                               db->capacity * sizeof(struct Student));
    }
    db->students[db->count++] = s;
}

void free_database(struct StudentDatabase *db) {
    free(db->students);
    free(db);
}

Memory Layout

Contiguous Storage

Array elements are stored contiguously in memory:

struct Student class[3]:

Address:  1000                  1060                  1120
         ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
         │     class[0]        │     class[1]        │     class[2]        │
         │ name[50]  id   gpa  │ name[50]  id   gpa  │ name[50]  id   gpa  │
         │   50B    4B   4B +  │   50B    4B   4B +  │   50B    4B   4B +  │
         │   padding           │   padding           │   padding           │
         ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
                60 bytes             60 bytes              60 bytes

Pointer Arithmetic

struct Student *ptr = class;

ptr + 0  →  &class[0]  →  Address 1000
ptr + 1  →  &class[1]  →  Address 1060 (1000 + sizeof(Student))
ptr + 2  →  &class[2]  →  Address 1120 (1000 + 2*sizeof(Student))

Visualizing with Code

#include <stddef.h>

struct Student class[3] = {
    {"Alice", 101, 3.8},
    {"Bob", 102, 3.5},
    {"Carol", 103, 3.9}
};

printf("Size of Student: %zu bytes\n", sizeof(struct Student));
printf("Size of array: %zu bytes\n", sizeof(class));

for (int i = 0; i < 3; i++) {
    printf("Address of class[%d]: %p (offset: %ld)\n",
           i, (void*)&class[i],
           (char*)&class[i] - (char*)&class[0]);
}

Common Patterns

1. Database-Style Operations

#define MAX_STUDENTS 100

struct Student database[MAX_STUDENTS];
int student_count = 0;

// Add
int add_student(const char *name, int id, float gpa) {
    if (student_count >= MAX_STUDENTS) return -1;
    strcpy(database[student_count].name, name);
    database[student_count].id = id;
    database[student_count].gpa = gpa;
    return student_count++;
}

// Find
struct Student* find_student(int id) {
    for (int i = 0; i < student_count; i++) {
        if (database[i].id == id) {
            return &database[i];
        }
    }
    return NULL;
}

// Delete (by swapping with last)
int delete_student(int id) {
    for (int i = 0; i < student_count; i++) {
        if (database[i].id == id) {
            database[i] = database[--student_count];
            return 1;  // Success
        }
    }
    return 0;  // Not found
}

// Update
int update_gpa(int id, float new_gpa) {
    struct Student *s = find_student(id);
    if (s != NULL) {
        s->gpa = new_gpa;
        return 1;
    }
    return 0;
}

2. Reading from User Input

void read_students(struct Student arr[], int *count, int max) {
    printf("How many students? ");
    scanf("%d", count);
    if (*count > max) *count = max;

    for (int i = 0; i < *count; i++) {
        printf("\nStudent %d:\n", i + 1);
        printf("Name: ");
        scanf("%s", arr[i].name);
        printf("ID: ");
        scanf("%d", &arr[i].id);
        printf("GPA: ");
        scanf("%f", &arr[i].gpa);
    }
}

3. Writing to File

void save_students(const char *filename, struct Student arr[], int count) {
    FILE *file = fopen(filename, "wb");
    if (file == NULL) return;

    fwrite(&count, sizeof(int), 1, file);
    fwrite(arr, sizeof(struct Student), count, file);

    fclose(file);
}

int load_students(const char *filename, struct Student arr[], int max) {
    FILE *file = fopen(filename, "rb");
    if (file == NULL) return 0;

    int count;
    fread(&count, sizeof(int), 1, file);
    if (count > max) count = max;

    fread(arr, sizeof(struct Student), count, file);

    fclose(file);
    return count;
}

4. Statistics Functions

struct Stats {
    float min_gpa;
    float max_gpa;
    float avg_gpa;
    int count;
};

struct Stats calculate_stats(struct Student arr[], int count) {
    struct Stats stats = {0};
    if (count == 0) return stats;

    stats.count = count;
    stats.min_gpa = arr[0].gpa;
    stats.max_gpa = arr[0].gpa;

    float total = 0;
    for (int i = 0; i < count; i++) {
        total += arr[i].gpa;
        if (arr[i].gpa < stats.min_gpa) stats.min_gpa = arr[i].gpa;
        if (arr[i].gpa > stats.max_gpa) stats.max_gpa = arr[i].gpa;
    }
    stats.avg_gpa = total / count;

    return stats;
}

Best Practices

1. Use Constants for Array Size

#define MAX_STUDENTS 100

struct Student class[MAX_STUDENTS];
int count = 0;

2. Track Actual Count

struct StudentList {
    struct Student students[MAX_STUDENTS];
    int count;  // Actual number of students
};

3. Validate Index Access

struct Student* get_student(struct Student arr[], int count, int index) {
    if (index < 0 || index >= count) {
        return NULL;  // Invalid index
    }
    return &arr[index];
}

4. Use const for Read-Only Functions

void print_all(const struct Student arr[], int count) {
    // Cannot modify arr elements
    for (int i = 0; i < count; i++) {
        printf("%s\n", arr[i].name);
    }
}

5. Pass Size Along with Array

// Good - size is explicit
void process(struct Student arr[], int size);

// Bad - no way to know size
void process(struct Student arr[]);

6. Use Meaningful Names

// Good
struct Employee employees[100];
struct Product inventory[500];
struct Order order_history[1000];

// Bad
struct Employee e[100];
struct Product p[500];

7. Check Memory Allocation

struct Student *create_class(int size) {
    struct Student *class = malloc(size * sizeof(struct Student));
    if (class == NULL) {
        fprintf(stderr, "Memory allocation failed\n");
        exit(1);
    }
    return class;
}

Summary

Key Concepts

ConceptDescription
Declarationstruct Type arr[SIZE];
Accessarr[i].member
Pointer Access(arr + i)->member or ptr[i].member
Sizesizeof(arr) / sizeof(arr[0])
Dynamicmalloc(n * sizeof(struct Type))

Common Operations

OperationCode
Initialize{{"a", 1}, {"b", 2}}
Iteratefor (i = 0; i < n; i++)
Pass to functionfunc(arr, size)
SearchLinear search with loop
Sortqsort or custom sort
Add elementarr[count++] = element

Memory

  • •Elements stored contiguously
  • •Pointer arithmetic: ptr + i moves by i * sizeof(struct)
  • •Dynamic arrays need manual memory management

Function Patterns

// Read-only access
void print_all(const struct Type arr[], int size);

// Modify elements
void update_all(struct Type arr[], int size);

// Return element
struct Type find_one(struct Type arr[], int size, int key);

// Return pointer
struct Type* find_one_ptr(struct Type arr[], int size, int key);

Next Topics

  • •Pointers to Structures
  • •Passing Structures to Functions
  • •Unions
  • •Bit Fields
README - C Programming Tutorial | DeepML