0% found this document useful (0 votes)
4 views43 pages

C Programming Book

Mastering C Programming is a comprehensive guide for learners at all levels, covering fundamental to advanced topics in C programming. The book includes practical code examples, exercises, and a structured approach divided into four parts: Foundations, Intermediate, Advanced, and Expert Topics. It emphasizes the importance of C in programming and provides insights into its history, features, and standardization.

Uploaded by

SAMEER PATHAK
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views43 pages

C Programming Book

Mastering C Programming is a comprehensive guide for learners at all levels, covering fundamental to advanced topics in C programming. The book includes practical code examples, exercises, and a structured approach divided into four parts: Foundations, Intermediate, Advanced, and Expert Topics. It emphasizes the importance of C in programming and provides insights into its history, features, and standardization.

Uploaded by

SAMEER PATHAK
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Mastering C Programming

From Beginner to Advanced


Theory, Practice & Real-World Applications

A Complete Learning Guide


Preface
Welcome to Mastering C Programming — a comprehensive guide designed for students and
self-learners who want to understand C from the ground up. Whether you are an absolute
beginner or someone looking to deepen your knowledge, this book covers everything you need:
from your first 'Hello, World!' program all the way to advanced topics like memory management,
data structures, file I/O, and system-level programming.

C is one of the most influential programming languages ever created. Born at Bell Labs in the
early 1970s, it remains the foundation of operating systems (Linux, Windows), embedded
systems, databases, and compilers. Learning C will give you a deep understanding of how
computers work — a skill that makes you a better programmer in any language.

This book is organized into four parts:


• Part I — Foundations: The basics of C syntax, variables, I/O, and control flow.
• Part II — Intermediate: Functions, arrays, strings, pointers, and memory.
• Part III — Advanced: Structures, file I/O, dynamic memory, and the preprocessor.
• Part IV — Expert Topics: Data structures, algorithms, system programming, and best
practices.

Each chapter combines theory with practical code examples and exercises. Code blocks appear
in a monospace font. Study them, type them out, run them, and experiment. The only way to
truly learn programming is by writing code.
Chapter 1: Introduction to C
1.1 History and Importance of C
C was developed by Dennis Ritchie at Bell Telephone Laboratories in 1972. It was originally
created to rewrite the Unix operating system. Before C, most operating systems were written in
assembly language — difficult, machine-specific code. C provided a powerful but portable
alternative.

C's influence is enormous. Java, C++, C#, Python, PHP, and JavaScript all owe their syntax to
C. The language's design philosophy — trust the programmer, be efficient, and be close to the
hardware — made it ideal for systems programming.

1.2 Features of C
• Simple and efficient — compact syntax with powerful features.
• Structured programming — supports functions and modular design.
• Low-level access — can manipulate memory directly via pointers.
• Portable — C programs can run on different machines with minimal changes.
• Extensive library — rich standard library (stdio.h, stdlib.h, string.h, etc.).
• Foundation for other languages — understanding C helps you learn C++, Java, and
more.

1.3 The C Standard


C has been standardized multiple times. The major standards are:
• K&R C (1978) — the original C described in 'The C Programming Language' by
Kernighan & Ritchie.
• ANSI C / C89 — the first official ANSI standard, adopted in 1989.
• C99 — introduced new features like variable-length arrays and inline functions.
• C11 — added multi-threading support and improved Unicode handling.
• C17 / C18 — minor corrections to C11.

Note: This book primarily targets C99 and C11, which are the most widely used in modern
environments.

1.4 Setting Up Your Environment


To write and run C programs, you need a compiler. The most popular free compiler is GCC
(GNU Compiler Collection).

Installing GCC
• Windows: Install MinGW (Minimalist GNU for Windows) or use WSL (Windows
Subsystem for Linux).
• Linux: Run 'sudo apt install gcc' (Debian/Ubuntu) or 'sudo dnf install gcc' (Fedora).
• macOS: Install Xcode Command Line Tools with 'xcode-select --install'. This installs
clang, which is compatible.

Your First Compile


Create a file named hello.c, then compile and run it:
// Compile:
gcc hello.c -o hello

// Run (Linux/macOS):
./hello

// Run (Windows):
[Link]

1.5 Your First C Program


Every C journey begins with 'Hello, World!'. Here is the complete program:
#include <stdio.h>

int main() {
printf("Hello, World!\n");
return 0;
}

Let's break this down line by line:


