📚 What is C Language?
C is a general-purpose, procedural programming language developed by Dennis Ritchie at Bell Labs in 1972. It was originally created to develop the UNIX operating system.
Why learn C?
- It is the foundation for C++, Java, Python, and many modern languages
- Operating systems (Windows, Linux, macOS), databases, and embedded systems are written in C
- Provides direct access to memory via pointers — gives you full control
- Very fast and efficient — close to hardware
Features of C: Structured language, rich library of built-in functions, portability (runs on any machine with a C compiler), extensible (you can add your own functions), case-sensitive (variable and Variable are different).
Structure of a C Program:
#include <stdio.h> // Preprocessor directive - includes standard I/O library
int main() { // main() - where program execution STARTS
printf("Hello!"); // printf() - prints output to screen
return 0; // Returns 0 to OS - means "program ended successfully"
}
Explanation of each part:
- #include <stdio.h> — tells the compiler to include functions like printf() and scanf(). stdio = Standard Input/Output. The .h means "header file".
- int main() — every C program MUST have a main() function. The "int" means it returns an integer. Execution always starts here.
- { } — curly braces define a block of code. Everything inside is part of main.
- printf("Hello!"); — built-in function that displays text. Every statement MUST end with a semicolon (;).
- return 0; — tells the operating system the program finished successfully.
Compilation Process:
📚 Variables, Data Types & Operators
A variable is a named container that stores a value in memory. Think of it as a labeled box.
int age = 20; // declares integer variable "age" with value 20 float gpa = 8.5; // declares decimal variable "gpa" char grade = 'A'; // declares character variable (single quotes!)
Rules for Variable Names:
- Can contain letters (a-z, A-Z), digits (0-9), and underscore (_)
- Must START with a letter or underscore (not a digit!)
- Cannot use reserved keywords (int, float, for, while, etc.)
- Case-sensitive: "Age" and "age" are DIFFERENT variables
Data Types (how much memory each uses):
| Type | Size | Range (approximate) | Format Specifier | Use For |
|---|---|---|---|---|
| int | 4 bytes | -2 billion to +2 billion | %d | Whole numbers: age, count, marks |
| float | 4 bytes | ±3.4 × 10³⁸ (6-7 decimal digits) | %f | Decimals: price, temperature |
| double | 8 bytes | ±1.7 × 10³⁰⁸ (15-16 digits) | %lf | High precision decimals |
| char | 1 byte | -128 to 127 / 0 to 255 | %c | Single character: 'A', '9', '#' |
| short | 2 bytes | -32768 to 32767 | %hd | Small integers |
| long | 8 bytes | Very large numbers | %ld | Large integers |
Input & Output:
int age;
printf("Enter your age: "); // Display prompt to user
scanf("%d", &age); // Read user input into variable 'age'
// & is REQUIRED - it gives the ADDRESS of variable
printf("You are %d years old\n", age); // Display with variable value
// \n = newline (move to next line)
// \t = tab space
// \\ = print backslash
// \" = print quote mark
Operators (Complete List):
| Category | Operators | Examples |
|---|---|---|
| Arithmetic | + - * / % | 10/3=3 (integer division!), 10%3=1 (remainder) |
| Relational | == != < > <= >= | 5==5 (true=1), 5!=3 (true=1), 5>3 (true=1) |
| Logical | && || ! | (5>3 && 2<4) = true, !(5>3) = false |
| Assignment | = += -= *= /= %= | x+=5 means x=x+5 |
| Increment/Decrement | ++ -- | i++ (use then add), ++i (add then use) |
| Bitwise | & | ^ ~ << >> | 5&3=1, 5|3=7, 5^3=6 |
| Ternary | ? : | result = (a>b) ? a : b; // if a>b then a, else b |
| sizeof | sizeof() | sizeof(int) = 4 |
📚 Decision Making (if, else, switch)
Decision-making statements let your program choose which code to execute based on conditions.
1. if statement — run code only IF condition is true:
int marks = 85;
if (marks >= 90) {
printf("Grade A");
} else if (marks >= 80) { // checked only if above condition was false
printf("Grade B");
} else if (marks >= 60) {
printf("Grade C");
} else { // if none of the above are true
printf("Grade F");
}
// Output: Grade B (because 85 >= 80 is true)
2. switch statement — choose from multiple fixed options:
int day = 3;
switch (day) {
case 1: printf("Monday"); break;
case 2: printf("Tuesday"); break;
case 3: printf("Wednesday"); break; // This executes!
case 4: printf("Thursday"); break;
default: printf("Invalid day"); // If no case matches
}
// Output: Wednesday
📚 Loops (for, while, do-while)
Loops repeat a block of code multiple times until a condition becomes false.
1. for loop — when you know HOW MANY times to repeat:
// init; condition; update
for (int i = 1; i <= 5; i++) {
printf("%d ", i);
}
// Output: 1 2 3 4 5
// How it works: i starts at 1. Each time, check i<=5. If true, print and add 1 to i.
2. while loop — when you DON'T know how many times (check condition FIRST):
int i = 1;
while (i <= 5) { // condition checked BEFORE each iteration
printf("%d ", i);
i++; // must update manually or infinite loop!
}
// Output: 1 2 3 4 5
3. do-while loop — executes AT LEAST ONCE (checks condition AFTER):
int i = 10;
do {
printf("%d ", i); // This executes at least once, even though 10 > 5!
i++;
} while (i <= 5);
// Output: 10 (executes once then stops because 11 <= 5 is false)
break vs continue:
// break - EXIT the loop immediately
for (int i = 1; i <= 10; i++) {
if (i == 5) break; // Stop when i reaches 5
printf("%d ", i);
}
// Output: 1 2 3 4
// continue - SKIP current iteration, go to next
for (int i = 1; i <= 5; i++) {
if (i == 3) continue; // Skip when i is 3
printf("%d ", i);
}
// Output: 1 2 4 5 (3 is skipped!)
Nested Loops (loop inside a loop):
// Multiplication Table
for (int i = 1; i <= 3; i++) { // outer: rows
for (int j = 1; j <= 3; j++) { // inner: columns
printf("%d*%d=%d\t", i, j, i*j);
}
printf("\n");
}
// Output:
// 1*1=1 1*2=2 1*3=3
// 2*1=2 2*2=4 2*3=6
// 3*1=3 3*2=6 3*3=9
📚 Functions — Reusable Blocks of Code
A function is a self-contained block of code that performs a specific task. Instead of writing the same code multiple times, you write it once in a function and call it whenever needed.
Why Functions?
- Reusability: Write once, use many times
- Modularity: Break large problems into smaller, manageable pieces
- Readability: Easier to understand and debug
- Maintenance: Fix in one place instead of everywhere
Function Syntax:
// DECLARATION (tells compiler the function exists)
return_type function_name(parameter_type param1, parameter_type param2);
// DEFINITION (the actual code)
return_type function_name(parameter_type param1, parameter_type param2) {
// body of code
return value; // send result back (skip if return_type is void)
}
// CALL (using the function)
result = function_name(arg1, arg2);
Complete Example:
#include <stdio.h>
// Declaration (prototype) — tells compiler about function before main()
int add(int a, int b);
int main() {
int result = add(10, 20); // CALL - passes 10 and 20 as arguments
printf("Sum = %d\n", result); // Output: Sum = 30
return 0;
}
// Definition
int add(int a, int b) { // a and b are PARAMETERS (receive values)
return a + b; // Sends result back to caller
}
Call by Value vs Call by Reference:
| Call by Value | Call by Reference (using pointers) |
|---|---|
| Copies of values are passed | Addresses (pointers) are passed |
| Changes inside function DON'T affect original | Changes inside function DO affect original |
| void swap(int a, int b) | void swap(int *a, int *b) |
// Call by Reference — ACTUALLY swaps values
void swap(int *a, int *b) { // receives addresses
int temp = *a; // *a means "value at address a"
*a = *b;
*b = temp;
}
int main() {
int x=10, y=20;
swap(&x, &y); // pass addresses using &
printf("x=%d, y=%d", x, y); // Output: x=20, y=10 (actually swapped!)
}
Recursion — A function calling itself:
// Factorial: n! = n * (n-1)!
int factorial(int n) {
if (n == 0 || n == 1) // BASE CASE - stops the recursion
return 1;
return n * factorial(n-1); // RECURSIVE CALL
}
// factorial(5) = 5 * factorial(4) = 5 * 4 * factorial(3) = ... = 5*4*3*2*1 = 120
📚 Arrays — Storing Multiple Values
An array is a collection of elements of the same data type stored in contiguous (next to each other) memory locations. Instead of creating 100 separate variables, you create one array of 100 elements.
// Declaration and initialization
int marks[5] = {85, 90, 78, 92, 88}; // Array of 5 integers
// Accessing elements (INDEX STARTS FROM 0, not 1!)
printf("%d", marks[0]); // 85 (first element)
printf("%d", marks[4]); // 88 (last element)
// marks[5] = ERROR! Out of bounds (valid indices: 0 to 4)
// Modifying an element
marks[2] = 95; // Changes third element from 78 to 95
// Reading array from user
int arr[5];
for (int i = 0; i < 5; i++) {
printf("Enter element %d: ", i+1);
scanf("%d", &arr[i]); // Note: &arr[i] not &arr
}
// Printing array
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
2D Arrays (Matrix):
int matrix[3][3] = {
{1, 2, 3}, // Row 0
{4, 5, 6}, // Row 1
{7, 8, 9} // Row 2
};
printf("%d", matrix[1][2]); // 6 (row 1, column 2)
// Traverse entire 2D array
for (int i = 0; i < 3; i++) { // rows
for (int j = 0; j < 3; j++) { // columns
printf("%d ", matrix[i][j]);
}
printf("\n");
}
📚 Strings — Array of Characters
In C, a string is simply an array of characters terminated by a null character '\0'. The null character marks where the string ends.
// Different ways to declare strings
char name[20] = "Sajid"; // Compiler adds '\0' automatically
char name[] = "Sajid"; // Size auto-determined (6: S,a,j,i,d,\0)
char name[] = {'S','a','j','i','d','\0'}; // Manual way
// Reading strings
char name[50];
scanf("%s", name); // Reads until space (no & needed for arrays!)
gets(name); // Reads entire line including spaces
fgets(name, 50, stdin); // Safer — prevents buffer overflow
// Printing strings
printf("%s", name); // Prints until \0
puts(name); // Prints and adds newline
String Functions (from <string.h>):
| Function | Purpose | Example |
|---|---|---|
| strlen(s) | Returns length (excluding \0) | strlen("Hello") = 5 |
| strcpy(dest, src) | Copies src into dest | strcpy(name, "Sajid") |
| strcat(s1, s2) | Appends s2 to end of s1 | "Hello" + " World" = "Hello World" |
| strcmp(s1, s2) | Compares: returns 0 if equal | strcmp("abc","abc") = 0 |
| strrev(s) | Reverses the string | "Hello" → "olleH" |
| strupr(s) | Converts to uppercase | "hello" → "HELLO" |
| strlwr(s) | Converts to lowercase | "HELLO" → "hello" |
📚 Pointers — Understanding Memory Addresses
Every variable in C is stored at a specific memory address (like a house number). A pointer is a variable that stores the address of another variable instead of storing data directly.
int age = 25; // Normal variable - stores value 25
int *ptr = &age; // Pointer variable - stores ADDRESS of age
// Two key operators:
// & (address-of) → &age gives the address where age is stored
// * (dereference) → *ptr gives the value AT the address ptr holds
printf("Value of age: %d\n", age); // 25
printf("Address of age: %p\n", &age); // e.g., 0x7ffd3c4b (memory address)
printf("ptr holds address: %p\n", ptr); // Same address as &age
printf("Value at ptr: %d\n", *ptr); // 25 (same as age!)
// Changing value through pointer
*ptr = 30; // Changes value at that address
printf("%d\n", age); // 30! (age changed because ptr points to it)
Pointer Arithmetic:
int arr[] = {10, 20, 30, 40, 50};
int *p = arr; // Points to first element (arr is itself an address)
printf("%d\n", *p); // 10 (first element)
printf("%d\n", *(p+1)); // 20 (next element — moves by sizeof(int) bytes)
printf("%d\n", *(p+3)); // 40
// Traversing array with pointer
for (int i = 0; i < 5; i++) {
printf("%d ", *(p + i)); // same as arr[i]
}
Dynamic Memory Allocation:
#include <stdlib.h>
// malloc — allocate memory at runtime
int *arr = (int*)malloc(5 * sizeof(int)); // allocates space for 5 integers
if (arr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Use it like a normal array
for (int i = 0; i < 5; i++) {
arr[i] = (i+1) * 10; // arr: 10, 20, 30, 40, 50
}
free(arr); // ALWAYS free when done! Prevents memory leaks
arr = NULL; // Good practice — prevents dangling pointer
// calloc — like malloc but initializes all to 0
int *arr2 = (int*)calloc(5, sizeof(int)); // all 5 elements = 0
📚 Structures — Custom Data Types
A structure groups different data types under one name. Unlike arrays (same type), structures can hold mixed types.
// Define a structure
struct Student {
char name[50]; // string
int roll; // integer
float marks; // decimal
char grade; // single character
};
int main() {
// Create a variable of type Student
struct Student s1;
// Assign values using dot (.) operator
strcpy(s1.name, "Sajid"); // Must use strcpy for strings
s1.roll = 101;
s1.marks = 87.5;
s1.grade = 'A';
printf("Name: %s\n", s1.name);
printf("Roll: %d\n", s1.roll);
printf("Marks: %.1f\n", s1.marks);
// Array of structures
struct Student class[60]; // 60 student records!
class[0].roll = 1;
strcpy(class[0].name, "Rahul");
}
typedef — Create alias for easier typing:
typedef struct {
char name[50];
int age;
} Person;
Person p1; // Now you can use "Person" instead of "struct Person"
p1.age = 20;
📚 File Handling — Reading & Writing Files
Files let you store data permanently (variables are lost when program ends).
File Modes:
| Mode | Meaning | If file doesn't exist |
|---|---|---|
| "r" | Read only | Error (returns NULL) |
| "w" | Write only (overwrites!) | Creates new file |
| "a" | Append (add to end) | Creates new file |
| "r+" | Read + Write | Error |
| "w+" | Read + Write (overwrites!) | Creates new file |
#include <stdio.h>
int main() {
FILE *fp; // File pointer
// WRITE to a file
fp = fopen("data.txt", "w"); // Opens (or creates) file for writing
if (fp == NULL) { // Always check!
printf("Error opening file!\n");
return 1;
}
fprintf(fp, "Name: Sajid\n"); // fprintf writes to file (like printf)
fprintf(fp, "Age: 20\n");
fclose(fp); // ALWAYS close when done!
// READ from a file
char line[100];
fp = fopen("data.txt", "r");
while (fgets(line, 100, fp) != NULL) { // Read line by line
printf("%s", line); // Print each line
}
fclose(fp);
// Other useful functions:
// fgetc(fp) — read one character
// fputc('A', fp) — write one character
// fscanf(fp, "%d", &n) — read formatted data
// feof(fp) — check if end of file reached
return 0;
}