C PROGRAMMING
Complete Learning Guide
From Basics to Advanced — With Code Examples
Covers: Syntax • Data Types • Control Flow • Functions • Pointers • Structures • File I/O • Memory
Management
Chapter 1: Introduction to C Programming
C is a general-purpose, procedural programming language developed by Dennis Ritchie at Bell Labs in
1972. It remains one of the most widely used languages due to its speed, portability, and close-to-
hardware control.
1.1 Why Learn C?
• Foundation of modern languages (C++, Java, Python are inspired by C)
• Used in operating systems, embedded systems, and compilers
• Gives deep understanding of memory and computer architecture
• Extremely fast execution speed
• Highly portable across platforms
1.2 History & Versions
Standard Year Key Features
K&R C 1978 Original by Kernighan & Ritchie
ANSI C / C89 1989 First standardized version
C99 1999 // comments, inline, VLAs, stdint.h
C11 2011 Multi-threading support, _Generic
C17 2018 Bug fixes and clarifications
C23 2023 Latest: typeof, constexpr, #embed
1.3 Structure of a C Program
Every C program follows this basic structure:
#include <stdio.h> // Preprocessor directive (includes header file)
int main() { // main() is the entry point of every C program
printf("Hello, World!\n"); // Print to console
return 0; // Return 0 = success
}
NOTE: Every C program must have exactly one main() function. Execution always starts from main().
1.4 Compilation Process
C is a compiled language. Before running, source code goes through these stages:
1. Source Code (.c file) — written by programmer
2. Preprocessor — handles #include, #define directives
3. Compiler — converts C code to assembly
4. Assembler — converts assembly to object code (.o)
5. Linker — links object files and libraries into executable
6. Executable — the final runnable program
// Compile with GCC:
gcc program.c -o program // compiles and links
./program // run on Linux/Mac
[Link] // run on Windows
Chapter 2: Data Types & Variables
A variable is a named memory location that stores data. Every variable must be declared with a data
type before use.
2.1 Basic Data Types
Data Type Size Range Format Specifier Example
int 4 bytes -2,147,483,648 %d int x = 10;
to 2,147,483,647
short 2 bytes -32,768 to %hd short s = 100;
32,767
long 8 bytes Very large range %ld long l = 100000L;
float 4 bytes 7 decimal digits %f float f = 3.14f;
double 8 bytes 15 decimal %lf double d = 3.14;
digits
char 1 byte -128 to 127 / 0- %c char c = 'A';
255
_Bool 1 byte 0 or 1 %d _Bool b = 1;
void 0 bytes No value — void func() {}
2.2 Variable Declaration & Initialization
// Declaration (reserves memory)
int age;
float salary;
// Initialization (assigns value at declaration)
int age = 25;
float salary = 50000.75;
char grade = 'A';
// Multiple variables of same type
int x = 1, y = 2, z = 3;
// Constants (cannot change after declaration)
const float PI = 3.14159;
#define MAX 100 // Preprocessor constant
2.3 Type Modifiers
Modifier Effect Example
signed Allows negative values signed int x = -5;
(default)
unsigned Only non-negative unsigned int x = 200;
values
short Smaller range short int s = 100;
long Larger range long int l = 99999L;
Modifier Effect Example
long long Even larger range long long ll = 9999999LL;
2.4 Type Conversion
Implicit Conversion (done automatically by compiler):
int i = 5;
float f = i; // int automatically converted to float → 5.0
double d = 3.14;
int x = d; // double to int → loses decimal → 3 (data loss!)
Explicit Conversion (Type Casting — done by programmer):
int a = 7, b = 2;
float result = (float)a / b; // Cast a to float before division → 3.5
// Without cast: 7/2 = 3 (integer division!)
Chapter 3: Operators
Operators are symbols that perform operations on variables and values.
3.1 Arithmetic Operators
Operator Name Example Result
+ Addition 5 + 3 8
- Subtraction 5 - 3 2
* Multiplication 5 * 3 15
/ Division 7 / 2 (integers) 3 (truncated)
% Modulus 7 % 2 1
(remainder)
++ Increment a++ or ++a +1
-- Decrement a-- or --a -1
3.2 Relational Operators
Operator Meaning Example Result
== Equal to 5 == 5 1 (true)
!= Not equal to 5 != 3 1 (true)
> Greater than 7 > 3 1 (true)
< Less than 2 < 5 1 (true)
>= Greater than or 5 >= 5 1 (true)
equal
<= Less than or 3 <= 5 1 (true)
equal
3.3 Logical Operators
Operator Name Example Meaning
&& Logical AND a>0 && b>0 Both must be true
|| Logical OR a>0 || b>0 At least one must be
true
! Logical NOT !(a>0) Reverses truth value
3.4 Assignment Operators
Operator Example Equivalent To
= a = 5 a = 5
+= a += 3 a = a + 3
-= a -= 3 a = a - 3
*= a *= 2 a = a * 2
/= a /= 2 a = a / 2
%= a %= 3 a = a % 3
3.5 Bitwise Operators
// Operate on individual bits
int a = 5; // Binary: 0101
int b = 3; // Binary: 0011
a & b → 0001 (AND) // Both bits must be 1
a | b → 0111 (OR) // At least one bit must be 1
a ^ b → 0110 (XOR) // Bits must be different
~a → 1010 (NOT) // Flip all bits
a << 1 → 1010 (Left Shift) // Multiply by 2
a >> 1 → 0010 (Right Shift) // Divide by 2
3.6 Operator Precedence (Highest to Lowest)
Precedence Operators Associativity
1 (highest) () [] -> . Left to right
2 ! ~ ++ -- (type) * & sizeof Right to left
3 * / % Left to right
4 + - Left to right
5 << >> Left to right
6 < <= > >= Left to right
7 == != Left to right
8–12 & ^ | && || Left to right
13 ?: Right to left
14 (lowest) = += -= etc. Right to left
Chapter 4: Control Flow Statements
Control flow statements determine the order in which code executes.
4.1 if / else if / else
int marks = 75;
if (marks >= 90) {
printf("Grade A\n");
} else if (marks >= 80) {
printf("Grade B\n");
} else if (marks >= 70) {
printf("Grade C\n");
} else if (marks >= 60) {
printf("Grade D\n");
} else {
printf("Fail\n");
}
// Output: Grade C
4.2 Ternary Operator
// Syntax: condition ? value_if_true : value_if_false
int a = 10, b = 20;
int max = (a > b) ? a : b; // max = 20
printf("%d\n", max);
4.3 switch Statement
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;
}
// Output: Wednesday
NOTE: Always use 'break' in switch cases. Without it, execution 'falls through' to the next case!
4.4 while Loop
// Executes while condition is true
int i = 1;
while (i <= 5) {
printf("%d ", i);
i++;
}
// Output: 1 2 3 4 5
4.5 do-while Loop
// Executes at least ONCE, then checks condition
int i = 1;
do {
printf("%d ", i);
i++;
} while (i <= 5);
// Output: 1 2 3 4 5
// Key difference: even if condition is false at start, body runs once!
int x = 10;
do { printf("Runs once!"); } while (x < 5);
4.6 for Loop
// Syntax: for (initialization; condition; update)
for (int i = 1; i <= 5; i++) {
printf("%d ", i);
}
// Output: 1 2 3 4 5
// Nested for loop (multiplication table)
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 3; j++) {
printf("%d ", i * j);
}
printf("\n");
}
4.7 break & continue
// break — exits the loop immediately
for (int i = 1; i <= 10; i++) {
if (i == 5) break;
printf("%d ", i); // Output: 1 2 3 4
}
// continue — skips current iteration
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) continue; // skip even numbers
printf("%d ", i); // Output: 1 3 5 7 9
}
Chapter 5: Arrays
An array is a collection of elements of the same data type stored in contiguous memory locations,
accessed by index.
5.1 1D Arrays
// Declaration and Initialization
int numbers[5]; // declares array of 5 ints
int scores[5] = {90, 85, 78, 92, 88}; // initialized
int marks[] = {75, 80, 65}; // size auto-determined = 3
// Accessing elements (0-indexed!)
printf("%d\n", scores[0]); // 90 (first element)
printf("%d\n", scores[4]); // 88 (last element)
// Modifying elements
scores[2] = 95; // change 78 to 95
// Traversing with for loop
for (int i = 0; i < 5; i++) {
printf("scores[%d] = %d\n", i, scores[i]);
}
5.2 2D Arrays (Matrix)
// Syntax: type name[rows][cols]
int matrix[3][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// Access element at row 1, col 2
printf("%d\n", matrix[1][2]); // Output: 6
// Print full matrix
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
5.3 Strings (Character Arrays)
// A string is a char array ending with '\0' (null terminator)
char name[] = "Alice"; // Stored as: A l i c e \0
char city[20] = "Lahore";
// Input/Output
printf("%s", name); // Print string
scanf("%s", name); // Read string (no spaces)
fgets(name, 50, stdin); // Read with spaces (safer)
// String functions (need #include <string.h>)
strlen(name) // Length of string
strcpy(dest, src) // Copy src into dest
strcat(str1, str2) // Append str2 to str1
strcmp(s1, s2) // Compare: 0 if equal
strupr(str) // Convert to uppercase
strlwr(str) // Convert to lowercase
Chapter 6: Functions
A function is a block of code that performs a specific task. Functions promote code reuse, modularity,
and readability.
6.1 Function Syntax
// Structure of a function:
return_type function_name(parameter_type param1, ...) {
// function body
return value; // if return_type is not void
}
// Example:
int add(int a, int b) { // Function definition
return a + b;
}
int main() {
int sum = add(3, 4); // Function call
printf("%d\n", sum); // Output: 7
return 0;
}
6.2 Function Prototype (Declaration)
// Declare before main(), define after
#include <stdio.h>
float area(float r); // Function prototype (declaration)
int main() {
printf("Area: %.2f\n", area(5.0));
return 0;
}
float area(float r) { // Function definition
return 3.14159 * r * r;
}
6.3 Call by Value vs Call by Reference
// CALL BY VALUE — changes do not affect original
void swap_val(int a, int b) {
int temp = a; a = b; b = temp; // Only local copies swapped!
}
// CALL BY REFERENCE — changes affect original (using pointers)
void swap_ref(int *a, int *b) {
int temp = *a; *a = *b; *b = temp; // Original values swapped!
}
int main() {
int x = 5, y = 10;
swap_val(x, y); // x=5, y=10 (unchanged)
swap_ref(&x, &y); // x=10, y=5 (CHANGED)
}
6.4 Recursion
// A function that calls itself
int factorial(int n) {
if (n == 0 || n == 1) // Base case
return 1;
return n * factorial(n - 1); // Recursive call
}
// factorial(5) = 5 * 4 * 3 * 2 * 1 = 120
// Fibonacci using recursion
int fib(int n) {
if (n <= 1) return n;
return fib(n-1) + fib(n-2);
}
NOTE: Always ensure recursion has a base case, otherwise it causes infinite recursion and stack
overflow!
6.5 Built-in Math Functions (#include <math.h>)
Function Description Example Result
sqrt(x) Square root sqrt(25) 5.0
pow(x, y) x to the power y pow(2, 8) 256.0
abs(x) Absolute value abs(-7) 7
(int)
fabs(x) Absolute value fabs(-3.5) 3.5
(float)
ceil(x) Round up ceil(4.2) 5.0
floor(x) Round down floor(4.9) 4.0
round(x) Round to nearest round(4.5) 5.0
log(x) Natural log (ln) log(2.718) ~1.0
sin/cos/tan Trig functions sin(0) 0.0
Chapter 7: Pointers
A pointer is a variable that stores the memory address of another variable. Pointers are one of the most
powerful and distinctive features of C.
7.1 Pointer Basics
int x = 10;
int *ptr; // Declare a pointer to int
ptr = &x; // & operator gets address of x
printf("%d\n", x); // Value of x: 10
printf("%p\n", ptr); // Address stored in ptr (e.g. 0x7fff5...)
printf("%d\n", *ptr); // Dereference: value AT address = 10
// Modify value through pointer
*ptr = 99; // x is now 99!
printf("%d\n", x); // Output: 99
7.2 Pointer Arithmetic
int arr[] = {10, 20, 30, 40, 50};
int *p = arr; // Points to first element
printf("%d\n", *p); // 10
p++; // Move to next element
printf("%d\n", *p); // 20
p += 2; // Skip 2 elements
printf("%d\n", *p); // 40
// Traverse array using pointer
for (int *q = arr; q < arr + 5; q++) {
printf("%d ", *q); // 10 20 30 40 50
}
7.3 Pointer to Array, Pointer to Function
// Pointer to array
int (*p)[5]; // pointer to an array of 5 ints
// Pointer to function
int add(int a, int b) { return a + b; }
int (*fp)(int, int) = &add; // Function pointer
printf("%d\n", fp(3, 4)); // Call via pointer → 7
7.4 Null Pointer & Void Pointer
// NULL pointer — points to nothing (safe initialization)
int *ptr = NULL;
if (ptr == NULL) printf("Pointer is null\n");
// void pointer — can point to any type
void *vptr;
int x = 5;
float f = 3.14;
vptr = &x; // OK
vptr = &f; // OK
// Must cast before dereferencing: *(int*)vptr or *(float*)vptr
Chapter 8: Structures & Unions
8.1 Structures
A structure groups related variables of different data types under one name.
#include <stdio.h>
#include <string.h>
// Define structure
struct Student {
int rollNo;
char name[50];
float marks;
};
int main() {
struct Student s1; // Declare struct variable
[Link] = 101;
strcpy([Link], "Ahmed");
[Link] = 88.5;
printf("Roll: %d\n", [Link]);
printf("Name: %s\n", [Link]);
printf("Marks: %.1f\n", [Link]);
// Array of structures
struct Student class[30];
class[0].rollNo = 1;
return 0;
}
8.2 typedef with Structures
// typedef creates an alias so you don't need 'struct' keyword
typedef struct {
int id;
char name[50];
float salary;
} Employee; // Employee is now a type
Employee e1; // No need to write 'struct Employee'
[Link] = 1001;
strcpy([Link], "Ali");
[Link] = 75000.0;
8.3 Pointer to Structure
struct Point { int x; int y; };
struct Point p1 = {3, 7};
struct Point *ptr = &p1;
// Two ways to access members via pointer:
printf("%d\n", (*ptr).x); // Method 1: dereference then member
printf("%d\n", ptr->x); // Method 2: arrow operator (preferred)
printf("%d\n", ptr->y); // Output: 7
8.4 Unions
A union is like a structure, but all members share the same memory location. Only one member can
hold a value at a time.
union Data {
int i;
float f;
char c;
};
union Data d;
d.i = 65; // Store int
printf("%d\n", d.i); // 65
printf("%c\n", d.c); // A (same memory = ASCII 65)
// Size of union = size of largest member
printf("%zu\n", sizeof(d)); // 4 bytes (size of float/int)
Chapter 9: Dynamic Memory Management
C allows you to allocate memory at runtime using the heap. This requires manual management.
9.1 Memory Functions (#include <stdlib.h>)
Function Purpose Returns
malloc(size) Allocates bytes; content void* or NULL on failure
undefined
calloc(n, size) Allocates n*size bytes; void* or NULL on failure
zero-initialized
realloc(ptr, Resize previously allocated void* or NULL on failure
size) block
Function Purpose Returns
free(ptr) Releases allocated memory void
9.2 malloc & free
#include <stdio.h>
#include <stdlib.h>
int main() {
int n = 5;
int *arr = (int*)malloc(n * sizeof(int)); // Allocate 20 bytes
if (arr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
for (int i = 0; i < n; i++)
arr[i] = i * 10;
for (int i = 0; i < n; i++)
printf("%d ", arr[i]); // 0 10 20 30 40
free(arr); // ALWAYS free when done!
arr = NULL; // Avoid dangling pointer
return 0;
}
NOTE: Never forget to call free()! Forgetting causes memory leaks. Never access memory after freeing
(dangling pointer).
9.3 Common Memory Errors
Error Description Prevention
Memory Leak Allocated memory never Always call free()
freed
Dangling Accessing freed memory Set pointer to NULL after free()
Pointer
Buffer Writing beyond array Check bounds carefully
Overflow bounds
Double Free Freeing same memory Set pointer to NULL after free()
twice
NULL Dereferencing NULL Check if pointer is NULL before
Dereference pointer use
Chapter 10: File Input/Output
C provides functions to read from and write to files on disk using file pointers.
10.1 File Modes
Mode Meaning File Exists? File Missing?
"r" Read only Opens Error
"w" Write only Overwrites Creates
"a" Append Appends to end Creates
"r+" Read and write Opens Error
"w+" Read and write Overwrites Creates
"rb", "wb" Binary Same as above —
read/write
10.2 Writing to a File
#include <stdio.h>
int main() {
FILE *fp = fopen("[Link]", "w"); // Open for writing
if (fp == NULL) {
printf("Cannot open file!\n");
return 1;
}
fprintf(fp, "Name: Ali\n");
fprintf(fp, "Age: 20\n");
fputs("Hello, File!\n", fp);
fclose(fp); // ALWAYS close the file!
return 0;
}
10.3 Reading from a File
#include <stdio.h>
int main() {
FILE *fp = fopen("[Link]", "r");
char line[100];
if (fp == NULL) { printf("File not found!\n"); return 1; }
// Method 1: Read line by line
while (fgets(line, sizeof(line), fp) != NULL) {
printf("%s", line);
}
// Method 2: Read character by character
int ch;
while ((ch = fgetc(fp)) != EOF) {
putchar(ch);
}
fclose(fp);
return 0;
}
Chapter 11: Preprocessor Directives
Preprocessor directives are processed before compilation. They begin with #.
11.1 Common Directives
#include <stdio.h> // Include standard library header
#include "myfile.h" // Include user-defined header
#define PI 3.14159 // Macro constant
#define MAX(a,b) ((a)>(b)?(a):(b)) // Macro function
#undef PI // Undefine a macro
// Conditional compilation
#ifdef DEBUG
printf("Debug mode\n");
#endif
#ifndef HEADER_H // Include guard pattern
#define HEADER_H
// ... header contents ...
#endif
11.2 Predefined Macros
Macro Description Example Output
__FILE__ Current filename "main.c"
__LINE__ Current line number 42
__DATE__ Compilation date "Jun 14 2026"
__TIME__ Compilation time "10:30:00"
__STDC__ Is ANSI C standard 1
Chapter 12: Common Exam Programs
12.1 Prime Number Check
int isPrime(int n) {
if (n < 2) return 0;
for (int i = 2; i * i <= n; i++)
if (n % i == 0) return 0;
return 1;
}
// Print all primes from 1 to 100
for (int i = 2; i <= 100; i++)
if (isPrime(i)) printf("%d ", i);
12.2 Fibonacci Series
// Iterative approach
int a = 0, b = 1, next;
printf("%d %d ", a, b);
for (int i = 2; i < 10; i++) {
next = a + b;
printf("%d ", next);
a = b; b = next;
}
// Output: 0 1 1 2 3 5 8 13 21 34
12.3 Bubble Sort
void bubbleSort(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;
}
}
}
}
int main() {
int arr[] = {64, 34, 25, 12, 22};
int n = 5;
bubbleSort(arr, n);
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
// Output: 12 22 25 34 64
}
12.4 Reverse a String
#include <string.h>
void reverseString(char str[]) {
int n = strlen(str);
for (int i = 0; i < n / 2; i++) {
char temp = str[i];
str[i] = str[n - i - 1];
str[n - i - 1] = temp;
}
}
char s[] = "Hello";
reverseString(s);
printf("%s\n", s); // Output: olleH
12.5 Linked List (Basic)
struct Node {
int data;
struct Node *next;
};
// Create a new node
struct Node* newNode(int val) {
struct Node *n = malloc(sizeof(struct Node));
n->data = val;
n->next = NULL;
return n;
}
// Print linked list
void printList(struct Node *head) {
while (head != NULL) {
printf("%d -> ", head->data);
head = head->next;
}
printf("NULL\n");
}
Quick Reference Sheet
Format Specifiers
Specifier Type Example
%d or %i int printf("%d", 42)
%f float printf("%f", 3.14)
%.2f float (2 decimal printf("%.2f", 3.14159) → 3.14
places)
%lf double scanf("%lf", &d)
%c char printf("%c", 'A')
%s string printf("%s", "hello")
%p pointer address printf("%p", ptr)
%o octal printf("%o", 8) → 10
%x hexadecimal printf("%x", 255) → ff
%u unsigned int printf("%u", 300u)
%ld long int printf("%ld", 100000L)
%zu size_t printf("%zu", sizeof(int))
Escape Sequences
Escape Meaning Effect
\n Newline Moves cursor to next line
\t Tab Horizontal tab (usually 8 spaces)
\r Carriage return Moves cursor to start of line
\\ Backslash Prints \ character
\' Single quote Prints ' inside char
\" Double quote Prints " inside string
\0 Null character String terminator
\a Alert/bell Plays beep sound
\b Backspace Moves cursor back one
— End of C Programming Guide —
Prepared with Claude AI • Abbottabad, KPK