• #include <stdio.h> — This is a preprocessor directive. It tells the compiler to include the
Standard Input/Output library, which provides the printf function.
• int main() — This is the main function. Every C program must have a main function.
Execution starts here. It returns an integer (int).
• printf("Hello, World!\n") — This function prints text to the console. \n is the newline
escape sequence.
• return 0 — Returns 0 to the operating system, indicating the program ran successfully.
Practice: Type this program by hand (do not copy-paste), compile it, and run it. Change the
message and observe the output.
Chapter 2: Variables, Data Types & Operators
2.1 Variables
A variable is a named storage location in memory. In C, every variable must be declared before
use, specifying its type and name.

Syntax: data_type variable_name;


int age; // Declare an integer
float price; // Declare a float
char grade; // Declare a character

age = 25; // Assign a value


price = 9.99;
grade = 'A';

// Declare and initialize in one step:


int score = 100;

2.2 Data Types


C provides several fundamental data types:

Type Description Size (typical) Example


int Integer number 4 bytes int x = 10;
short Small integer 2 bytes short s = 100;
long Large integer 4 or 8 bytes long l = 100000L;
float Single precision decimal 4 bytes float f = 3.14f;
double Double precision decimal 8 bytes double d = 3.14159;
char Single character 1 byte char c = 'A';
void No value (used in functions) - void func();

2.3 Constants
Constants are fixed values that cannot be changed. There are two ways to define them:
// Using #define (preprocessor constant)
#define PI 3.14159
#define MAX_SIZE 100

// Using const keyword (type-safe)


const int MAX_AGE = 120;
const float GRAVITY = 9.81f;

2.4 Operators
Arithmetic Operators
int a = 10, b = 3;
printf("%d\n", a + b); // 13 (addition)
printf("%d\n", a - b); // 7 (subtraction)
printf("%d\n", a * b); // 30 (multiplication)
printf("%d\n", a / b); // 3 (integer division)
printf("%d\n", a % b); // 1 (modulus/remainder)

Relational Operators
// Return 1 (true) or 0 (false)
a == b // equal to
a != b // not equal to
a > b // greater than
a < b // less than
a >= b // greater than or equal
a <= b // less than or equal

Logical Operators
&& // Logical AND: (a > 0 && b > 0)
|| // Logical OR: (a > 0 || b > 0)
! // Logical NOT: !(a == b)

Assignment Operators
a = 5; // Assign
a += 3; // a = a + 3 => 8
a -= 2; // a = a - 2 => 6
a *= 4; // a = a * 4 => 24
a /= 6; // a = a / 6 => 4
a %= 3; // a = a % 3 => 1
Increment and Decrement
int x = 5;
x++; // Post-increment: use x (5), then increment to 6
++x; // Pre-increment: increment to 7, then use x (7)
x--; // Post-decrement
--x; // Pre-decrement

2.5 Input with scanf


Use scanf to read user input. The format specifiers match those of printf.
#include <stdio.h>

int main() {
int age;
float height;
printf("Enter your age: ");
scanf("%d", &age);
printf("Enter your height in meters: ");
scanf("%f", &height);
printf("Age: %d, Height: %.2f m\n", age, height);
return 0;
}

Important: The & (address-of) operator before the variable name in scanf is required. It
tells scanf where in memory to store the value.
Chapter 3: Control Flow
3.1 if, else if, else
Conditional statements control the flow of execution based on conditions.
#include <stdio.h>

int main() {
int score;
printf("Enter score: ");
scanf("%d", &score);

if (score >= 90) {


printf("Grade: A\n");
} else if (score >= 80) {
printf("Grade: B\n");
} else if (score >= 70) {
printf("Grade: C\n");
} else if (score >= 60) {
printf("Grade: D\n");
} else {
printf("Grade: F\n");
}
return 0;
}

3.2 switch Statement


The switch statement is useful when comparing a variable against multiple constant values.
int day = 3;
switch (day) {
case 1: printf("Monday\n"); break;
case 2: printf("Tuesday\n"); break;
case 3: printf("Wednesday\n"); break;
case 4: printf("Thursday\n"); break;
case 5: printf("Friday\n"); break;
default: printf("Weekend\n"); break;
}
Remember: Always include 'break' at the end of each case, or execution will 'fall through' to
the next case.

3.3 while Loop


The while loop repeats a block as long as a condition is true.
int i = 1;
while (i <= 5) {
printf("%d ", i);
i++;
}
// Output: 1 2 3 4 5

3.4 do-while Loop


