Practical C Programming: Structures and Linked Lists
Structure Declaration Methods
There are four common approaches to declaring structure variables in C:
Method 1: Define First, Declare Later
Define the structure type, then declare variables separately:
struct pupil {
long id;
char fullname[20];
char gender;
float grade;
}; // Type definition ends here
struct pupil candidate1, candidate2; // Variable declaration
Method 2: Define and Declare Simultaneously
Combine type definition with variable declaration:
struct pupil {
long id;
char fullname[20];
char gender;
float grade;
} candidate1, candidate2; // Variables declared with type
Method 3: Anonymous Structure with Direct Declaration
Omit the structure tag when variables are declared immediately:
struct { // No structure name
long id;
char fullname[20];
char gender;
float grade;
} candidate1, candidate2; // Direct variable declaration
Note: This approach prevents subsequent declarations of the same type elsewhere in the program, limiting its practical utility.
Method 4: Type Alias with typedef
Create a type alias for cleaner syntax:
typedef struct pupil Scholar; // Create alias
struct pupil {
long id;
char fullname[20];
char gender;
float grade;
};
Scholar candidate1, candidate2; // Use alias for declaration
Programming Exercises
Exercise 1: Comparing Student Records
Task: Read information for two students (ID, name, and integer score) from standard input, store in structures, and output the details of the student with the higher score.
#include <stdio.h>
typedef struct pupil Scholar;
struct pupil {
long id;
char fullname[20];
int marks;
};
Scholar first, second;
int main() {
scanf("%ld %s %d", &first.id, first.fullname, &first.marks);
scanf("%ld %s %d", &second.id, second.fullname, &second.marks);
if (first.marks > second.marks) {
printf("%ld %s %d", first.id, first.fullname, first.marks);
} else {
printf("%ld %s %d", second.id, second.fullname, second.marks);
}
return 0;
}
Exercise 2: Sorting by Academic Performance
Task: Given information for n students (ID, name, score), output the records sorted in descending order by score.
Solution using Standard Library:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int enrollment_id;
char fullname[50];
int marks;
} Learner;
int compare_by_score(const void *first, const void *second) {
const Learner *alpha = (const Learner *)first;
const Learner *beta = (const Learner *)second;
if (alpha->marks < beta->marks) {
return 1; // Descending order
} else if (alpha->marks > beta->marks) {
return -1;
} else {
return 0; // Equal scores maintain relative order
}
}
int main() {
int count;
scanf("%d", &count);
Learner cohort[count];
for (int i = 0; i < count; i++) {
scanf("%d%s%d", &cohort[i].enrollment_id, cohort[i].fullname, &cohort[i].marks);
}
qsort(cohort, count, sizeof(Learner), compare_by_score);
for (int i = 0; i < count; i++) {
printf("%d %s %d\n", cohort[i].enrollment_id, cohort[i].fullname, cohort[i].marks);
}
return 0;
}
Implementing Bubble Sort with Generic Interface:
Standard bubble sort for integers:
void bubble_sort_integers(int *dataset, size_t length) {
for (size_t iteration = 0; iteration < length - 1; iteration++) {
int is_sorted = 1; // Optimistic assumption
for (size_t position = 0; position < length - 1 - iteration; position++) {
if (dataset[position] > dataset[position + 1]) {
int temporary = dataset[position];
dataset[position] = dataset[position + 1];
dataset[position + 1] = temporary;
is_sorted = 0; // Swapping occurred
}
}
if (is_sorted) {
break; // Early termination if already ordered
}
}
}
Generic implementation mimicking qsort signature:
#include <stddef.h>
void byte_swap(char *first, char *second, size_t width) {
for (size_t offset = 0; offset < width; offset++) {
char temporary = *first;
*first = *second;
*second = temporary;
first++;
second++;
}
}
void generic_bubble_sort(void *base, size_t element_count, size_t element_width,
int (*compare)(const void *, const void *)) {
char *array = (char *)base;
for (size_t outer = 0; outer < element_count - 1; outer++) {
int swapped = 0;
for (size_t inner = 0; inner < element_count - 1 - outer; inner++) {
char *current = array + inner * element_width;
char *next = array + (inner + 1) * element_width;
if (compare(current, next) > 0) {
byte_swap(current, next, element_width);
swapped = 1;
}
}
if (!swapped) {
break;
}
}
}
Key Implementation Details:
-
Byte-level Pointer Arithmetic: The
basepointer is cast tochar*because char pointer arithmetic moves in single-byte increments. Addingwidthbytes navigates to the next element, regardless of data type. -
Element Width Parameter: The swap function requires
widthto perform byte-by-byte exchange, enabling generic handling of any data structure. -
Address Calculation: The expression
(char*)base + j * widthcalculates the address of the j-th element by skippingjblocks ofwidthbytes each.
Exercise 3: Election Vote Counting
Task: Three candidates ("Li", "Zhang", "Sun") are running. Each voter casts one vote. Count and display the total votes for each candidate using structures.
#include <stdio.h>
#include <string.h>
typedef struct {
char candidate_name[20];
} Ballot;
int main() {
int voter_count;
scanf("%d", &voter_count);
Ballot votes[voter_count];
int li_votes = 0, zhang_votes = 0, sun_votes = 0;
for (int i = 0; i < voter_count; i++) {
scanf("%s", votes[i].candidate_name);
}
for (int i = 0; i < voter_count; i++) {
if (strcmp(votes[i].candidate_name, "Li") == 0) {
li_votes++;
} else if (strcmp(votes[i].candidate_name, "Zhang") == 0) {
zhang_votes++;
} else if (strcmp(votes[i].candidate_name, "Sun") == 0) {
sun_votes++;
}
}
printf("Li:%d\nZhang:%d\nSun:%d", li_votes, zhang_votes, sun_votes);
return 0;
}
Exercise 4: Student Information Management
Task: Maintain records for up to 50 students (ID, name, three course scores, total). Implement query, update, and delete operations.
#include <stdio.h>
#include <string.h>
int active_count;
struct academic_record {
char student_id[20];
char fullname[20];
int mathematics;
int literature;
int science;
int aggregate;
};
void display_record(struct academic_record entry) {
printf("%s %s %d %d %d %d\n",
entry.student_id, entry.fullname,
entry.mathematics, entry.literature, entry.science, entry.aggregate);
}
void search_by_name(struct academic_record roster[], char *target_name) {
for (int i = 0; i < active_count; i++) {
if (strcmp(roster[i].fullname, target_name) == 0) {
display_record(roster[i]);
}
}
}
void remove_by_id(struct academic_record roster[], char *target_id) {
int found_index = -1;
for (int i = 0; i < active_count; i++) {
if (strcmp(roster[i].student_id, target_id) == 0) {
found_index = i;
break;
}
}
if (found_index != -1) {
for (int i = found_index; i < active_count - 1; i++) {
roster[i] = roster[i + 1];
}
active_count--;
}
}
void modify_scores(struct academic_record roster[], char *target_id,
int math_score, int lit_score, int sci_score) {
int found_index = -1;
for (int i = 0; i < active_count; i++) {
if (strcmp(roster[i].student_id, target_id) == 0) {
found_index = i;
break;
}
}
if (found_index != -1) {
roster[found_index].mathematics = math_score;
roster[found_index].literature = lit_score;
roster[found_index].science = sci_score;
roster[found_index].aggregate = math_score + lit_score + sci_score;
}
}
int main(void) {
int initial_count, operations;
struct academic_record class_roster[50];
scanf("%d%d", &initial_count, &operations);
active_count = initial_count;
for (int i = 0; i < initial_count; i++) {
scanf("%s%s%d%d%d", class_roster[i].student_id, class_roster[i].fullname,
&class_roster[i].mathematics, &class_roster[i].literature, &class_roster[i].science);
class_roster[i].aggregate = class_roster[i].mathematics +
class_roster[i].literature +
class_roster[i].science;
}
while (operations--) {
int operation_type;
scanf("%d", &operation_type);
char temp_id[20], temp_name[20];
if (operation_type == 1) {
scanf("%s", temp_name);
search_by_name(class_roster, temp_name);
} else if (operation_type == 2) {
int new_math, new_lit, new_sci;
scanf("%s%d%d%d", temp_id, &new_math, &new_lit, &new_sci);
modify_scores(class_roster, temp_id, new_math, new_lit, new_sci);
for (int i = 0; i < active_count; i++) {
display_record(class_roster[i]);
}
} else {
scanf("%s", temp_id);
remove_by_id(class_roster, temp_id);
for (int i = 0; i < active_count; i++) {
display_record(class_roster[i]);
}
}
}
return 0;
}
Linked List Fundamentals
Dynamic linked lists are constructed by allocating nodes dynamicalyl and connecting them via pointers. The construction process involves:
- Creating the initial head node
- Iteratively appending new nodes to the tail
- Terminating with a NULL pointer in the final node's link field
Key pointer variables in list construction:
head: Marks the beginning of the listcurrent: Traverses to the end during insertionnew_node: Holds the address of dynamically allocated memory
Creating a Singly Linked List
#include <stdio.h>
#include <stdlib.h>
typedef struct list_node {
int payload;
struct list_node *link;
} ListNode;
ListNode* initialize_list(int initial_value) {
ListNode *head = (ListNode *)malloc(sizeof(ListNode));
if (head == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(1);
}
head->payload = initial_value;
head->link = NULL;
return head;
}
void append_node(ListNode *head, int value) {
ListNode *fresh = (ListNode *)malloc(sizeof(ListNode));
if (fresh == NULL) {
fprintf(stderr, "Memory allocation failed\n");
return;
}
fresh->payload = value;
fresh->link = NULL;
ListNode *current = head;
while (current->link != NULL) {
current = current->link;
}
current->link = fresh;
}
void traverse_and_print(ListNode *head) {
ListNode *current = head;
while (current != NULL) {
printf("%d ", current->payload);
current = current->link;
}
printf("\n");
}
int main(void) {
int node_count, value;
scanf("%d", &node_count);
scanf("%d", &value);
ListNode *list_head = initialize_list(value);
for (int i = 1; i < node_count; i++) {
scanf("%d", &value);
append_node(list_head, value);
}
traverse_and_print(list_head);
return 0;
}
Counting Nodes in a List
size_t count_elements(ListNode *head) {
size_t tally = 0;
ListNode *current = head;
while (current != NULL) {
tally++;
current = current->link;
}
return tally;
}
Reversing a Linked List
Reversing a singly linked list requires three pointers to maintain state during traversal: previous, current, and next.
ListNode* reverse_list(ListNode *head) {
if (head == NULL || head->link == NULL) {
return head;
}
ListNode *previous = NULL;
ListNode *current = head;
ListNode *upcoming = NULL;
while (current != NULL) {
upcoming = current->link; // Store next node
current->link = previous; // Reverse the link
previous = current; // Move previous forward
current = upcoming; // Move current forward
}
return previous; // New head of reversed list
}
Complete Reversal Example:
int main(void) {
int node_count, value;
scanf("%d", &node_count);
scanf("%d", &value);
ListNode *sequence = initialize_list(value);
for (int i = 1; i < node_count; i++) {
scanf("%d", &value);
append_node(sequence, value);
}
printf("Before reversal:\n");
traverse_and_print(sequence);
ListNode *reversed = reverse_list(sequence);
printf("After reversal:\n");
traverse_and_print(reversed);
return 0;
}