Docs
README
Array of Structures in C
Table of Contents
- ā¢Introduction
- ā¢What is an Array of Structures?
- ā¢Declaring Array of Structures
- ā¢Initializing Array of Structures
- ā¢Accessing Elements
- ā¢Iterating Through Array
- ā¢Passing to Functions
- ā¢Searching and Sorting
- ā¢Dynamic Array of Structures
- ā¢Memory Layout
- ā¢Common Patterns
- ā¢Best Practices
- ā¢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
| Benefit | Description |
|---|---|
| Organization | Keep related records together |
| Scalability | Easily handle multiple records |
| Uniformity | All elements have same structure |
| Easy Access | Index-based access to any record |
| Iteration | Loop 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
| Concept | Description |
|---|---|
| Declaration | struct Type arr[SIZE]; |
| Access | arr[i].member |
| Pointer Access | (arr + i)->member or ptr[i].member |
| Size | sizeof(arr) / sizeof(arr[0]) |
| Dynamic | malloc(n * sizeof(struct Type)) |
Common Operations
| Operation | Code |
|---|---|
| Initialize | {{"a", 1}, {"b", 2}} |
| Iterate | for (i = 0; i < n; i++) |
| Pass to function | func(arr, size) |
| Search | Linear search with loop |
| Sort | qsort or custom sort |
| Add element | arr[count++] = element |
Memory
- ā¢Elements stored contiguously
- ā¢Pointer arithmetic:
ptr + imoves byi * 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