The do-while loop executes the block at least once before checking the condition.
int num;
do {
printf("Enter a positive number: ");
scanf("%d", &num);
} while (num <= 0);
printf("You entered: %d\n", num);

3.5 for Loop


The for loop is ideal when the number of iterations is known in advance.
// Syntax: for (initialization; condition; update)
for (int i = 0; i < 5; i++) {
printf("%d ", i);
}
// Output: 0 1 2 3 4

// Multiplication table for 5


for (int j = 1; j <= 10; j++) {
printf("5 x %d = %d\n", j, 5 * j);
}

3.6 break and continue


// break: exits the loop immediately
for (int i = 0; i < 10; i++) {
if (i == 5) break;
printf("%d ", i); // Output: 0 1 2 3 4
}

// continue: skips the rest of the current iteration


for (int i = 0; i < 10; i++) {
if (i % 2 == 0) continue; // Skip even numbers
printf("%d ", i); // Output: 1 3 5 7 9
}

3.7 Nested Loops


Loops can be nested inside each other. Here's a classic example — printing a multiplication
table:
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 5; j++) {
printf("%4d", i * j);
}
printf("\n");
}
Chapter 4: Functions
4.1 What is a Function?
A function is a self-contained block of code that performs a specific task. Functions allow you to
break a large program into smaller, manageable pieces — promoting code reuse and
readability.

4.2 Defining and Calling Functions


// Function definition
int add(int a, int b) {
return a + b;
}

int main() {
int result = add(3, 4); // Function call
printf("3 + 4 = %d\n", result); // Output: 7
return 0;
}

4.3 Function Prototypes (Declarations)


If you define a function after main, you must declare it first with a prototype:
#include <stdio.h>

// Prototype (declaration)
float area_circle(float radius);

int main() {
float r = 5.0;
printf("Area = %.2f\n", area_circle(r));
return 0;
}

// Definition (can come after main)


float area_circle(float radius) {
return 3.14159 * radius * radius;
}
4.4 Void Functions
A function that does not return a value has return type void.
void greet(char name[]) {
printf("Hello, %s!\n", name);
}

int main() {
greet("Alice");
greet("Bob");
return 0;
}

4.5 Recursion
A function that calls itself is called recursive. Every recursive function needs a base case to stop
the recursion.
// Factorial: n! = n * (n-1) * (n-2) * ... * 1
int factorial(int n) {
if (n == 0 || n == 1) return 1; // Base case
return n * factorial(n - 1); // Recursive call
}

// Fibonacci sequence
int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}

Warning: Deep recursion can cause a stack overflow. Always define a clear base case.

4.6 Scope and Lifetime of Variables


Variables in C have different scopes. A local variable exists only inside the function where it is
declared. A global variable is declared outside all functions and is accessible throughout the
program.
int globalVar = 100; // Global variable
void showScope() {
int localVar = 50; // Local variable
printf("Global: %d, Local: %d\n", globalVar, localVar);
}

// Static local: retains value between calls


void counter() {
static int count = 0;
count++;
printf("Called %d times\n", count);
}
Chapter 5: Arrays and Strings
5.1 One-Dimensional Arrays
An array stores multiple values of the same type in contiguous memory locations.
// Declaration and initialization
int scores[5] = {85, 92, 78, 96, 88};

// Access elements (0-indexed)


printf("%d\n", scores[0]); // 85
printf("%d\n", scores[4]); // 88

// Iterate over array


int sum = 0;
for (int i = 0; i < 5; i++) {
sum += scores[i];
}
printf("Average: %.1f\n", (float)sum / 5);

5.2 Multidimensional Arrays


C supports 2D and higher-dimensional arrays. A 2D array is like a table with rows and columns.
// 2D array: 3 rows, 4 columns
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};

// Access element at row 1, column 2


printf("%d\n", matrix[1][2]); // 7

// Print entire matrix


for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%4d", matrix[i][j]);
}
printf("\n");
}
5.3 Strings in C
A string in C is an array of characters terminated by a null character '\0'. The <string.h> library
provides useful string functions.
#include <string.h>

char name[] = "Alice"; // Compiler adds \0 automatically


char greeting[20] = "Hello";

printf("Length: %zu\n", strlen(name)); // 5


printf("Name: %s\n", name);

// String functions from string.h


char str1[20] = "Hello";
char str2[] = " World";

strcat(str1, str2); // Concatenate: str1 = "Hello World"


printf("%s\n", str1);

int cmp = strcmp("apple", "banana"); // Compare: negative (a < b)


