Show description
CSE 240 Module 4: Advanced C Programming
CSE 240 Module 4: Advanced C Programming
CSE 240 Module 4
Advanced C Programming: Memory, Files, and Data Structures
Module 4 Topics
Structure Padding
File I/O
Buffer Management
Parameter Passing
Linked Lists
Glossary
Structure Padding & Memory Layout
The compiler adds padding bytes between structure members to ensure proper memory alignment for efficient access. Understanding this is crucial for memory optimization and avoiding bugs.
Memory Alignment Rules
Most systems require data to be aligned on boundaries equal to their size:
char: 1-byte alignment (no padding needed)
short: 2-byte alignment
int/float: 4-byte alignment
double/long long: 8-byte alignment
Example: Poor vs Good Structure Design
// Poor design - lots of padding
struct bad_example {
char a; // 1 byte
// 3 bytes padding
int b; // 4 bytes
char c; // 1 byte
// 7 bytes padding
double d; // 8 bytes
}; // Total: 24 bytes
// Better design - minimized padding
struct good_example {
double d; // 8 bytes
int b; // 4 bytes
char a; // 1 byte
char c; // 1 byte
// 2 bytes padding
}; // Total: 16 bytes
Memory Layout Visualization:
Bad Structure (24 bytes):
a___bbbbc_______dddddddd
Good Structure (16 bytes):
ddddddddbbbbac__
Tip: Use `sizeof()` to check actual structure sizes and `offsetof()` from stddef.h to see member positions.
Packed Structures
// Force no padding (compiler-specific)
#pragma pack(1)
struct packed_struct {
char a;
int b;
char c;
}; // Exactly 6 bytes, but slower access
#pragma pack()
// Or use __attribute__((packed)) with GCC
struct packed_struct2 {
char a;
int b;
char c;
} __attribute__((packed));
File I/O Operations
C provides both high-level (stdio.h) and low-level file operations.…
CSE 240 Module 4: Advanced C Programming
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSE 240 Module 4: Advanced C Programming</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
background: linear-gradient(135deg, #0a0e1a 0%, #1a1f3a 50%, #0f1419 100%);
color: #e2e8f0;
line-height: 1.6;
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
h1 {
text-align: center;
color: #60a5fa;
font-size: 2.5em;
margin-bottom: 10px;
text-shadow: 0 0 20px rgba(96, 165, 250, 0.5);
}
.subtitle {
text-align: center;
color: #94a3b8;
margin-bottom: 40px;
font-size: 1.2em;
}
.nav {
background: rgba(30, 41, 59, 0.8);
border: 1px solid #334155;
border-radius: 10px;
padding: 20px;
margin-bottom: 30px;
backdrop-filter: blur(10px);
}
.nav h2 {
color: #3b82f6;
margin-bottom: 15px;
}
.nav-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
}
.nav-item {
background: rgba(59, 130, 246, 0.1);
border: 1px solid #1e40af;
border-radius: 5px;
padding: 10px;
cursor: pointer;
transition: all 0.3s ease;
}
.nav-item:hover {
background: rgba(59, 130, 246, 0.2);
transform: translateY(-2px);
}
.section {
background: rgba(15, 23, 42, 0.9);
border: 1px solid #1e293b;
border-radius: 15px;
padding: 30px;
margin-bottom: 30px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
h2 {
color: #60a5fa;
font-size: 1.8em;
margin-bottom: 20px;
border-bottom: 2px solid #1e40af;
padding-bottom: 10px;
}
h3 {
color: #3b82f6;
margin: 25px 0 15px 0;
font-size: 1.3em;
}
h4 {
color: #93c5fd;
margin: 20px 0 10px 0;
font-size: 1.1em;
}
.code-block {
background: #000000;
border: 1px solid #333333;
border-radius: 6px;
padding: 20px;
margin: 15px 0;
overflow-x: auto;
position: relative;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.7);
}
.code-block::before {
content: 'C';
position: absolute;
top: 8px;
right: 15px;
color: #888888;
font-size: 0.75em;
background: #1a1a1a;
padding: 2px 6px;
border-radius: 3px;
border: 1px solid #333333;
}
.code-block pre {
margin: 0;
background: none;
border: none;
padding: 0;
color: #ffffff;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
font-size: 14px;
line-height: 1.5;
white-space: pre;
overflow-x: auto;
}
code {
color: #ffffff;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
line-height: 1.5;
white-space: pre;
display: block;
font-size: 14px;
background: none;
}
.code-block code {
background: none;
padding: 0;
border: none;
border-radius: 0;
}
/* Better syntax highlighting for dark theme */
.keyword { color: #569cd6; font-weight: bold; }
.string { color: #ce9178; }
.comment { color: #6a9955; font-style: italic; }
.number { color: #b5cea8; }
.type { color: #4ec9b0; }
.function { color: #dcdcaa; }
.preprocessor { color: #c586c0; }
.highlight {
background: rgba(59, 130, 246, 0.2);
padding: 2px 4px;
border-radius: 3px;
}
.warning {
background: rgba(239, 68, 68, 0.1);
border: 1px solid #dc2626;
border-radius: 8px;
padding: 15px;
margin: 15px 0;
}
.warning::before {
content: "⚠️ ";
color: #f87171;
}
.tip {
background: rgba(34, 197, 94, 0.1);
border: 1px solid #16a34a;
border-radius: 8px;
padding: 15px;
margin: 15px 0;
}
.tip::before {
content: "💡 ";
color: #4ade80;
}
.memory-diagram {
background: #1e293b;
border: 1px solid #475569;
border-radius: 8px;
padding: 20px;
margin: 15px 0;
font-family: monospace;
overflow-x: auto;
}
.address {
color: #fbbf24;
}
.data {
color: #34d399;
}
.pointer {
color: #f472b6;
}
.glossary {
background: rgba(30, 41, 59, 0.5);
border-radius: 10px;
padding: 20px;
margin: 20px 0;
}
.glossary-term {
color: #60a5fa;
font-weight: bold;
margin-top: 15px;
}
.glossary-def {
color: #cbd5e1;
margin-left: 20px;
margin-bottom: 10px;
}
.interactive {
background: rgba(59, 130, 246, 0.1);
border: 2px dashed #3b82f6;
border-radius: 10px;
padding: 20px;
margin: 20px 0;
text-align: center;
}
.button {
background: linear-gradient(45deg, #1e40af, #3b82f6);
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
font-size: 1em;
margin: 5px;
transition: transform 0.2s;
}
.button:hover {
transform: scale(1.05);
}
.visualization {
background: #0f172a;
border: 1px solid #1e40af;
border-radius: 10px;
padding: 20px;
margin: 20px 0;
text-align: center;
}
.node {
display: inline-block;
background: linear-gradient(45deg, #1e40af, #3b82f6);
color: white;
padding: 10px 15px;
border-radius: 8px;
margin: 5px;
position: relative;
}
.arrow {
color: #60a5fa;
font-size: 1.5em;
margin: 0 10px;
}
.footer {
text-align: center;
color: #64748b;
margin-top: 50px;
padding: 20px;
border-top: 1px solid #334155;
}
</style>
</head>
<body>
<div class="container">
<h1>CSE 240 Module 4</h1>
<div class="subtitle">Advanced C Programming: Memory, Files, and Data Structures</div>
<div class="nav">
<h2>Module 4 Topics</h2>
<div class="nav-grid">
<div class="nav-item" onclick="scrollToSection('padding')">Structure Padding</div>
<div class="nav-item" onclick="scrollToSection('fileio')">File I/O</div>
<div class="nav-item" onclick="scrollToSection('buffers')">Buffer Management</div>
<div class="nav-item" onclick="scrollToSection('params')">Parameter Passing</div>
<div class="nav-item" onclick="scrollToSection('linkedlists')">Linked Lists</div>
<div class="nav-item" onclick="scrollToSection('glossary')">Glossary</div>
</div>
</div>
<div class="section" id="padding">
<h2>Structure Padding & Memory Layout</h2>
<p>The compiler adds padding bytes between structure members to ensure proper memory alignment for efficient access. Understanding this is crucial for memory optimization and avoiding bugs.</p>
<h3>Memory Alignment Rules</h3>
<p>Most systems require data to be aligned on boundaries equal to their size:</p>
<ul>
<li><strong>char</strong>: 1-byte alignment (no padding needed)</li>
<li><strong>short</strong>: 2-byte alignment</li>
<li><strong>int/float</strong>: 4-byte alignment</li>
<li><strong>double/long long</strong>: 8-byte alignment</li>
</ul>
<h4>Example: Poor vs Good Structure Design</h4>
<div class="code-block">
<code>// Poor design - lots of padding
struct bad_example {
char a; // 1 byte
// 3 bytes padding
int b; // 4 bytes
char c; // 1 byte
// 7 bytes padding
double d; // 8 bytes
}; // Total: 24 bytes
// Better design - minimized padding
struct good_example {
double d; // 8 bytes
int b; // 4 bytes
char a; // 1 byte
char c; // 1 byte
// 2 bytes padding
}; // Total: 16 bytes</code>
</div>
<div class="memory-diagram">
Memory Layout Visualization:
<br><br>
Bad Structure (24 bytes):
<span class="data">a</span><span class="address">___</span><span class="data">bbbb</span><span class="data">c</span><span class="address">_______</span><span class="data">dddddddd</span>
<br><br>
Good Structure (16 bytes):
<span class="data">dddddddd</span><span class="data">bbbb</span><span class="data">ac</span><span class="address">__</span>
</div>
<div class="tip">
Tip: Use `sizeof()` to check actual structure sizes and `offsetof()` from stddef.h to see member positions.
</div>
<h4>Packed Structures</h4>
<div class="code-block">
<code>// Force no padding (compiler-specific)
#pragma pack(1)
struct packed_struct {
char a;
int b;
char c;
}; // Exactly 6 bytes, but slower access
#pragma pack()
// Or use __attribute__((packed)) with GCC
struct packed_struct2 {
char a;
int b;
char c;
} __attribute__((packed));</code>
</div>
</div>
<div class="section" id="fileio">
<h2>File I/O Operations</h2>
<p>C provides both high-level (stdio.h) and low-level file operations. Understanding both gives you full control over file handling.</p>
<h3>Standard File Operations</h3>
<div class="code-block">
<code>#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *file;
char buffer[256];
// Opening files
file = fopen("data.txt", "r"); // read
if (file == NULL) {
perror("Error opening file");
return 1;
}
// Reading operations
fgets(buffer, sizeof(buffer), file); // read line
rewind(file); // go back to start
char ch;
while ((ch = fgetc(file)) != EOF) { // read char by char
putchar(ch);
}
fclose(file);
// Writing operations
file = fopen("output.txt", "w");
fprintf(file, "Number: %d\n", 42);
fputs("Hello world\n", file);
fputc('!', file);
fclose(file);
return 0;
}</code>
</div>
<h3>File Modes</h3>
<div class="memory-diagram">
File Opening Modes:
<br>
"r" - Read only (file must exist)
"w" - Write only (truncates or creates)
"a" - Append (writes at end)
"r+" - Read/write (file must exist)
"w+" - Read/write (truncates or creates)
"a+" - Read/append
"rb" - Binary read
"wb" - Binary write
</div>
<h3>Working with Struct Arrays and Files</h3>
<div class="code-block">
<code>#include <stdio.h>
#include <stdlib.h>
typedef struct {
int id;
char name[50];
double gpa;
} Student;
int write_students(const char *filename, Student students[], int count) {
FILE *file = fopen(filename, "wb");
if (!file) {
perror("Cannot open file for writing");
return 0;
}
size_t written = fwrite(students, sizeof(Student), count, file);
fclose(file);
return written == count;
}
int read_students(const char *filename, Student **students, int *count) {
FILE *file = fopen(filename, "rb");
if (!file) return 0;
// Get file size to determine number of students
fseek(file, 0, SEEK_END);
long file_size = ftell(file);
rewind(file);
*count = file_size / sizeof(Student);
*students = malloc(*count * sizeof(Student));
if (!*students) {
fclose(file);
return 0;
}
size_t read = fread(*students, sizeof(Student), *count, file);
fclose(file);
return read == *count;
}</code>
</div>
<h3>Advanced Position-Based Operations</h3>
<div class="code-block">
<code>// Random access to records in a file
void update_student_gpa(const char *filename, int student_id, double new_gpa) {
FILE *file = fopen(filename, "r+b"); // read/write binary
if (!file) return;
Student temp;
long position = 0;
// Search for student by ID
while (fread(&temp, sizeof(Student), 1, file) == 1) {
if (temp.id == student_id) {
// Found student, update GPA
temp.gpa = new_gpa;
// Seek back to this record's position
fseek(file, position, SEEK_SET);
fwrite(&temp, sizeof(Student), 1, file);
break;
}
position = ftell(file); // save position for next iteration
}
fclose(file);
}
// Get file statistics
void analyze_student_file(const char *filename) {
FILE *file = fopen(filename, "rb");
if (!file) return;
fseek(file, 0, SEEK_END);
long file_size = ftell(file);
int num_students = file_size / sizeof(Student);
printf("File: %s\n", filename);
printf("Size: %ld bytes\n", file_size);
printf("Students: %d\n", num_students);
printf("Record size: %zu bytes\n", sizeof(Student));
fclose(file);
}</code>
</div>
<h3>Buffer Interaction: Formatted vs Unformatted</h3>
<div class="code-block">
<code>#include <stdio.h>
void demonstrate_buffer_mixing() {
FILE *file = fopen("mixed_io.txt", "w+");
// Write formatted data
fprintf(file, "Line 1: %d\n", 100);
fprintf(file, "Line 2: %d\n", 200);
// Write unformatted data
char raw_data[] = "RAW DATA";
fwrite(raw_data, 1, sizeof(raw_data), file);
// IMPORTANT: Flush before switching read/write modes
fflush(file);
rewind(file);
// Read back with mixed methods
char line_buffer[100];
fgets(line_buffer, sizeof(line_buffer), file);
printf("Formatted read: %s", line_buffer);
char raw_buffer[20];
fread(raw_buffer, 1, 10, file);
printf("Unformatted read: %.10s\n", raw_buffer);
fclose(file);
}
// Buffer management functions
void buffer_control_demo() {
FILE *file = fopen("test.txt", "w");
// Different buffering modes
char *buffer = malloc(8192);
setvbuf(file, buffer, _IOFBF, 8192); // full buffering
// Write lots of data - stays in buffer
for (int i = 0; i < 1000; i++) {
fprintf(file, "Line %d\n", i);
}
printf("Data written to buffer, not disk yet\n");
// Force flush to disk
fflush(file);
printf("Now data is on disk\n");
fclose(file);
free(buffer);
}</code>
</div>
<div class="code-block">
<code>typedef struct {
int id;
char name[50];
double salary;
} Employee;
int write_employee_binary(const char *filename, Employee emp) {
FILE *file = fopen(filename, "wb");
if (!file) return 0;
size_t written = fwrite(&emp, sizeof(Employee), 1, file);
fclose(file);
return written == 1;
}
int read_employee_binary(const char *filename, Employee *emp) {
FILE *file = fopen(filename, "rb");
if (!file) return 0;
size_t read = fread(emp, sizeof(Employee), 1, file);
fclose(file);
return read == 1;
}</code>
</div>
<h3>File Positioning</h3>
<div class="code-block">
<code>FILE *file = fopen("data.txt", "r+");
// Get current position
long pos = ftell(file);
// Move to specific position
fseek(file, 0, SEEK_SET); // beginning
fseek(file, 0, SEEK_END); // end
fseek(file, -10, SEEK_CUR); // 10 bytes back from current
// Alternative positioning
rewind(file); // same as fseek(file, 0, SEEK_SET)
fclose(file);</code>
</div>
</div>
<div class="section" id="buffers">
<h2>Buffer Management</h2>
<p>Proper buffer management prevents overflow attacks, memory corruption, and ensures efficient I/O operations.</p>
<h3>Buffer Types and Control</h3>
<div class="code-block">
<code>#include <stdio.h>
int main() {
FILE *file = fopen("output.txt", "w");
// Buffer control
setvbuf(file, NULL, _IONBF, 0); // no buffering
setvbuf(file, NULL, _IOLBF, 0); // line buffering
setvbuf(file, NULL, _IOFBF, 8192); // full buffering
// Custom buffer
char custom_buffer[4096];
setvbuf(file, custom_buffer, _IOFBF, sizeof(custom_buffer));
// Force flush
fprintf(file, "This gets flushed immediately\n");
fflush(file); // force write to disk
fclose(file);
return 0;
}</code>
</div>
<h3>Safe String Handling</h3>
<div class="code-block">
<code>// Unsafe - buffer overflow risk
char buffer[50];
gets(buffer); // NEVER USE THIS!
scanf("%s", buffer); // Also dangerous
// Safe alternatives
char safe_buffer[50];
fgets(safe_buffer, sizeof(safe_buffer), stdin);
// Remove newline from fgets
safe_buffer[strcspn(safe_buffer, "\n")] = '\0';
// Safe formatted input
char name[50];
scanf("%49s", name); // limit to buffer size - 1
// Even safer with snprintf
char output[100];
snprintf(output, sizeof(output), "Hello %s", name);</code>
</div>
<h4>Buffer Overflow Prevention</h4>
<div class="warning">
Always validate input sizes! Buffer overflows are a major security vulnerability. Use functions like fgets(), snprintf(), and strncpy() instead of their unsafe counterparts.
</div>
<div class="code-block">
<code>// Safe string copying and concatenation
char dest[100];
char src[] = "Hello World";
// Safe copy
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0'; // ensure null termination
// Safe concatenation
size_t remaining = sizeof(dest) - strlen(dest) - 1;
strncat(dest, " - Added text", remaining);</code>
</div>
</div>
<div class="section" id="params">
<h2>Parameter Passing Mechanisms</h2>
<p>C uses pass-by-value for all parameters, but you can simulate other passing methods using pointers and arrays.</p>
<h3>Pass by Value</h3>
<div class="code-block">
<code>void modify_value(int x) {
x = 100; // only changes local copy
printf("Inside function: %d\n", x);
}
int main() {
int num = 5;
modify_value(num);
printf("After function: %d\n", num); // still 5
return 0;
}</code>
</div>
<h3>Pass by Reference (using pointers)</h3>
<div class="code-block">
<code>void modify_reference(int *x) {
*x = 100; // changes original value
printf("Inside function: %d\n", *x);
}
void swap_values(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int num = 5;
modify_reference(&num);
printf("After function: %d\n", num); // now 100
int x = 10, y = 20;
swap_values(&x, &y);
printf("x=%d, y=%d\n", x, y); // x=20, y=10
return 0;
}</code>
</div>
<h3>Array Parameters</h3>
<div class="code-block">
<code>// Arrays are always passed by reference (pointer to first element)
void print_array(int arr[], int size) {
// arr is actually int *arr
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
void modify_array(int arr[], int size) {
for (int i = 0; i < size; i++) {
arr[i] *= 2; // modifies original array
}
}
int main() {
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
print_array(numbers, size);
modify_array(numbers, size);
print_array(numbers, size); // values doubled
return 0;
}</code>
</div>
<h3>Pass by Alias (References in C++)</h3>
<div class="code-block">
<code>// C++ reference parameters (pass-by-alias)
#ifdef __cplusplus
void swap_by_alias(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
void modify_by_alias(int &x) {
x = 999; // directly modifies original variable
}
// References must be initialized and cannot be reassigned
void reference_demo() {
int original = 42;
int &ref = original; // ref is an alias for original
ref = 100; // changes original to 100
printf("original = %d\n", original); // prints 100
}
#endif
// In C, simulate pass-by-alias with pointers
void c_style_alias(int *const x) {
*x = 999; // const pointer simulates reference behavior
}</code>
</div>
<h4>Complex Type Parameter Passing</h4>
<div class="code-block">
<code>typedef struct {
int id;
char name[50];
double scores[5];
} Student;
// Pass struct by value (expensive - full copy)
void print_student_copy(Student s) {
printf("ID: %d, Name: %s\n", s.id, s.name);
// Changes to s don't affect original
}
// Pass struct by address (efficient - pointer)
void print_student_ref(const Student *s) {
printf("ID: %d, Name: %s\n", s->id, s->name);
// Use const to prevent accidental modification
}
// Modify struct through pointer
void update_student(Student *s, double new_score) {
s->scores[0] = new_score; // modifies original
}
// Return struct by value
Student create_student(int id, const char *name) {
Student s;
s.id = id;
strncpy(s.name, name, sizeof(s.name) - 1);
s.name[sizeof(s.name) - 1] = '\0';
// Initialize scores
for (int i = 0; i < 5; i++) {
s.scores[i] = 0.0;
}
return s; // returns copy of struct
}</code>
</div>
<div class="code-block">
<code>// Function pointer declarations
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
// Function pointer variable
int (*operation)(int, int);
int main() {
operation = add;
printf("5 + 3 = %d\n", operation(5, 3));
operation = multiply;
printf("5 * 3 = %d\n", operation(5, 3));
return 0;
}</code>
</div>
</div>
<div class="section" id="linkedlists">
<h2>Linked Lists</h2>
<p>Linked lists are dynamic data structures where elements (nodes) contain data and a pointer to the next element. They allow efficient insertion and deletion but require sequential access.</p>
<h3>Basic Node Structure</h3>
<div class="code-block">
<code>// Simple linked list node
typedef struct Node {
int data;
struct Node *next;
} Node;
// Alternative with typedef for cleaner syntax
typedef struct ListNode ListNode;
struct ListNode {
int data;
ListNode *next;
};</code>
</div>
<div class="visualization">
<h4>Linked List Visualization</h4>
<div style="margin: 20px 0;">
<span class="node">10</span>
<span class="arrow">→</span>
<span class="node">20</span>
<span class="arrow">→</span>
<span class="node">30</span>
<span class="arrow">→</span>
<span style="color: #ef4444;">NULL</span>
</div>
</div>
<h3>Complete Linked List Implementation</h3>
<div class="code-block">
<code>#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node *next;
} Node;
// Create new node
Node* create_node(int data) {
Node *new_node = malloc(sizeof(Node));
if (new_node == NULL) {
printf("Memory allocation failed\n");
exit(1);
}
new_node->data = data;
new_node->next = NULL;
return new_node;
}
// Insert at beginning
Node* insert_front(Node *head, int data) {
Node *new_node = create_node(data);
new_node->next = head;
return new_node; // new head
}
// Insert at end
Node* insert_end(Node *head, int data) {
Node *new_node = create_node(data);
if (head == NULL) {
return new_node;
}
Node *current = head;
while (current->next != NULL) {
current = current->next;
}
current->next = new_node;
return head;
}
// Delete by value
Node* delete_node(Node *head, int data) {
if (head == NULL) return NULL;
// Delete head node
if (head->data == data) {
Node *temp = head;
head = head->next;
free(temp);
return head;
}
// Find and delete other nodes
Node *current = head;
while (current->next != NULL && current->next->data != data) {
current = current->next;
}
if (current->next != NULL) {
Node *temp = current->next;
current->next = current->next->next;
free(temp);
}
return head;
}
// Print list
void print_list(Node *head) {
Node *current = head;
while (current != NULL) {
printf("%d", current->data);
if (current->next != NULL) printf(" -> ");
current = current->next;
}
printf(" -> NULL\n");
}
// Free entire list
void free_list(Node *head) {
Node *current = head;
while (current != NULL) {
Node *temp = current;
current = current->next;
free(temp);
}
}
// Search for value
Node* search(Node *head, int data) {
Node *current = head;
while (current != NULL) {
if (current->data == data) {
return current;
}
current = current->next;
}
return NULL;
}
<div class="section">
<h2>Practical Application: Student Database System</h2>
<p>Let's combine all module 4 concepts into a complete program that manages student records using files, proper parameter passing, and linked lists.</p>
<div class="code-block">
<code>#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Well-designed struct with minimal padding
typedef struct {
double gpa; // 8 bytes (largest first)
int id; // 4 bytes
char name[32]; // 32 bytes
char grade; // 1 byte
// 3 bytes padding = 48 bytes total
} StudentRecord;
// Linked list node for in-memory operations
typedef struct StudentNode {
StudentRecord data;
struct StudentNode *next;
} StudentNode;
// Function prototypes demonstrating different parameter passing
void print_record_copy(StudentRecord student); // pass-by-value
void print_record_ref(const StudentRecord *student); // pass-by-address
void update_gpa(StudentRecord *student, double new_gpa); // pass-by-address
StudentRecord create_student(int id, const char *name, double gpa, char grade);
// Linked list operations
StudentNode* add_student_front(StudentNode *head, StudentRecord student);
StudentNode* remove_student_front(StudentNode *head);
void traverse_students(StudentNode *head);
StudentNode* find_student_by_id(StudentNode *head, int id);
void free_student_list(StudentNode *head);
// File operations with proper buffering
int save_students_to_file(const char *filename, StudentNode *head);
StudentNode* load_students_from_file(const char *filename);
int main() {
StudentNode *student_list = NULL;
// Create some students
StudentRecord s1 = create_student(1001, "Alice Johnson", 3.8, 'A');
StudentRecord s2 = create_student(1002, "Bob Smith", 3.2, 'B');
StudentRecord s3 = create_student(1003, "Carol Davis", 3.9, 'A');
// Add to linked list
student_list = add_student_front(student_list, s1);
student_list = add_student_front(student_list, s2);
student_list = add_student_front(student_list, s3);
printf("Student List:\n");
traverse_students(student_list);
// Demonstrate parameter passing
printf("\nDemonstrating parameter passing:\n");
print_record_copy(s1); // pass-by-value (copy)
print_record_ref(&s1); // pass-by-address (pointer)
// Modify through pointer
update_gpa(&s1, 4.0);
printf("After GPA update:\n");
print_record_ref(&s1);
// Save to file
if (save_students_to_file("students.dat", student_list)) {
printf("\nStudents saved to file successfully\n");
}
// Clean up
free_student_list(student_list);
// Load back from file
StudentNode *loaded_list = load_students_from_file("students.dat");
if (loaded_list) {
printf("\nLoaded students from file:\n");
traverse_students(loaded_list);
free_student_list(loaded_list);
}
return 0;
}
// Pass-by-value: receives copy of entire struct
void print_record_copy(StudentRecord student) {
printf("COPY - ID: %d, Name: %s, GPA: %.2f\n",
student.id, student.name, student.gpa);
// Any changes here don't affect original
}
// Pass-by-address: receives pointer to struct
void print_record_ref(const StudentRecord *student) {
printf("REF - ID: %d, Name: %s, GPA: %.2f\n",
student->id, student->name, student->gpa);
// const prevents accidental modification
}
void update_gpa(StudentRecord *student, double new_gpa) {
student->gpa = new_gpa; // modifies original struct
}
StudentRecord create_student(int id, const char *name, double gpa, char grade) {
StudentRecord student;
student.id = id;
student.gpa = gpa;
student.grade = grade;
// Safe string copy
strncpy(student.name, name, sizeof(student.name) - 1);
student.name[sizeof(student.name) - 1] = '\0';
return student; // return by value
}
// Linked list implementations
StudentNode* add_student_front(StudentNode *head, StudentRecord student) {
StudentNode *new_node = malloc(sizeof(StudentNode));
if (!new_node) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
new_node->data = student;
new_node->next = head;
return new_node;
}
StudentNode* remove_student_front(StudentNode *head) {
if (head == NULL) {
printf("Cannot remove from empty list\n");
return NULL;
}
StudentNode *temp = head;
head = head->next;
printf("Removing student: %s (ID: %d)\n",
temp->data.name, temp->data.id);
free(temp);
return head;
}
void traverse_students(StudentNode *head) {
StudentNode *current = head;
int position = 0;
while (current != NULL) {
printf("[%d] ID: %d, Name: %-15s, GPA: %.2f, Grade: %c\n",
position, current->data.id, current->data.name,
current->data.gpa, current->data.grade);
current = current->next;
position++;
}
}
StudentNode* find_student_by_id(StudentNode *head, int id) {
StudentNode *current = head;
while (current != NULL) {
if (current->data.id == id) {
return current;
}
current = current->next;
}
return NULL;
}
void free_student_list(StudentNode *head) {
StudentNode *current = head;
while (current != NULL) {
StudentNode *temp = current;
current = current->next;
free(temp);
}
}
// File operations with proper buffering
int save_students_to_file(const char *filename, StudentNode *head) {
FILE *file = fopen(filename, "wb");
if (!file) {
perror("Cannot open file for writing");
return 0;
}
// Set up efficient buffering for batch writes
char *buffer = malloc(8192);
if (buffer) {
setvbuf(file, buffer, _IOFBF, 8192);
}
// Write each student record
StudentNode *current = head;
int count = 0;
while (current != NULL) {
size_t written = fwrite(¤t->data, sizeof(StudentRecord), 1, file);
if (written != 1) {
fprintf(stderr, "Error writing student %d\n", current->data.id);
fclose(file);
free(buffer);
return 0;
}
current = current->next;
count++;
}
fflush(file); // ensure all data is written
fclose(file);
free(buffer);
printf("Saved %d student records\n", count);
return 1;
}
StudentNode* load_students_from_file(const char *filename) {
FILE *file = fopen(filename, "rb");
if (!file) {
perror("Cannot open file for reading");
return NULL;
}
StudentNode *head = NULL;
StudentRecord temp_student;
// Read records and build linked list
while (fread(&temp_student, sizeof(StudentRecord), 1, file) == 1) {
head = add_student_front(head, temp_student);
}
fclose(file);
return head;
}</code>
</div>
<div class="tip">
This complete example demonstrates: structure padding optimization, file I/O with binary data, proper buffering, all parameter passing methods, and full linked list operations including the specific functions required by the learning objectives.
</div>
<h3>Advanced: Doubly Linked Lists</h3>
<div class="code-block">
<code>typedef struct DNode {
int data;
struct DNode *next;
struct DNode *prev;
} DNode;
DNode* insert_sorted(DNode *head, int data) {
DNode *new_node = malloc(sizeof(DNode));
new_node->data = data;
new_node->next = NULL;
new_node->prev = NULL;
// Empty list
if (head == NULL) {
return new_node;
}
// Insert at beginning
if (data < head->data) {
new_node->next = head;
head->prev = new_node;
return new_node;
}
// Find insertion point
DNode *current = head;
while (current->next != NULL && current->next->data < data) {
current = current->next;
}
// Insert after current
new_node->next = current->next;
new_node->prev = current;
if (current->next != NULL) {
current->next->prev = new_node;
}
current->next = new_node;
return head;
}</code>
</div>
<div class="visualization">
<h4>Doubly Linked List</h4>
<div style="margin: 20px 0; font-size: 0.9em;">
<span style="color: #ef4444;">NULL</span>
<span class="arrow">←</span>
<span class="node">10</span>
<span class="arrow">⟷</span>
<span class="node">20</span>
<span class="arrow">⟷</span>
<span class="node">30</span>
<span class="arrow">→</span>
<span style="color: #ef4444;">NULL</span>
</div>
</div>
</div>
<div class="section">
<h2>Advanced Topics & Common Patterns</h2>
<h3>Memory Management Best Practices</h3>
<div class="code-block">
<code>// Always check malloc return values
void* safe_malloc(size_t size) {
void *ptr = malloc(size);
if (ptr == NULL) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
return ptr;
}
// RAII-style cleanup with function pointers
typedef struct {
void *data;
void (*cleanup)(void*);
} Resource;
Resource* create_resource(size_t size, void (*cleanup_func)(void*)) {
Resource *res = safe_malloc(sizeof(Resource));
res->data = safe_malloc(size);
res->cleanup = cleanup_func;
return res;
}
void destroy_resource(Resource *res) {
if (res && res->cleanup) {
res->cleanup(res->data);
}
free(res);
}</code>
</div>
<h3>Error Handling Patterns</h3>
<div class="code-block">
<code>typedef enum {
SUCCESS = 0,
ERROR_NULL_POINTER,
ERROR_MEMORY_ALLOCATION,
ERROR_FILE_NOT_FOUND,
ERROR_INVALID_INPUT
} ErrorCode;
ErrorCode process_file(const char *filename, int **result, size_t *count) {
if (!filename || !result || !count) {
return ERROR_NULL_POINTER;
}
FILE *file = fopen(filename, "r");
if (!file) {
return ERROR_FILE_NOT_FOUND;
}
// Process file...
*result = malloc(100 * sizeof(int));
if (!*result) {
fclose(file);
return ERROR_MEMORY_ALLOCATION;
}
// Success
*count = 100;
fclose(file);
return SUCCESS;
}</code>
</div>
<div class="interactive">
<h4>Practice Exercise</h4>
<p>Click to reveal a coding challenge:</p>
<button class="button" onclick="toggleChallenge()">Show Challenge</button>
<div id="challenge" style="display: none; margin-top: 20px; text-align: left;">
<strong>Challenge:</strong> Implement a function that reads a file of integers, stores them in a dynamically allocated array, sorts them, and writes the sorted results to a new file. Handle all error cases properly.
</div>
</div>
</div>
<div class="section" id="glossary">
<h2>Glossary</h2>
<div class="glossary">
<div class="glossary-term">Alignment</div>
<div class="glossary-def">Memory addresses that are multiples of the data type size for efficient processor access.</div>
<div class="glossary-term">Buffer</div>
<div class="glossary-def">Temporary storage area in memory used to hold data during I/O operations.</div>
<div class="glossary-term">EOF</div>
<div class="glossary-def">End-of-File constant (-1) returned when no more data can be read from a file.</div>
<div class="glossary-term">FILE*</div>
<div class="glossary-def">Pointer to a FILE structure that represents an open file stream.</div>
<div class="glossary-term">Padding</div>
<div class="glossary-def">Extra bytes added by the compiler between structure members to maintain alignment.</div>
<div class="glossary-term">Pass-by-value</div>
<div class="glossary-def">Parameter passing where a copy of the argument is made; original value unchanged.</div>
<div class="glossary-term">Pass-by-reference</div>
<div class="glossary-def">Simulated in C using pointers to pass the address of a variable.</div>
<div class="glossary-term">Node</div>
<div class="glossary-def">Basic unit of a linked list containing data and pointer(s) to other nodes.</div>
<div class="glossary-term">Traversal</div>
<div class="glossary-def">Process of visiting each node in a linked list sequentially.</div>
<div class="glossary-term">Memory Leak</div>
<div class="glossary-def">Allocated memory that is not freed, causing gradual memory consumption.</div>
<div class="glossary-term">Dangling Pointer</div>
<div class="glossary-def">Pointer that references memory that has been deallocated or is invalid.</div>
<div class="glossary-term">Buffer Overflow</div>
<div class="glossary-def">Writing data beyond the allocated buffer size, potentially overwriting adjacent memory.</div>
</div>
</div>
<div class="section">
<h2>Key Takeaways</h2>
<div class="tip">
<strong>Memory Management:</strong> Always pair malloc() with free(). Check for NULL returns. Use tools like valgrind to detect leaks.
</div>
<div class="tip">
<strong>Structure Design:</strong> Order members by size (largest first) to minimize padding and memory usage.
</div>
<div class="tip">
<strong>File Operations:</strong> Always check if file operations succeed. Close files when done. Use appropriate buffering for your use case.
</div>
<div class="tip">
<strong>Safe Programming:</strong> Use bounds-checking functions. Validate all inputs. Initialize pointers to NULL.
</div>
<div class="warning">
<strong>Common Mistakes to Avoid:</strong> Using gets(), forgetting to free memory, not checking malloc returns, buffer overflows, accessing freed memory, infinite loops in linked lists.
</div>
</div>
<div class="footer">
<p>CSE 240 - Introduction to Programming Languages | Module 4 Reference</p>
<p>Master these concepts through practice and experimentation!</p>
</div>
</div>
<script>
function scrollToSection(id) {
document.getElementById(id).scrollIntoView({
behavior: 'smooth'
});
}
function toggleChallenge() {
const challenge = document.getElementById('challenge');
const button = event.target;
if (challenge.style.display === 'none') {
challenge.style.display = 'block';
button.textContent = 'Hide Challenge';
} else {
challenge.style.display = 'none';
button.textContent = 'Show Challenge';
}
}
// Add some interactive highlighting
document.addEventListener('DOMContentLoaded', function() {
const codeBlocks = document.querySelectorAll('.code-block');
codeBlocks.forEach(block => {
block.addEventListener('click', function() {
this.style.border = '2px solid #3b82f6';
setTimeout(() => {
this.style.border = '1px solid #334155';
}, 1000);
});
});
});
</script>
</body>
</html>