C Programming Curriculum - Instructor Resources
VISUAL LEARNING AIDS
Memory Layout Diagrams
Stack vs Heap Memory
MEMORY LAYOUT
HIGH ADDRESSES
+------------------+
| Command Line | <- argv, environment variables
| Arguments |
+------------------+
| STACK | <- Local variables, function parameters
| | | Grows downward
| v |
+------------------+
| | <- Free memory
| |
+------------------+
| ^ |
| | |
| HEAP | <- malloc(), dynamic allocation
| | Grows upward
+------------------+
| UNINITIALIZED | <- BSS segment (global/static vars)
| DATA | initialized to zero
+------------------+
| INITIALIZED | <- Data segment (initialized globals)
| DATA |
+------------------+
| TEXT | <- Program code (read-only)
| (CODE) |
+------------------+
LOW ADDRESSES
Pointer Visualization
int x = 42;
int *p = &x;
Memory Address: Variable: Value:
+-------------+ +-------+ +------+
| 1000 | <- | x | -> | 42 |
+-------------+ +-------+ +------+
| 2000 | <- | p | -> | 1000 | (points to x)
+-------------+ +-------+ +------+
*p = 100; // Changes x to 100
Memory Address: Variable: Value:
+-------------+ +-------+ +------+
| 1000 | <- | x | -> | 100 |
+-------------+ +-------+ +------+
| 2000 | <- | p | -> | 1000 |
+-------------+ +-------+ +------+
Array Memory Layout
int arr[5] = {10, 20, 30, 40, 50};
Memory:
Address: 1000 1004 1008 1012 1016
+----+----+----+----+----+
Value: | 10 | 20 | 30 | 40 | 50 |
+----+----+----+----+----+
Index: 0 1 2 3 4
arr[2] is at address: 1000 + (2 * sizeof(int)) = 1008
INTERACTIVE DEMONSTRATIONS
Demonstration 1: Variable Scope
File: scope_demo.c
#include <stdio.h>
int global_var = 100; // Global scope
void function_demo() {
int local_var = 200; // Local scope
static int static_var = 300; // Static scope
printf("Inside function:\n");
printf("Global: %d\n", global_var);
printf("Local: %d\n", local_var);
printf("Static: %d\n", static_var);
global_var++;
local_var++;
static_var++;
}
int main() {
int main_local = 400; // Local to main
printf("In main (before function call):\n");
printf("Global: %d\n", global_var);
printf("Main local: %d\n", main_local);
function_demo();
printf("\nIn main (after first function call):\n");
printf("Global: %d\n", global_var);
function_demo(); // Call again to show static behavior
return 0;
}
Expected Output:
In main (before function call):
Global: 100
Main local: 400
Inside function:
Global: 100
Local: 200
Static: 300
In main (after first function call):
Global: 101
Inside function:
Global: 101
Local: 200
Static: 301
Demonstration 2: Pointer Arithmetic
File: pointer_demo.c
#include <stdio.h>
void demonstrate_pointer_arithmetic() {
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr; // Points to first element
printf("Array elements using array notation:\n");
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d (address: %p)\n", i, arr[i], (void*)&arr[i]);
}
printf("\nArray elements using pointer arithmetic:\n");
for (int i = 0; i < 5; i++) {
printf("*(ptr + %d) = %d (address: %p)\n", i, *(ptr + i), (void*)(ptr + i));
}
printf("\nTraversing with pointer increment:\n");
ptr = arr; // Reset pointer
for (int i = 0; i < 5; i++) {
printf("*ptr = %d (address: %p)\n", *ptr, (void*)ptr);
ptr++; // Move to next element
}
}
int main() {
demonstrate_pointer_arithmetic();
return 0;
}
Demonstration 3: Memory Allocation
File: memory_demo.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void demonstrate_memory_allocation() {
// Static allocation
int static_array[5] = {1, 2, 3, 4, 5};
// Dynamic allocation
int *dynamic_array = (int*)malloc(5 * sizeof(int));
if (dynamic_array == NULL) {
printf("Memory allocation failed!\n");
return;
}
// Initialize dynamic array
for (int i = 0; i < 5; i++) {
dynamic_array[i] = (i + 1) * 10;
}
printf("Static array (on stack):\n");
for (int i = 0; i < 5; i++) {
printf("static_array[%d] = %d (address: %p)\n",
i, static_array[i], (void*)&static_array[i]);
}
printf("\nDynamic array (on heap):\n");
for (int i = 0; i < 5; i++) {
printf("dynamic_array[%d] = %d (address: %p)\n",
i, dynamic_array[i], (void*)&dynamic_array[i]);
}
// Resize dynamic array
dynamic_array = (int*)realloc(dynamic_array, 10 * sizeof(int));
if (dynamic_array == NULL) {
printf("Memory reallocation failed!\n");
return;
}
// Initialize new elements
for (int i = 5; i < 10; i++) {
dynamic_array[i] = (i + 1) * 10;
}
printf("\nResized dynamic array:\n");
for (int i = 0; i < 10; i++) {
printf("dynamic_array[%d] = %d\n", i, dynamic_array[i]);
}
free(dynamic_array); // Always free allocated memory
printf("\nMemory freed successfully!\n");
}
int main() {
demonstrate_memory_allocation();
return 0;
}
HANDS-ON EXERCISES WITH SOLUTIONS
Exercise Set 1: Debugging Practice
Buggy Code 1: Find and Fix the Errors
#include <stdio.h>
int main() {
int numbers[5];
int sum = 0;
printf("Enter 5 numbers:\n");
for (int i = 1; i <= 5; i++) { // BUG: Array index out of bounds
scanf("%d", &numbers[i]);
sum += numbers[i];
}
printf("Sum: %d\n", sum);
printf("Average: %d\n", sum / 5); // BUG: Integer division
return 0;
}
Fixed Version:
#include <stdio.h>
int main() {
int numbers[5];
int sum = 0;
printf("Enter 5 numbers:\n");
for (int i = 0; i < 5; i++) { // FIX: Correct array indexing
scanf("%d", &numbers[i]);
sum += numbers[i];
}
printf("Sum: %d\n", sum);
printf("Average: %.2f\n", (float)sum / 5); // FIX: Float division
return 0;
}
Exercise Set 2: Code Optimization
Inefficient Code:
// Inefficient string length function
int strlen_slow(char *str) {
int count = 0;
int i = 0;
while (i < 1000) { // Unnecessary loop limit
if (str[i] == '\0') {
break;
}
count++;
i++;
}
return count;
}
Optimized Version:
// Efficient string length function
int strlen_fast(char *str) {
char *start = str;
while (*str) str++; // Find end of string
return str - start; // Return difference
}
PRACTICAL PROJECTS FOR EACH LEVEL
Beginner Project: Personal Finance Calculator
Requirements: - Track income and expenses - Calculate monthly budget - Show spending by category - Simple file storage
Starter Code:
#include <stdio.h>
#include <string.h>
#define MAX_TRANSACTIONS 100
#define MAX_CATEGORIES 20
struct Transaction {
char description[50];
char category[20];
float amount;
int is_income; // 1 for income, 0 for expense
};
struct Budget {
struct Transaction transactions[MAX_TRANSACTIONS];
int count;
float monthly_income_goal;
float monthly_expense_limit;
};
void add_transaction(struct Budget *budget) {
// Implementation here
}
void show_summary(struct Budget *budget) {
// Implementation here
}
int main() {
struct Budget my_budget = {0};
my_budget.monthly_income_goal = 3000.0;
my_budget.monthly_expense_limit = 2500.0;
// Menu system implementation
return 0;
}
Intermediate Project: Text-Based Adventure Game
Requirements: - Room navigation system - Inventory management - Save/load game state - Simple combat system
Core Structure:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[30];
char description[200];
int north, south, east, west; // Room connections (-1 for none)
int has_item;
char item_name[30];
} Room;
typedef struct {
char name[30];
int health;
int attack;
char inventory[10][30];
int inventory_count;
int current_room;
} Player;
// Function prototypes
void display_room(Room rooms[], Player *player);
void move_player(Room rooms[], Player *player, char direction);
void take_item(Room rooms[], Player *player);
void save_game(Player *player);
void load_game(Player *player);
Advanced Project: Database Management System
Requirements: - Create/drop tables - Insert/update/delete records - Select with WHERE clauses - Basic indexing - File persistence
Core Components:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef enum {
TYPE_INT,
TYPE_FLOAT,
TYPE_STRING
} DataType;
typedef struct {
char name[30];
DataType type;
int size;
} Column;
typedef struct {
char name[30];
Column columns[20];
int column_count;
void **data;
int row_count;
int capacity;
} Table;
typedef struct {
Table tables[10];
int table_count;
} Database;
// Function prototypes
int create_table(Database *db, char *name, Column columns[], int col_count);
int insert_record(Database *db, char *table_name, void *data[]);
int select_records(Database *db, char *table_name, char *where_clause);
int update_records(Database *db, char *table_name, char *set_clause, char *where_clause);
int delete_records(Database *db, char *table_name, char *where_clause);
Expert Project: Multi-threaded Web Server
Requirements: - Handle HTTP requests - Thread pool for concurrent connections - Static file serving - Basic CGI support - Logging system
Foundation:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#define PORT 8080
#define THREAD_POOL_SIZE 10
#define BUFFER_SIZE 1024
typedef struct {
int socket_fd;
struct sockaddr_in client_addr;
} Connection;
typedef struct {
Connection *connections;
int front, rear, count;
int capacity;
pthread_mutex_t mutex;
pthread_cond_t not_empty;
pthread_cond_t not_full;
} ConnectionQueue;
// Function prototypes
void* worker_thread(void* arg);
void handle_request(int client_socket);
void serve_file(int client_socket, char *filename);
void send_response(int client_socket, int status_code, char *content_type, char *body);
DEBUGGING TECHNIQUES AND TOOLS
Using GDB (GNU Debugger)
Basic GDB Commands:
# Compile with debug symbols
gcc -g -o program program.c
# Start GDB
gdb ./program
# Basic commands
(gdb) break main # Set breakpoint at main
(gdb) break 25 # Set breakpoint at line 25
(gdb) run # Start program
(gdb) next # Execute next line
(gdb) step # Step into functions
(gdb) continue # Continue execution
(gdb) print variable # Print variable value
(gdb) list # Show source code
(gdb) backtrace # Show call stack
(gdb) quit # Exit GDB
Sample Debug Session:
// debug_example.c
#include <stdio.h>
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
int main() {
int num = 5;
int result = factorial(num);
printf("Factorial of %d is %d\n", num, result);
return 0;
}
GDB Session:
$ gcc -g -o debug_example debug_example.c
$ gdb ./debug_example
(gdb) break factorial
(gdb) run
(gdb) print n
(gdb) step
(gdb) print n
(gdb) continue
Memory Debugging with Valgrind
Common Memory Errors:
// memory_errors.c
#include <stdio.h>
#include <stdlib.h>
int main() {
// Memory leak
int *ptr1 = malloc(sizeof(int) * 10);
*ptr1 = 42;
// Missing free(ptr1);
// Use after free
int *ptr2 = malloc(sizeof(int) * 5);
free(ptr2);
*ptr2 = 100; // ERROR: Use after free
// Double free
int *ptr3 = malloc(sizeof(int) * 3);
free(ptr3);
free(ptr3); // ERROR: Double free
// Buffer overflow
int *ptr4 = malloc(sizeof(int) * 5);
ptr4[10] = 50; // ERROR: Writing beyond allocated memory
free(ptr4);
return 0;
}
Valgrind Usage:
# Compile program
gcc -g -o memory_errors memory_errors.c
# Run with Valgrind
valgrind --leak-check=full --show-leak-kinds=all ./memory_errors
CODE STYLE GUIDELINES
Naming Conventions
// Good naming examples
int student_count; // Variables: lowercase with underscores
float calculate_average(); // Functions: lowercase with underscores
struct StudentRecord; // Structs: PascalCase
#define MAX_STUDENTS 100 // Constants: UPPERCASE with underscores
// Poor naming examples
int sc; // Too short, unclear
float calcAvg(); // Mixed case, abbreviated
struct studentrecord; // All lowercase
#define maxstudents 100 // Inconsistent case
Code Formatting
// Good formatting
if (condition) {
statement1;
statement2;
} else {
statement3;
}
for (int i = 0; i < count; i++) {
process_item(i);
}
// Poor formatting
if(condition){
statement1;
statement2;
}else{
statement3;
}
for(int i=0;i<count;i++){
process_item(i);
}
Documentation Standards
/**
* Calculates the factorial of a given number
* @param n The number to calculate factorial for (must be >= 0)
* @return The factorial of n, or -1 if n is negative
*/
long factorial(int n) {
// Input validation
if (n < 0) {
return -1; // Error: negative input
}
// Base case
if (n <= 1) {
return 1;
}
// Recursive calculation
return n * factorial(n - 1);
}
COMMON PITFALLS AND SOLUTIONS
Pitfall 1: Buffer Overflow
// DANGEROUS CODE
char buffer[10];
printf("Enter your name: ");
gets(buffer); // Never use gets()!
// SAFE ALTERNATIVE
char buffer[10];
printf("Enter your name: ");
fgets(buffer, sizeof(buffer), stdin);
buffer[strcspn(buffer, "\n")] = '\0'; // Remove newline
Pitfall 2: Uninitialized Pointers
// DANGEROUS CODE
int *ptr;
*ptr = 42; // Undefined behavior!
// SAFE ALTERNATIVE
int *ptr = NULL; // Initialize to NULL
ptr = malloc(sizeof(int));
if (ptr != NULL) {
*ptr = 42;
free(ptr);
ptr = NULL; // Prevent accidental reuse
}
Pitfall 3: Array Index Errors
// DANGEROUS CODE
int arr[5];
for (int i = 1; i <= 5; i++) { // Wrong bounds!
arr[i] = i;
}
// SAFE ALTERNATIVE
int arr[5];
for (int i = 0; i < 5; i++) { // Correct bounds
arr[i] = i + 1;
}
PERFORMANCE OPTIMIZATION TIPS
Tip 1: Avoid Repeated Calculations
// INEFFICIENT
for (int i = 0; i < strlen(string); i++) { // strlen called every iteration
process_char(string[i]);
}
// EFFICIENT
int len = strlen(string); // Calculate once
for (int i = 0; i < len; i++) {
process_char(string[i]);
}
Tip 2: Use Appropriate Data Types
// INEFFICIENT (for small values)
long long tiny_counter = 0;
// EFFICIENT
int tiny_counter = 0; // or even unsigned char for very small ranges
Tip 3: Minimize Dynamic Allocation
// INEFFICIENT (frequent allocation/deallocation)
for (int i = 0; i < 1000; i++) {
int *temp = malloc(sizeof(int) * 100);
// ... use temp ...
free(temp);
}
// EFFICIENT (allocate once)
int *buffer = malloc(sizeof(int) * 100);
for (int i = 0; i < 1000; i++) {
// ... reuse buffer ...
}
free(buffer);
This comprehensive teaching material provides instructors with visual aids, interactive demonstrations, practical exercises, and detailed guidelines to effectively teach C programming at all levels. The materials emphasize best practices, common pitfalls, and real-world applications to prepare students for professional development.