char copy[20];
strcpy(copy, str1); // Copy str1 into copy

5.4 Passing Arrays to Functions


When you pass an array to a function, C passes a pointer to its first element. The function can
modify the original array.
void print_array(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}

void reverse_array(int arr[], int size) {


int left = 0, right = size - 1;
while (left < right) {
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
left++; right--;
}
}
Chapter 6: Pointers
6.1 What is a Pointer?
A pointer is a variable that stores the memory address of another variable. Pointers are one of
C's most powerful — and most misunderstood — features. They are essential for dynamic
memory allocation, arrays, strings, and passing by reference.

int x = 42;
int *ptr; // Declare a pointer to int
ptr = &x; // & is the address-of operator

printf("Value of x: %d\n", x); // 42


printf("Address of x: %p\n", (void*)ptr);// memory address
printf("Value via ptr: %d\n", *ptr); // 42 (dereference)

*ptr = 100; // Modify x through the pointer


printf("x is now: %d\n", x); // 100

6.2 Pointer Arithmetic


You can perform arithmetic on pointers. Adding 1 to a pointer moves it to the next element of its
type.
int arr[] = {10, 20, 30, 40, 50};
int *p = arr; // Points to arr[0]

printf("%d\n", *p); // 10
printf("%d\n", *(p + 1)); // 20
printf("%d\n", *(p + 4)); // 50

// Traversing with pointer increment


for (int i = 0; i < 5; i++) {
printf("%d ", *p);
p++;
}

6.3 Pointers and Functions


Passing a pointer to a function allows the function to modify the original variable (pass by
reference).
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}

int main() {
int x = 5, y = 10;
printf("Before: x=%d, y=%d\n", x, y);
swap(&x, &y);
printf("After: x=%d, y=%d\n", x, y);
return 0;
}

6.4 Pointer to Pointer


A pointer to a pointer (double pointer) stores the address of another pointer.
int val = 5;
int *ptr = &val;
int **pptr = &ptr;

printf("%d\n", val); // 5
printf("%d\n", *ptr); // 5
printf("%d\n", **pptr); // 5

6.5 NULL Pointer


A NULL pointer points to nothing. It is used to initialize pointers and as error return values.
int *p = NULL;

// Always check for NULL before dereferencing


if (p != NULL) {
printf("%d\n", *p);
} else {
printf("Pointer is NULL!\n");
}
Safety Rule: Never dereference a NULL pointer or an uninitialized pointer. This causes
undefined behavior (often a crash).
Chapter 7: Memory Management
7.1 Stack vs. Heap
C programs use two main memory areas. The stack stores local variables and function call
information. It is managed automatically — memory is allocated when a function is called and
freed when it returns. The heap is a large pool of memory available for dynamic allocation. The
programmer is responsible for allocating and freeing heap memory.

7.2 Dynamic Memory Allocation


The <stdlib.h> library provides four key functions for heap memory management:
• malloc(size) — allocates 'size' bytes. Returns a void pointer. Memory is uninitialized.
• calloc(n, size) — allocates memory for n elements of 'size' bytes each. Initializes
memory to zero.
• realloc(ptr, new_size) — resizes a previously allocated block.
• free(ptr) — releases allocated memory back to the heap.

#include <stdlib.h>
#include <stdio.h>

int main() {
int n = 5;

// Allocate array dynamically


int *arr = (int *)malloc(n * sizeof(int));

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

for (int i = 0; i < n; i++) {


arr[i] = (i + 1) * 10;
}

for (int i = 0; i < n; i++) {


printf("%d ", arr[i]); // 10 20 30 40 50
}
printf("\n");
free(arr); // ALWAYS free allocated memory
arr = NULL; // Prevent dangling pointer
return 0;
}

7.3 Memory Leaks


A memory leak occurs when you allocate memory but never free it. Over time, this can consume
all available memory and crash your program. Rule: every malloc/calloc must have a
corresponding free.

Best Practice: After freeing a pointer, set it to NULL immediately. This prevents use-after-
free bugs and double-free errors.
Chapter 8: Structures and Unions
8.1 Structures
A structure (struct) groups together variables of different types under one name. This allows you
to model real-world entities.
#include <stdio.h>
#include <string.h>

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

int main() {
struct Student s1;
strcpy([Link], "Alice");
[Link] = 20;
[Link] = 3.85;

printf("Name: %s\n", [Link]);


printf("Age: %d\n", [Link]);
printf("GPA: %.2f\n", [Link]);
return 0;
}

8.2 typedef with Structures


The typedef keyword creates an alias for a type, making code more readable.
typedef struct {
float x;
float y;
} Point;

Point p1 = {3.0, 4.0};


printf("Point: (%.1f, %.1f)\n", p1.x, p1.y);

float distance = sqrt(p1.x * p1.x + p1.y * p1.y);


printf("Distance from origin: %.2f\n", distance);

8.3 Pointers to Structures


When working with structures dynamically or passing to functions, use pointers. The arrow
operator (->) is used to access members via a pointer.
typedef struct {
char name[50];
int age;
} Person;

void birthday(Person *p) {


p->age++; // Same as (*p).age++
printf("%s is now %d years old.\n", p->name, p->age);
}

int main() {
Person alice = {"Alice", 25};
birthday(&alice); // Now 26
return 0;
}

8.4 Unions
A union is similar to a structure, but all members share the same memory location. Only one
member can hold a value at a time. Unions save memory when only one of multiple values is
needed at a time.
union Data {
int i;
float f;
char str[20];
};

union Data d;
d.i = 10;
printf("d.i = %d\n", d.i); // 10
d.f = 3.14;
printf("d.f = %.2f\n", d.f); // 3.14
// Note: d.i is now garbage since we wrote d.f
Chapter 9: File Input/Output
9.1 Opening and Closing Files
C provides file I/O through the <stdio.h> library. You open a file with fopen and close it with
fclose. Always check if fopen succeeded.
FILE *fopen(const char *filename, const char *mode);

// Common modes:
// "r" - read (file must exist)
// "w" - write (creates/overwrites)
// "a" - append (creates if not exists)
// "r+" - read and write
// "rb", "wb" - binary read/write

9.2 Writing to Files


#include <stdio.h>

int main() {
FILE *fp = fopen("[Link]", "w");

if (fp == NULL) {
printf("Cannot open file!\n");
return 1;
}

fprintf(fp, "Name: Alice\n");


fprintf(fp, "Score: %d\n", 95);

fclose(fp);
printf("File written successfully.\n");
return 0;
}

9.3 Reading from Files


#include <stdio.h>
int main() {
FILE *fp = fopen("[Link]", "r");
char line[100];

if (fp == NULL) {
printf("File not found!\n");
return 1;
}

while (fgets(line, sizeof(line), fp) != NULL) {


printf("%s", line);
}

fclose(fp);
return 0;
}

9.4 Binary File I/O


Binary files store data in the exact binary format used in memory. Use fwrite and fread for binary
I/O.
typedef struct { char name[50]; int age; float gpa; } Student;

// Write binary
FILE *fp = fopen("[Link]", "wb");
Student s = {"Alice", 20, 3.85};
fwrite(&s, sizeof(Student), 1, fp);
fclose(fp);

// Read binary
fp = fopen("[Link]", "rb");
Student loaded;
fread(&loaded, sizeof(Student), 1, fp);
printf("%s, Age %d, GPA %.2f\n", [Link], [Link], [Link]);
fclose(fp);
Chapter 10: The Preprocessor
10.1 Preprocessor Directives
The C preprocessor processes source code before compilation. It handles #include, #define, #if,
and other directives. These lines start with # and are not C statements.

10.2 Macros with #define


// Object-like macros (constants)
#define PI 3.14159265
#define BUFFER_SIZE 1024
#define TRUE 1
#define FALSE 0

// Function-like macros
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define ABS(x) ((x) < 0 ? -(x) : (x))

// Usage
printf("PI = %.5f\n", PI);
printf("5 squared = %d\n", SQUARE(5)); // 25
printf("Max(3,7) = %d\n", MAX(3, 7)); // 7

Warning: Always wrap macro parameters and the entire macro in parentheses.
SQUARE(2+3) without parens gives 2+3*2+3 = 11 instead of 25.

10.3 Conditional Compilation


#define DEBUG_MODE

#ifdef DEBUG_MODE
printf("DEBUG: x = %d\n", x);
#endif

// Include guard to prevent double inclusion


#ifndef MYHEADER_H
#define MYHEADER_H

// Header content goes here...

#endif

10.4 Creating Header Files


Header files (.h) contain function declarations, macro definitions, and type definitions. They are
included with #include. Example — mymath.h:
// mymath.h
#ifndef MYMATH_H
#define MYMATH_H

#define PI 3.14159

float circle_area(float r);


float circle_perimeter(float r);
int is_prime(int n);

#endif

// mymath.c
#include "mymath.h"

float circle_area(float r) { return PI * r * r; }


float circle_perimeter(float r) { return 2 * PI * r; }
Chapter 11: Data Structures in C
11.1 Linked List
A linked list is a dynamic data structure where each element (node) contains data and a pointer
to the next node. Unlike arrays, linked lists can grow and shrink at runtime.
#include <stdio.h>
#include <stdlib.h>

typedef struct Node {


int data;
struct Node *next;
} Node;

// Create a new node


Node* create_node(int val) {
Node *n = (Node*)malloc(sizeof(Node));
n->data = val;
n->next = NULL;
return n;
}

// Insert at beginning
Node* push_front(Node *head, int val) {
Node *n = create_node(val);
n->next = head;
return n;
}

// Print the list


void print_list(Node *head) {
while (head != NULL) {
printf("%d -> ", head->data);
head = head->next;
}
printf("NULL\n");
}

// Free all nodes


void free_list(Node *head) {
while (head != NULL) {
Node *temp = head;
head = head->next;
free(temp);
}
}

11.2 Stack (Array-Based)


A stack follows Last-In, First-Out (LIFO) order. Think of a stack of plates.
#define MAX 100

typedef struct {
int data[MAX];
int top;
} Stack;

void stack_init(Stack *s) { s->top = -1; }


int stack_empty(Stack *s) { return s->top == -1; }
void stack_push(Stack *s, int val) { s->data[++s->top] = val; }
int stack_pop(Stack *s) { return s->data[s->top--]; }
int stack_peek(Stack *s) { return s->data[s->top]; }

11.3 Queue (Circular Array)


A queue follows First-In, First-Out (FIFO) order. Think of a line at a ticket counter.
#define MAX 100

typedef struct {
int data[MAX];
int front, rear, size;
} Queue;

void queue_init(Queue *q) { q->front = 0; q->rear = 0; q->size = 0; }


int queue_empty(Queue *q) { return q->size == 0; }

void enqueue(Queue *q, int val) {


q->data[q->rear] = val;
q->rear = (q->rear + 1) % MAX;
q->size++;
}

int dequeue(Queue *q) {


int val = q->data[q->front];
q->front = (q->front + 1) % MAX;
q->size--;
return val;
}

11.4 Binary Search Tree


A Binary Search Tree (BST) is a tree where each node's left child is smaller and right child is
greater. This enables fast search, insert, and delete operations.
typedef struct BST {
int key;
struct BST *left, *right;
} BST;

BST* bst_insert(BST *root, int key) {


if (root == NULL) {
BST *n = (BST*)malloc(sizeof(BST));
n->key = key; n->left = n->right = NULL;
return n;
}
if (key < root->key) root->left = bst_insert(root->left, key);
else root->right = bst_insert(root->right, key);
return root;
}

// In-order traversal (gives sorted output)


void inorder(BST *root) {
if (root != NULL) {
inorder(root->left);
printf("%d ", root->key);
inorder(root->right);
}
}
Chapter 12: Sorting and Searching Algorithms
12.1 Bubble Sort
Bubble sort repeatedly compares adjacent elements and swaps them if they are in the wrong
order. Simple but O(n^2) — slow for large arrays.
void bubble_sort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}

12.2 Selection Sort


Selection sort finds the minimum element in the unsorted portion and places it at the beginning.
O(n^2).
void selection_sort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
int min_idx = i;
for (int j = i + 1; j < n; j++) {
if (arr[j] < arr[min_idx]) min_idx = j;
}
int temp = arr[min_idx];
arr[min_idx] = arr[i];
arr[i] = temp;
}
}

12.3 Insertion Sort


Insertion sort builds the sorted array one element at a time. Efficient for small or nearly-sorted
data. O(n^2) worst case, O(n) best case.
void insertion_sort(int arr[], int n) {
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}

12.4 Merge Sort


Merge sort divides the array in half, sorts each half, then merges them. O(n log n) — efficient
and stable.
void merge(int arr[], int l, int m, int r) {
int n1 = m - l + 1, n2 = r - m;
int L[n1], R[n2];
for (int i = 0; i < n1; i++) L[i] = arr[l + i];
for (int j = 0; j < n2; j++) R[j] = arr[m + 1 + j];
int i = 0, j = 0, k = l;
while (i < n1 && j < n2)
arr[k++] = (L[i] <= R[j]) ? L[i++] : R[j++];
while (i < n1) arr[k++] = L[i++];
while (j < n2) arr[k++] = R[j++];
}

void merge_sort(int arr[], int l, int r) {


if (l < r) {
int m = (l + r) / 2;
merge_sort(arr, l, m);
merge_sort(arr, m + 1, r);
merge(arr, l, m, r);
}
}

12.5 Binary Search


Binary search finds a target in a sorted array by repeatedly halving the search space. O(log n).
int binary_search(int arr[], int n, int target) {
int low = 0, high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] == target) return mid;
else if (arr[mid] < target) low = mid + 1;
else high = mid - 1;
}
return -1; // Not found
}
Chapter 13: Advanced Topics
13.1 Bit Manipulation
C allows direct manipulation of individual bits using bitwise operators. This is essential in
systems programming, embedded development, and performance-critical code.
int a = 0b1010; // 10 in decimal
int b = 0b1100; // 12 in decimal

printf("%d\n", a & b); // AND: 1000 = 8


printf("%d\n", a | b); // OR: 1110 = 14
printf("%d\n", a ^ b); // XOR: 0110 = 6
printf("%d\n", ~a); // NOT: ...11110101
printf("%d\n", a << 1); // Left shift: 10100 = 20
printf("%d\n", a >> 1); // Right shift: 0101 = 5

// Common bit tricks:


// Check if bit n is set:
if (a & (1 << n)) { /* bit n is set */ }
// Set bit n:
a |= (1 << n);
// Clear bit n:
a &= ~(1 << n);
// Toggle bit n:
a ^= (1 << n);

13.2 Function Pointers


In C, functions have addresses in memory. A function pointer stores a function's address and
can be used to call the function dynamically. This enables callbacks, dispatch tables, and
plugins.
// Declare a function pointer
int (*operation)(int, int);

int add(int a, int b) { return a + b; }


int sub(int a, int b) { return a - b; }
int mult(int a, int b) { return a * b; }

int main() {
// Array of function pointers
int (*ops[])(int, int) = { add, sub, mult };
char *names[] = { "add", "sub", "mult" };

for (int i = 0; i < 3; i++) {


printf("%s(10, 3) = %d\n", names[i], ops[i](10, 3));
}
return 0;
}

13.3 Variable Arguments (stdarg.h)


Functions can accept a variable number of arguments using the <stdarg.h> library. This is how
printf works.
#include <stdarg.h>

int sum(int count, ...) {


va_list args;
va_start(args, count);
int total = 0;
for (int i = 0; i < count; i++) {
total += va_arg(args, int);
}
va_end(args);
return total;
}

int main() {
printf("%d\n", sum(3, 10, 20, 30)); // 60
printf("%d\n", sum(5, 1, 2, 3, 4, 5)); // 15
return 0;
}

13.4 Command-Line Arguments


C programs can receive arguments from the command line via argc (argument count) and argv
(argument vector).
int main(int argc, char *argv[]) {
printf("Program: %s\n", argv[0]);
printf("Arguments: %d\n", argc - 1);

for (int i = 1; i < argc; i++) {


printf(" Arg %d: %s\n", i, argv[i]);
}
return 0;
}

// Usage: ./program hello world


// Output:
// Program: ./program
// Arguments: 2
// Arg 1: hello
// Arg 2: world

13.5 Error Handling


C uses the errno global variable and the perror/strerror functions for error reporting. Functions
typically return -1 or NULL on error.
#include <errno.h>
#include <string.h>

FILE *fp = fopen("[Link]", "r");


if (fp == NULL) {
fprintf(stderr, "Error: %s\n", strerror(errno));
// Or simply:
perror("fopen failed");
return 1;
}
Chapter 14: Best Practices & Real-World C
14.1 Writing Clean C Code
• Use meaningful variable names: totalScore instead of ts.
• Keep functions short and focused — one function, one purpose.
• Comment why, not what — explain intent, not obvious mechanics.
• Use constants (#define or const) for magic numbers.
• Always check return values of malloc, fopen, scanf, etc.
• Avoid global variables where possible — pass data as parameters.

14.2 Common C Bugs and How to Avoid Them


The most common bugs in C programs are:
• Buffer overflow — writing beyond array bounds. Always check bounds.
• Off-by-one errors — loop conditions like i <= n instead of i < n.
• Uninitialized variables — always initialize variables when declaring.
• Memory leaks — every malloc needs a free. Use tools like Valgrind.
• Dangling pointers — after free(ptr), set ptr = NULL.
• Format string vulnerabilities — never use user input directly as a printf format string. Use
printf("%s", user_input) not printf(user_input).

14.3 Debugging Tools


Several tools help find bugs in C programs:
• GDB (GNU Debugger) — step through code, set breakpoints, inspect variables. Compile
with 'gcc -g -o program program.c', then run 'gdb ./program'.
• Valgrind — detects memory leaks and invalid memory access. Run with 'valgrind --leak-
check=full ./program'.
• AddressSanitizer — compile with 'gcc -fsanitize=address -o program program.c' to
detect memory errors at runtime.
• Static analysis — tools like clang-tidy and cppcheck find bugs without running the
program.

14.4 Compiling with Warnings


Always compile with warning flags. Warnings often point to real bugs:
gcc -Wall -Wextra -Werror -std=c11 -o program program.c
// -Wall : Enable common warnings
// -Wextra : Enable extra warnings
// -Werror : Treat warnings as errors
// -std=c11 : Use the C11 standard
// -g : Include debug symbols (for GDB)
// -O2 : Optimization level 2

14.5 Multi-File Projects and Makefiles


Real projects span multiple files. A Makefile automates compilation:
# Makefile
CC = gcc
CFLAGS = -Wall -Wextra -std=c11

TARGET = myapp
SRCS = main.c utils.c math.c
OBJS = $(SRCS:.c=.o)

$(TARGET): $(OBJS)
\t$(CC) $(OBJS) -o $(TARGET)

%.o: %.c
\t$(CC) $(CFLAGS) -c $< -o $@

clean:
\trm -f $(OBJS) $(TARGET)

Run: Type 'make' to build the project and 'make clean' to remove compiled files.
Appendix A: Quick Reference
A.1 Format Specifiers
%d or %i - int
%f - float
%lf - double
%c - char
%s - string (char array)
%p - pointer address
%x - hexadecimal
%o - octal
%u - unsigned int
%ld - long int
%lld - long long int
%zu - size_t
%.2f - float with 2 decimal places
%5d - integer with width 5
%-5d - left-aligned width 5

A.2 Escape Sequences


\n - newline
\t - horizontal tab
\r - carriage return
\\ - backslash
\' - single quote
\" - double quote
\0 - null character
\a - alert (bell)
\b - backspace

A.3 Standard Library Headers


#include <stdio.h> // printf, scanf, fopen, fclose, fgets
#include <stdlib.h> // malloc, free, exit, rand, atoi
#include <string.h> // strcpy, strlen, strcmp, strcat, memcpy
#include <math.h> // sqrt, pow, sin, cos, log (link with -lm)
#include <ctype.h> // isalpha, isdigit, toupper, tolower
#include <time.h> // time, clock, struct tm
#include <stdbool.h> // bool, true, false (C99)
#include <stdint.h> // int8_t, uint32_t, int64_t (C99)
#include <assert.h> // assert()
#include <errno.h> // errno, ENOENT, EINVAL
#include <limits.h> // INT_MAX, INT_MIN, CHAR_MAX

A.4 Recommended Practice Projects


The best way to cement your C skills is to build projects. Here are some ideas by difficulty:

Beginner:
• Calculator — read two numbers and an operator, compute the result.
• Unit converter — convert temperature, length, weight.
• Guessing game — generate a random number, let user guess.
• Simple contact book — read/write contacts to a text file.

Intermediate:
• Student grade manager — store/sort/search student records in a file.
• Matrix operations — add, multiply, transpose matrices.
• Text statistics — count words, sentences, and characters in a file.
• Stack-based calculator — evaluate postfix expressions.

Advanced:
• Mini shell — parse and execute commands like a Unix shell.
• HTTP server — serve static HTML files over TCP sockets.
• Compiler/interpreter — build a simple expression evaluator.
• Memory allocator — implement your own malloc/free using sbrk.
Final Words
Congratulations on reaching the end of Mastering C Programming! You have traveled from your
very first 'Hello, World!' to dynamic memory management, data structures, file I/O, and system-
level programming.

C is a language that rewards patience and curiosity. It does not hide what is happening under
the hood — and that is precisely what makes it so valuable. Every time you write a pointer,
manage memory manually, or use a struct, you are thinking at the level of the machine. This
perspective will serve you for the rest of your programming career, regardless of what
languages you work with.

Keep coding, keep experimenting, and never stop learning. The best C programmer is one who
understands not just how to use the language, but why it works the way it does. Build projects,
read source code for real systems (Linux kernel, Redis, SQLite), and engage with the
community.

Good luck on your journey!

— End of Book —

You might also like