C Language


Beginners To Experts


The site is under development.

C Language Tutorial

1.1 History of C
C was developed by Dennis Ritchie at Bell Labs in 1972 and is a foundational programming language.
/* This program does not run; it's an info example */
// C language was created in early 1970s by Dennis Ritchie.
      

1.2 Characteristics of C
C is a procedural, compiled language combining low-level and high-level features.
/* Example of procedural style in C */
// Function to add two numbers
int add(int a, int b) {
    return a + b; // Return sum of a and b
}
      

1.3 Structure of a C Program
Basic structure includes headers, main function, and statements.
#include <stdio.h>          // Include standard input-output library

int main() {                  // Main function where execution starts
    printf("Hello, World!\n"); // Print message to console
    return 0;                 // Exit program with success code
}
      

1.4 Writing Your First Program
Write code, save as `.c`, compile with a compiler like gcc, then run.
/* First program to print Hello World */
#include <stdio.h>

int main() {
    printf("Hello, World!\n"); // Output text to screen
    return 0;                  // End of program
}
      

1.5 Compilation Process
Preprocessing, compiling, assembling, linking produce executable.
/* Command-line example */
// gcc program.c -o program
// Steps: preprocessing → compiling → assembling → linking
      

1.6 IDEs and Editors
Tools like Visual Studio Code, Code::Blocks, or Vim used to write and debug.
/* Example: Opening file in Vim */
// vim program.c
      

1.7 Hello World Explained
`#include <stdio.h>` includes I/O functions, `printf` prints output.
#include <stdio.h>

int main() {
    // Print Hello World to console
    printf("Hello, World!\n");
    return 0; // Program ends successfully
}
      

1.8 Comments in C
Single line `//`, multi-line `/* ... */` for explanations.
#include <stdio.h>

int main() {
    // Single line comment
    printf("Comments example\n"); /* This is a
                                    multi-line comment */
    return 0;
}
      

1.9 Basic Input/Output
Use `printf` for output, `scanf` for input.
#include <stdio.h>

int main() {
    int num;
    printf("Enter a number: ");       // Prompt user
    scanf("%d", &num);            // Read integer input
    printf("You entered: %d\n", num); // Display input
    return 0;
}
      

1.10 Running C Programs
Compile using `gcc file.c -o file`, then run with `./file`.
/* Terminal commands */
// gcc hello.c -o hello
// ./hello
      

2.1 Basic Data Types
int, char, float, double are fundamental types.
int age = 30;          // Integer type
char grade = 'A';      // Single character
float pi = 3.14f;      // Floating-point number
double e = 2.7182818;  // Double precision float
      

2.2 Variable Declaration and Initialization
Variables declared with type and optionally initialized.
int count;        // Declared without initial value
int total = 100;  // Declared with initial value
      

2.3 Constants
Use `const` keyword for fixed values.
const float TAX_RATE = 0.07f; // Constant value, cannot be changed
      

2.4 Modifiers
short, long, signed, unsigned change storage size and sign.
unsigned int positive = 100;    // Only positive values
long int bigNumber = 1000000L; // Large integer value
      

2.5 Type Conversion
Automatic or explicit casting between compatible types.
int i = 10;
float f = (float)i; // Explicit cast from int to float
      

2.6 Size of Data Types
Use `sizeof` operator to check memory size.
printf("Size of int: %zu bytes\n", sizeof(int));
      

2.7 Enumerations
Define named integer constants with `enum`.
enum Color { RED, GREEN, BLUE };
enum Color favorite = GREEN;
      

2.8 Scope and Lifetime
Variables can be local (inside functions) or global.
int globalVar = 5; // Global variable

void func() {
    int localVar = 10; // Local variable
}
      

2.9 Static and External Variables
`static` persists lifetime inside a function, `extern` used for external linkage.
static int counter = 0;  // Retains value between calls

extern int sharedVar;    // Defined in another file
      

2.10 Best Practices for Variables
Name clearly, initialize before use.
int totalScore = 0; // Clear, initialized variable
      

3.1 Arithmetic Operators
+, -, *, /, % perform math operations.
int a = 10, b = 3;
int sum = a + b;      // 13
int diff = a - b;     // 7
int product = a * b;  // 30
int quotient = a / b; // 3
int remainder = a % b;// 1
      

3.2 Relational Operators
Compare values: ==, !=, <, >, <=, >=.
int x = 5, y = 10;
if (x < y) {
    printf("x is less than y\n");
}
      

3.3 Logical Operators
&& (and), || (or), ! (not) combine boolean expressions.
int a = 5, b = 10;
if (a < 10 && b > 5) {
    printf("Both conditions true\n");
}
      

3.4 Assignment Operators
=, +=, -=, *=, /= shortcuts.
int c = 10;
c += 5; // c = c + 5 = 15
      

3.5 Increment and Decrement
++ and -- increase or decrease by one.
int i = 0;
i++; // i becomes 1
++i; // i becomes 2
i--; // i becomes 1
      

3.6 Bitwise Operators
&, |, ^, ~, <<, >> manipulate bits.
int a = 5; // 0101
int b = 3; // 0011
int c = a & b; // 0001 = 1
      

3.7 Conditional (Ternary) Operator
`condition ? expr1 : expr2` shorthand for if-else.
int a = 10, b = 20;
int max = (a > b) ? a : b;
      

3.8 Operator Precedence
Determines order of operations.
int result = 3 + 4 * 5; // result is 23, not 35
      

3.9 Expressions and Statements
Expressions produce values, statements execute actions.
int a = 5 + 3; // Expression
printf("%d\n", a); // Statement
      

3.10 Best Practices
Use parentheses to clarify complex expressions.
int val = (a + b) * c; // Clear grouping
      

4.1 if Statement
Executes code if condition is true.
int a = 10;
if (a > 5) {
    printf("a is greater than 5\n");
}
      

4.2 if-else Statement
Executes alternate code if condition is false.
int a = 3;
if (a > 5) {
    printf("a is greater than 5\n");
} else {
    printf("a is not greater than 5\n");
}
      

4.3 else if Ladder
Multiple conditions checked sequentially.
int score = 75;
if (score >= 90) {
    printf("Grade A\n");
} else if (score >= 80) {
    printf("Grade B\n");
} else {
    printf("Grade C or below\n");
}
      

4.4 switch Statement
Selects code block based on value.
char grade = 'B';
switch (grade) {
    case 'A': printf("Excellent\n"); break;
    case 'B': printf("Good\n"); break;
    default:  printf("Needs Improvement\n");
}
      

4.5 while Loop
Repeats block while condition is true.
int i = 0;
while (i < 5) {
    printf("%d\n", i);
    i++;
}
      

4.6 do-while Loop
Executes at least once, then repeats if condition true.
int i = 0;
do {
    printf("%d\n", i);
    i++;
} while (i < 5);
      

4.7 for Loop
Compact loop with initialization, condition, increment.
for (int i = 0; i < 5; i++) {
    printf("%d\n", i);
}
      

4.8 break Statement
Exit loop immediately.
for (int i = 0; i < 10; i++) {
    if (i == 5)
        break; // Exit loop when i is 5
    printf("%d\n", i);
}
      

4.9 continue Statement
Skip current iteration.
for (int i = 0; i < 10; i++) {
    if (i % 2 == 0)
        continue; // Skip even numbers
    printf("%d\n", i);
}
      

4.10 Best Practices for Control Flow
Keep conditions clear and avoid deeply nested blocks.
// Use early returns or switches to simplify complex logic
      

5.1 What is a Function?
Blocks of code to perform tasks, reusable.
int add(int a, int b) {
    return a + b; // Return sum
}
      

5.2 Function Declaration and Definition
Declaration (prototype) before use, definition with body.
int multiply(int x, int y); // Declaration

int multiply(int x, int y) { // Definition
    return x * y;
}
      

5.3 Function Parameters
Inputs passed to functions.
void printNumber(int num) {
    printf("Number: %d\n", num);
}
      

5.4 Return Values
Functions can return values.
int square(int n) {
    return n * n;
}
      

5.5 Calling Functions
Invoke functions by name.
int result = add(5, 10);
printf("Result: %d\n", result);
      

5.6 Recursion
Functions calling themselves.
int factorial(int n) {
    if (n == 0)
        return 1;
    else
        return n * factorial(n - 1);
}
      

5.7 Void Functions
Functions with no return value.
void greet() {
    printf("Hello!\n");
}
      

5.9 Passing by Value
Function receives a copy of argument.
void increment(int x) {
    x = x + 1; // Changes local copy only
}
      

5.10 Best Practices for Functions
Keep functions small and focused.
// Single responsibility principle
      

6.1 What is an Array?
Collection of elements of the same type.
int numbers[5]; // Array of 5 integers
      

6.2 Array Initialization
Assign values at declaration.
int numbers[5] = {1, 2, 3, 4, 5};
      

6.3 Accessing Array Elements
Use index starting at 0.
int first = numbers[0]; // Access first element
      

6.4 Multi-Dimensional Arrays
Arrays of arrays.
int matrix[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};
      

6.5 Looping Through Arrays
Use loops to process elements.
for (int i = 0; i < 5; i++) {
    printf("%d ", numbers[i]);
}
      

6.6 Arrays and Functions
Passing arrays to functions.
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
}
      

6.7 Limitations of Arrays
Fixed size, no bounds checking.

6.8 String as Character Array
Null-terminated char arrays.
char name[6] = {'H','e','l','l','o','\0'};
      

6.9 Common Errors with Arrays
Out of bounds access causes undefined behavior.

6.10 Best Practices for Arrays
Always stay within bounds.
// Use size variables and loops carefully
      

7.1 What is a String?
Sequence of characters ending with `\0`.
char str[] = "Hello";
      

7.2 String Initialization
Using string literals or char arrays.
char greeting[] = "Hi there";
      

7.3 String Input/Output
Using `printf` and `scanf`.
char name[20];
printf("Enter name: ");
scanf("%19s", name); // Read string safely
printf("Hello %s\n", name);
      

7.4 String Library Functions
`strlen`, `strcpy`, `strcmp`, etc.
#include <string.h>
int len = strlen("Hello"); // 5
      

7.5 String Copy
Copy one string to another.
char dest[20];
strcpy(dest, "Copy this");
      

7.6 String Comparison
Compare two strings.
if (strcmp("abc", "def") < 0) {
    printf("abc is less than def\n");
}
      

7.7 Concatenation
Append one string to another.
char s1[20] = "Hello ";
strcat(s1, "World");
      

7.8 Searching in Strings
Find substring or character.
char *pos = strchr("Hello", 'e'); // points to 'e'
      

7.9 Common String Errors
Buffer overflow and missing null terminator.

7.10 Best Practices for Strings
Always ensure space for `\0` and avoid unsafe functions.

8.1 What is a Structure?
Group related variables into a single unit.
struct Point {
    int x;
    int y;
};
      

8.2 Declaring Structures
Define with `struct` keyword.
struct Person {
    char name[50];
    int age;
};
      

8.3 Accessing Members
Use dot operator for variables.
struct Point p1;
p1.x = 10;
p1.y = 20;
      

8.4 Nested Structures
Structures inside structures.
struct Date {
    int day, month, year;
};

struct Person {
    char name[50];
    struct Date birthday;
};
      

8.5 Arrays of Structures
Store multiple structure variables.
struct Point points[5];
points[0].x = 1;
      

8.6 Structures and Functions
Pass structures as arguments.
void printPoint(struct Point p) {
    printf("X: %d, Y: %d\n", p.x, p.y);
}
      

8.7 Pointers to Structures
Use arrow operator `->`.
struct Point *ptr = &p1;
printf("%d\n", ptr->x);
      

8.8 Typedef with Structures
Create alias names.
typedef struct {
    int x;
    int y;
} Point;
      

8.9 Common Uses of Structures
Store complex data like records.

8.10 Best Practices for Structures
Keep structures simple and consistent.

9.1 What is a Pointer?
Variable storing memory address.
int x = 10;
int *ptr = &x; // Pointer to x
      

9.2 Pointer Declaration
Declare with `*`.
int *p;    // Pointer to int
char *c;   // Pointer to char
      

9.3 Pointer Dereferencing
Access value at pointed address.
printf("%d\n", *ptr); // Prints value of x
      

9.4 Pointer Arithmetic
Increment or decrement pointer.
int arr[3] = {1, 2, 3};
int *p = arr;
p++; // Points to arr[1]
      

9.5 Null Pointers
Pointer that points to nothing.
int *p = NULL;
      

9.6 Pointers and Arrays
Arrays decay to pointers.
int arr[5];
int *p = arr; // Same as &arr[0]
      

9.7 Pointers to Pointers
Pointer storing address of another pointer.
int **pp;
      

9.8 Function Pointers
Pointers to functions.
int add(int a, int b) { return a + b; }
int (*funcPtr)(int, int) = &add;
      

9.10 Best Practices for Pointers
Initialize before use, avoid dangling pointers.

10.1 What is Dynamic Memory?
Memory allocated at runtime, flexible size.
int *p = (int*)malloc(sizeof(int) * 10);
      

10.2 malloc()
Allocates memory, returns pointer.
int *arr = (int*)malloc(sizeof(int) * 5);
if (arr == NULL) {
    printf("Memory allocation failed\n");
}
      

10.3 calloc()
Allocates and zero-initializes.
int *arr = (int*)calloc(5, sizeof(int));
      

10.4 realloc()
Resize allocated memory.
arr = (int*)realloc(arr, sizeof(int) * 10);
      

10.5 free()
Releases memory to avoid leaks.
free(arr);
      

10.6 Dangling Pointers
Pointers pointing to freed memory.
free(arr);
arr = NULL; // Avoid dangling pointer
      

10.7 Memory Leaks
Forgetting to free causes leaks.

10.8 Using Pointers with Dynamic Memory
Store and access allocated memory.
for (int i = 0; i < 10; i++) {
    p[i] = i;
}
      

10.9 Checking Allocation Success
Always verify pointers returned by malloc/calloc.
if (p == NULL) {
    printf("Allocation failed\n");
    exit(1);
}
      

10.10 Best Practices for Dynamic Memory
Always free, avoid memory leaks and dangling pointers.

11.1 Introduction to File Handling
File handling lets a program read and write data to files on disk. This is useful to save data permanently. The process involves opening, manipulating, and closing files.
#include <stdio.h>

int main() {
    FILE *file = fopen("example.txt", "w"); // Open file for writing
    if (file == NULL) {
        printf("Failed to open file.\n");
        return 1;
    }
    fprintf(file, "Hello File Handling!\n"); // Write to file
    fclose(file); // Close the file
    return 0;
}
      

11.2 fopen() Function
fopen() opens a file and returns a pointer to it. It needs the filename and the mode ("r" for read, "w" for write, etc.). Always check if fopen returns NULL.
FILE *fp = fopen("data.txt", "r"); // Open for reading
if (fp == NULL) {
    printf("Cannot open file.\n");
}
      

11.3 fclose() Function
fclose() closes the file and releases system resources. Forgetting to close files can cause data loss or memory leaks.
fclose(fp); // Close the opened file
      

11.4 Reading from a File
Reading files can be done with fgets(), fscanf(), or fread(). fgets() reads a line, fscanf() reads formatted data.
char buffer[100];
FILE *fp = fopen("data.txt", "r");
if (fp) {
    while (fgets(buffer, sizeof(buffer), fp)) {
        printf("%s", buffer); // Print each line read
    }
    fclose(fp);
}
      

11.5 Writing to a File
You can write using fprintf(), fputs(), or fwrite(). Open file in "w" to write or "a" to append.
FILE *fp = fopen("data.txt", "a"); // Open for appending
if (fp) {
    fprintf(fp, "Appending a new line.\n");
    fclose(fp);
}
      

11.6 File Modes Explained
Common modes: "r" read, "w" write (overwrite), "a" append, "rb"/"wb" for binary files. Modes control file access.
FILE *fp = fopen("file.bin", "rb"); // Open binary file for reading
      

11.7 Error Handling in File Operations
Always check return values of file functions and handle errors gracefully, such as NULL pointers.
if (fopen("file.txt", "r") == NULL) {
    perror("Error opening file");
}
      

11.8 Random Access in Files
Use fseek(), ftell(), rewind() to move file pointer for reading/writing at specific positions.
fseek(fp, 0, SEEK_END); // Move to end of file
long size = ftell(fp);   // Get current position (file size)
rewind(fp);             // Go back to beginning
      

11.9 Binary vs Text Files
Text files store characters, binary files store raw bytes. Always use correct mode to avoid data corruption.

11.10 Best Practices in File Handling
Always close files, check errors, and back up important data to avoid loss.

12.1 What is a Preprocessor?
The C preprocessor runs before compilation and handles directives like includes and macros. It modifies source code by textual replacement.
#include <stdio.h> // Includes standard input-output library before compilation
      

12.2 #include Directive
Used to include header files or user files in the program. Angle brackets for system headers, quotes for user headers.
#include <stdio.h>    // System header
#include "myheader.h"  // User-defined header file
      

12.3 #define Directive
Defines symbolic constants or macros for token replacement in the code.
#define PI 3.14159
#define MAX 100
      

12.4 Macro Functions
Macros can accept arguments like inline functions but without type safety.
#define SQUARE(x) ((x) * (x))
int val = SQUARE(5); // val will be 25
      

12.5 Conditional Compilation
Allows compilation of code only if certain macros are defined, useful for debugging or platform-specific code.
#ifdef DEBUG
printf("Debug mode enabled\n");
#endif
      

12.6 #undef Directive
Undefines or removes a macro definition to prevent its use.
#undef MAX
      

12.7 #pragma Directive
Used to give special instructions to the compiler, often compiler-specific.
#pragma warning(disable:4996) // Disable specific warnings in MSVC
      

12.8 #error and #warning Directives
Used to generate compile-time errors or warnings to enforce conditions.
#error "Unsupported compiler version"
#warning "This feature is deprecated"
      

12.9 Predefined Macros
Built-in macros like __FILE__ and __LINE__ give file name and line number.
printf("File: %s, Line: %d\n", __FILE__, __LINE__);
      

12.10 Best Practices for Preprocessor
Keep macros simple, prefer constants or inline functions for complex logic.

13.1 Common Errors in C
Syntax errors are caused by mistakes like missing semicolons. Logical errors happen when the program runs incorrectly. Runtime errors happen during execution.
// Syntax error example: missing semicolon
int x = 10
      

13.2 Using printf for Debugging
printf() statements print variable values and program flow to help find bugs.
int x = 5;
printf("x = %d\n", x); // Debug output
      

13.3 Using Debuggers (gdb)
gdb lets you run programs step by step, set breakpoints, and inspect variables.
// Compile with debugging info:
// gcc -g program.c -o program
// Run in gdb:
// gdb ./program
      

13.4 Breakpoints
Breakpoints pause execution at specified lines to inspect program state.

13.5 Watch Variables
Watches monitor changes in variable values during debugging.

13.6 Handling Runtime Errors
Detect errors like null pointer dereferences and handle them safely.
if (ptr == NULL) {
    printf("Null pointer detected\n");
}
      

13.7 Using assert()
assert() checks conditions and terminates program if false, helpful during debugging.
#include <assert.h>
assert(x > 0);
      

13.8 Sanitizers and Tools
Tools like AddressSanitizer help detect memory errors and leaks.

13.9 Logging Errors
Logging errors to files helps with post-mortem analysis.

13.10 Best Practices in Debugging
Start with simple prints, use tools, isolate issues, and test fixes thoroughly.

14.1 What is Recursion?
Recursion is when a function calls itself to solve smaller parts of a problem. It must have a base case to stop.
int factorial(int n) {
    if (n == 0)
        return 1;
    else
        return n * factorial(n - 1);
}
      

14.2 Base Case Importance
Base case stops the recursion to prevent infinite calls and stack overflow.

14.3 Recursive Factorial Example
Calculate factorial using recursion, multiplying descending numbers.
int fact(int n) {
    if (n <= 1) return 1;
    else return n * fact(n - 1);
}
      

14.4 Fibonacci with Recursion
Calculate Fibonacci numbers by summing previous two recursively.
int fib(int n) {
    if (n <= 1) return n;
    else return fib(n - 1) + fib(n - 2);
}
      

14.5 Advantages and Disadvantages
Recursion simplifies code but can be inefficient and cause stack overflow.

14.6 Tail Recursion
Tail recursion calls itself as the last operation, allowing compiler optimization.

14.7 Recursive vs Iterative
Recursive code is easier to write for some problems; iterative is more efficient.

14.8 Recursive Tree Example
Trees can be traversed using recursive functions for pre-order, in-order, post-order.

14.9 Common Errors in Recursion
Missing base case or incorrect recursion causes infinite loops and crashes.

14.10 Best Practices for Recursion
Always include base case, test for stack overflow, and prefer iteration when performance critical.

15.1 Introduction to Data Structures
Data structures organize data for efficient access and modification. Examples include arrays, lists, and trees.

15.2 Arrays Recap
Arrays hold fixed size sequential elements of same type, allowing direct access by index.

15.3 Linked Lists
Linked lists are dynamic, consisting of nodes pointing to next nodes.
struct Node {
    int data;
    struct Node *next;
};
      

15.4 Stacks
Stacks use Last In First Out (LIFO) method, with push and pop operations.

15.5 Queues
Queues use First In First Out (FIFO), with enqueue and dequeue.

15.6 Trees Introduction
Trees are hierarchical structures with parent and child nodes.

15.7 Graphs Introduction
Graphs are nodes connected by edges, useful for networks.

15.8 Hash Tables Introduction
Hash tables store key-value pairs and provide fast lookup.

15.9 Abstract Data Types (ADT)
ADTs define behavior like stack or queue without specifying implementation.

15.10 Choosing Data Structures
Select data structures based on speed, memory, and complexity of operations.

16.1 What is a Linked List?
Linked lists are dynamic data structures made of nodes connected by pointers. They allow efficient insertion/deletion.
struct Node {
    int data;
    struct Node *next;
};
      

16.2 Creating a Linked List
Allocate nodes dynamically and link them to form a list.
struct Node *head = NULL; // Start empty
head = malloc(sizeof(struct Node));
head->data = 10;
head->next = NULL;
      

16.3 Traversing a Linked List
Start from head and visit each node until NULL.
struct Node *temp = head;
while (temp != NULL) {
    printf("%d\n", temp->data);
    temp = temp->next;
}
      

16.4 Inserting at Beginning
Create a new node and update head pointer.
struct Node *newNode = malloc(sizeof(struct Node));
newNode->data = 5;
newNode->next = head;
head = newNode;
      

16.5 Inserting at End
Traverse to last node and add new node.
struct Node *temp = head;
while (temp->next != NULL) {
    temp = temp->next;
}
struct Node *newNode = malloc(sizeof(struct Node));
newNode->data = 15;
newNode->next = NULL;
temp->next = newNode;
      

16.6 Deleting a Node
Remove node by changing pointers and freeing memory.

16.7 Searching in Linked List
Traverse and compare node data with target value.

16.8 Advantages and Disadvantages
Flexible size, but slower random access compared to arrays.

16.9 Circular Linked Lists
Last node points back to head creating a loop.

16.10 Best Practices for Linked Lists
Always handle edge cases and manage memory carefully.

17.1 What is a Stack?
A stack is a LIFO data structure, where last added item is removed first.

17.2 Stack Operations
Basic operations include push (add) and pop (remove) items.

17.3 Stack Implementation using Array
Use an array and integer to track top of stack.
#define MAX 100
int stack[MAX];
int top = -1;

void push(int val) {
    if (top < MAX - 1) {
        stack[++top] = val;
    }
}
      

17.4 Stack Implementation using Linked List
Use linked nodes where each node points to next, top is head.

17.5 Stack Overflow and Underflow
Overflow happens when pushing full stack; underflow when popping empty.

17.6 Applications of Stacks
Used in parsing, expression evaluation, backtracking algorithms.

17.7 Stack in Function Calls
Function call stack stores return addresses and local variables.

17.8 Evaluating Expressions with Stack
Convert infix expressions to postfix and evaluate using stack.

17.9 Balanced Parentheses
Use stack to check matching pairs in expressions.

17.10 Best Practices for Stacks
Always check boundaries and handle errors gracefully.

18.1 What is a Queue?
A queue is a FIFO data structure where elements are added at rear and removed from front.

18.2 Queue Operations
Enqueue adds item to rear; dequeue removes from front.

18.3 Queue Implementation using Array
Use array and indices to manage front and rear positions.

18.4 Circular Queue
Circular queue treats array as circular buffer to utilize space.

18.5 Queue Using Linked List
Dynamic queues implemented with linked nodes for flexible size.

18.6 Queue Overflow and Underflow
Overflow occurs when queue is full; underflow when empty.

18.7 Applications of Queues
Queues are used in task scheduling, buffering, and BFS algorithm.

18.8 Priority Queue
Elements dequeued based on priority, not arrival order.

18.9 Deque (Double Ended Queue)
Insert and remove elements from both ends.

18.10 Best Practices for Queues
Handle indices carefully and check boundary conditions.

19.1 Why Sorting?
Sorting organizes data in a particular order, making search and processing faster.

19.2 Bubble Sort
Repeatedly swaps adjacent elements if they are in wrong order.
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;
            }
        }
    }
}
      

19.3 Selection Sort
Selects minimum element and swaps with current index.

19.4 Insertion Sort
Builds sorted array one element at a time by insertion.

19.5 Quick Sort
Uses divide and conquer to partition array and sort recursively.

19.6 Merge Sort
Recursively splits and merges arrays in sorted order.

19.7 Heap Sort
Sorts using a binary heap data structure.

19.8 Comparison of Sorting Algorithms
Analyzes time complexity and space usage of sorts.

19.9 When to Use Which Sort?
Depends on dataset size and nature.

19.10 Best Practices for Sorting
Use built-in sorts for efficiency; optimize large datasets.

20.1 Linear Search
Checks each element sequentially until target found or end reached.
int linearSearch(int arr[], int n, int target) {
    for (int i = 0; i < n; i++) {
        if (arr[i] == target)
            return i; // Return index if found
    }
    return -1; // Not found
}
      

20.2 Binary Search
Efficient search in sorted array by repeatedly halving search space.
int binarySearch(int arr[], int n, int target) {
    int left = 0, right = n - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] == target)
            return mid;
        else if (arr[mid] < target)
            left = mid + 1;
        else
            right = mid - 1;
    }
    return -1;
}
      

20.3 Interpolation Search
Improves binary search on uniformly distributed data by estimating position.

20.4 Exponential Search
Finds range and performs binary search within range.

20.5 Jump Search
Searches by jumping fixed steps to reduce comparisons.

20.6 Fibonacci Search
Uses Fibonacci numbers to divide search space.

20.7 Hashing for Search
Hash tables provide near constant time lookup.

20.8 Search in Trees and Graphs
Uses DFS and BFS to traverse data structures.

20.9 When to Use Which Search?
Choose based on data type and ordering.

20.10 Best Practices for Searching
Sort data for binary search; prefer hashing for fast lookup.

21.1 Introduction to Dynamic Memory
Dynamic memory allows allocation of memory during program execution. It helps manage memory flexibly depending on runtime needs.

21.2 malloc() Function
malloc() allocates requested memory in bytes and returns a pointer. If allocation fails, it returns NULL.
#include <stdlib.h>

int *ptr = (int *)malloc(sizeof(int) * 5); // Allocate memory for 5 integers
if (ptr == NULL) {
    printf("Memory allocation failed.\n");
}
      

21.3 calloc() Function
calloc() allocates memory for multiple elements and initializes them to zero.
int *arr = (int *)calloc(5, sizeof(int)); // Allocate and zero initialize 5 ints
if (arr == NULL) {
    printf("Calloc failed.\n");
}
      

21.4 realloc() Function
realloc() resizes previously allocated memory, preserving data.
ptr = (int *)realloc(ptr, sizeof(int) * 10); // Resize to hold 10 ints
if (ptr == NULL) {
    printf("Reallocation failed.\n");
}
      

21.5 free() Function
free() deallocates previously allocated memory to prevent leaks.
free(ptr); // Free dynamically allocated memory
      

21.6 Dangling Pointers
Dangling pointers occur when pointers point to freed memory. Avoid by setting pointers to NULL after free.

21.7 Memory Leaks
Memory leaks happen when allocated memory isn't freed, causing wasted memory over time.

21.8 Use of Pointers with Dynamic Memory
Pointers store addresses of dynamically allocated blocks and allow data access.

21.9 Common Errors in Dynamic Memory
Errors include double free, invalid free, and forgetting to free memory.

21.10 Best Practices in Dynamic Memory
Always check allocation success, free memory, and avoid dangling pointers.

22.1 Introduction to Structures
Structures group variables of different types into a single unit, useful for related data.
struct Point {
    int x;
    int y;
};
      

22.2 Declaring and Using Structures
Declare struct variables and access members with dot operator.
struct Point p1;
p1.x = 10;
p1.y = 20;
      

22.3 Structures with Functions
Pass structures to functions by value or reference.
void printPoint(struct Point p) {
    printf("X: %d, Y: %d\n", p.x, p.y);
}
      

22.4 Nested Structures
Structures can contain other structures as members.

22.5 Introduction to Unions
Unions store different data types in the same memory location, saving space.
union Data {
    int i;
    float f;
    char str[20];
};
      

22.6 Differences Between Struct and Union
Struct allocates separate memory for each member; union shares memory among members.

22.7 Using Unions
Only one member can hold a value at a time.

22.8 Bit Fields in Structures
Bit fields allow allocation of specific bits to structure members.

22.9 Alignment and Padding
Compiler may add padding bytes for memory alignment inside structures.

22.10 Best Practices for Structures and Unions
Use structures for grouping related data; unions to save memory when only one value is used.

23.1 Pointer Basics
Pointers store memory addresses of variables and are used for indirect access.
int x = 10;
int *p = &x; // Pointer to x
printf("Value: %d\n", *p);
      

23.2 Pointer Arithmetic
You can increment/decrement pointers to traverse arrays.
int arr[3] = {1, 2, 3};
int *p = arr;
printf("%d\n", *p);     // 1
p++;
printf("%d\n", *p);     // 2
      

23.3 Arrays and Pointers Relation
Arrays decay into pointers to their first element when passed to functions.

23.4 Pointer to Pointer
Pointers can store addresses of other pointers.
int x = 5;
int *p = &x;
int **pp = &p;
printf("%d\n", **pp);  // 5
      

23.5 Passing Arrays to Functions
Pass array pointers and size for processing.

23.6 Multidimensional Arrays
Arrays with multiple indices accessed via pointer arithmetic.

23.7 Dynamic Arrays with Pointers
Allocate arrays dynamically and access using pointers.

23.8 Common Pointer Errors
Dangling pointers, null pointers, and invalid dereferences.

23.9 Pointer Typecasting
Cast pointers to different types carefully.

23.10 Best Practices with Pointers and Arrays
Initialize pointers, avoid invalid memory access, and document code clearly.

24.1 Introduction to Strings
Strings are arrays of characters terminated by a null character '\0'.

24.2 Declaring and Initializing Strings
Use char arrays or pointers to hold strings.
char str1[] = "Hello";
char *str2 = "World";
      

24.3 String Input and Output
Use gets(), fgets() for input and printf() for output.

24.4 String Library Functions
Functions like strlen(), strcpy(), strcat() help manipulate strings.
#include <string.h>
char src[] = "Hello";
char dest[10];
strcpy(dest, src);
printf("%s\n", dest);
      

24.5 String Comparison
strcmp() compares two strings and returns relation.

24.6 String Concatenation
strcat() appends one string to another.

24.7 Common String Manipulation Techniques
Copy, compare, find length, and concatenate strings.

24.8 String Tokenization
strtok() breaks string into tokens using delimiters.

24.9 Mutable and Immutable Strings
Strings in arrays can be changed; string literals should not be modified.

24.10 Best Practices with Strings Keep buffer sizes sufficient and use safe string functions.

25.1 Standard Input and Output
Use scanf() to get user input and printf() to display output.
These are the most common ways to interact with users.
They use format specifiers to parse and print data.
#include <stdio.h>
int main() {
    int num;
    printf("Enter a number: ");
    scanf("%d", &num);
    printf("You entered: %d\n", num);
    return 0;
}
      

25.2 Formatted Output
printf supports format specifiers to control output appearance.
You can control width, precision, and alignment.
It's useful for neat tables and readable output.
#include <stdio.h>
int main() {
    float pi = 3.14159;
    printf("Pi rounded to 2 decimals: %.2f\n", pi);
    return 0;
}
      

25.3 Reading Strings
fgets() is safer than gets() for string input.
It prevents buffer overflows by limiting characters read.
It also captures spaces unlike scanf with %s.
#include <stdio.h>
int main() {
    char name[50];
    printf("Enter your name: ");
    fgets(name, sizeof(name), stdin);
    printf("Hello, %s", name);
    return 0;
}
      

25.4 File Input and Output Recap
File I/O is done using fopen(), fprintf(), fscanf(), etc.
It's useful for saving data across program runs.
Closing files after access is essential.
#include <stdio.h>
int main() {
    FILE *f = fopen("data.txt", "w");
    fprintf(f, "Saving this to a file.\n");
    fclose(f);
    return 0;
}
      

25.5 getchar() and putchar()
getchar() reads a single character from input.
putchar() writes a character to output.
These are useful for low-level I/O tasks.
#include <stdio.h>
int main() {
    char c;
    printf("Type a character: ");
    c = getchar();
    printf("You typed: ");
    putchar(c);
    return 0;
}
      

25.6 Handling Input Errors
Always check the return value of input functions.
Clear input buffer when necessary using getchar().
Helps prevent invalid input from crashing the program.
#include <stdio.h>
int main() {
    int x;
    printf("Enter a number: ");
    if (scanf("%d", &x) != 1) {
        printf("Invalid input!\n");
    }
    return 0;
}
      

25.7 Command Line Arguments
main() can receive arguments from the command line.
Useful for scripts and tools that require input at launch.
argc is argument count, argv is argument vector.
#include <stdio.h>
int main(int argc, char *argv[]) {
    if (argc > 1) printf("First arg: %s\n", argv[1]);
    return 0;
}
      

25.8 Buffering and Flushing
stdout is usually buffered for performance.
fflush(stdout) forces output to appear immediately.
Useful in real-time output situations.
#include <stdio.h>
int main() {
    printf("Loading...");
    fflush(stdout);
    // simulate delay
    for (int i = 0; i < 100000000; i++);
    printf(" Done!\n");
    return 0;
}
      

25.9 Advanced Output Formatting
printf() supports width, alignment, and padding.
It improves display of data tables or reports.
Format flags like '-' or '0' control style.
#include <stdio.h>
int main() {
    printf("%-10s %5d\n", "Apples", 12);
    printf("%-10s %5d\n", "Oranges", 45);
    return 0;
}
      

25.10 Best Practices for Input and Output
Always validate user input before use.
Use fgets over gets for safety.
Close files and flush outputs when needed.
#include <stdio.h>
int main() {
    char input[20];
    printf("Enter name: ");
    if (fgets(input, sizeof(input), stdin)) {
        printf("Welcome %s", input);
    }
    return 0;
}
      

26.1 Introduction to File Handling
File handling allows reading/writing data to files for persistent storage.
Files provide a way to store data permanently across sessions.
File operations are performed using standard library functions.
#include <stdio.h>
int main() {
    FILE *f = fopen("data.txt", "w");
    fprintf(f, "Hello, File!\n");
    fclose(f);
    return 0;
}
      

26.2 Opening and Closing Files
Use fopen() to open a file with specified mode.
Always use fclose() to release file resources.
Check for NULL to ensure file opened successfully.
FILE *f = fopen("example.txt", "r");
if (f == NULL) printf("File not found!\n");
else fclose(f);
      

26.3 Reading from Files
Use fscanf(), fgets(), or fread() to read file contents.
fgets() is safer for reading strings with spaces.
Always check return values while reading.
char line[100];
FILE *f = fopen("readme.txt", "r");
if (fgets(line, sizeof(line), f)) printf("%s", line);
fclose(f);
      

26.4 Writing to Files
Use fprintf() for formatted output to a file.
fwrite() is used for binary data.
Ensure file opened in write mode before writing.
FILE *f = fopen("output.txt", "w");
fprintf(f, "Writing to file.\n");
fclose(f);
      

26.5 File Modes
Modes include "r", "w", "a" for text and "rb", "wb" for binary.
Each mode controls how file is accessed.
Using the correct mode prevents data loss.
FILE *f1 = fopen("file.txt", "r"); // read
FILE *f2 = fopen("file.txt", "w"); // write
FILE *f3 = fopen("file.txt", "a"); // append
      

26.6 File Positioning
fseek() moves file pointer; ftell() returns current position.
Rewinding can be done using rewind().
Useful for skipping or revisiting data.
FILE *f = fopen("log.txt", "r");
fseek(f, 0, SEEK_END);
long size = ftell(f);
rewind(f);
fclose(f);
      

26.7 Error Handling in Files
Always check return values for file functions.
Use perror() or strerror() for error descriptions.
Prevent file-related crashes and undefined behavior.
FILE *f = fopen("missing.txt", "r");
if (!f) perror("Open error");
      

26.8 Binary File Handling
fread() and fwrite() are used for binary data.
Suitable for non-text data like images or structures.
Requires exact size and structure knowledge.
int data[3] = {1, 2, 3};
FILE *f = fopen("data.bin", "wb");
fwrite(data, sizeof(int), 3, f);
fclose(f);
      

26.9 File Locking (Basics)
Locking prevents conflicts in concurrent access.
Supported via OS-specific methods or libraries.
Use with caution in multi-threaded apps.
// POSIX file locking (Linux/Unix only)
#include <fcntl.h>
int fd = open("file.txt", O_WRONLY);
struct flock lock = {F_WRLCK, SEEK_SET, 0, 0};
fcntl(fd, F_SETLK, &lock);
      

26.10 Best Practices for File Handling
Always close files and check function return values.
Use buffers and error handling properly.
Keep file access fast and safe.
FILE *f = fopen("data.txt", "r");
if (!f) return 1;
// read or write operations
fclose(f);
      

27.1 What is Recursion?
Recursion is a function calling itself to solve smaller instances of a problem.

27.2 Base Case Importance
Every recursive function needs a base case to stop infinite calls.

27.3 Factorial Example
Recursive factorial function multiplies current number by factorial of n-1.
int factorial(int n) {
    if (n == 0)
        return 1; // Base case
    else
        return n * factorial(n - 1); // Recursive call
}
      

27.4 Fibonacci Series
Recursion calculates nth Fibonacci number by summing two previous.

27.5 Recursive vs Iterative
Recursion is elegant but may cause overhead compared to loops.

27.6 Tail Recursion
Tail recursion optimizes recursive calls by doing last operation.

27.7 Recursive Tree and Stack
Each recursive call adds a new stack frame until base case.

27.8 Common Errors in Recursion
Missing base case leads to infinite recursion and stack overflow.

27.9 When to Use Recursion Solve problems naturally defined by subproblems.

27.10 Best Practices in Recursion Always define clear base cases and avoid excessive depth.

28.1 What are Bitwise Operators?
Bitwise operators perform operations on bits of integer types.

28.2 AND Operator (&)
Performs bitwise AND, bits set only if both bits are 1.
int a = 5;      // 0101
int b = 3;      // 0011
int c = a & b; // 0001 (1)
      

28.3 OR Operator (|)
Performs bitwise OR, bits set if either bit is 1.
int c = a | b; // 0111 (7)
      

28.4 XOR Operator (^)
Performs bitwise XOR, bits set if bits differ.
int c = a ^ b; // 0110 (6)
      

28.5 NOT Operator (~)
Inverts all bits.
int c = ~a; // Bitwise NOT of 0101
      

28.6 Left Shift (<<)
Shifts bits left, filling with zeros.

28.7 Right Shift (>>)
Shifts bits right, filling left with zeros or sign bit.

28.8 Common Uses of Bitwise Ops
Used in flags, masks, encryption, and optimization.

28.9 Bitwise in Embedded Systems
Control hardware and manipulate registers efficiently.

28.10 Best Practices with Bitwise Operators Use parentheses and document expressions clearly.

29.1 What is Debugging?
Debugging identifies and removes errors from programs.
It helps in maintaining program correctness and quality.
Debugging involves tools, techniques, and logical thinking.
#include <stdio.h>
int main() {
    int x = 5, y = 0;
    printf("Before bug fix: x = %d, y = %d\n", x, y);
    y = x + 3; // Fixed logic bug
    printf("After bug fix: x = %d, y = %d\n", x, y);
    return 0;
}
      

29.2 Types of Bugs
Bugs can be syntax, logic, runtime, or semantic errors.
Syntax bugs prevent compilation, while logic bugs affect results.
Understanding the type helps in quicker fixes.
// Syntax bug fixed:
#include <stdio.h>
int main() {
    int a = 10;
    printf("Value: %d\n", a); // Added missing semicolon
    return 0;
}
      

29.3 Print Debugging
Use print statements to inspect values and program flow.
Helpful in small programs or without debuggers.
It's a manual but effective debugging method.
#include <stdio.h>
int main() {
    int sum = 0;
    for (int i = 0; i < 5; i++) {
        sum += i;
        printf("i=%d, sum=%d\n", i, sum);
    }
    return 0;
}
      

29.4 Debugging with GDB
GDB allows stepping, breakpoints, and inspection.
Run with -g flag in compilation to use GDB.
Use commands like 'break', 'run', 'next', 'print'.
// Compile with: gcc -g prog.c -o prog
// Run GDB: gdb ./prog
// Inside GDB:
// break main
// run
// print variable
// next
      

29.5 Common Debugging Tools
Tools include GDB, Valgrind, and IDE debuggers.
These help trace bugs and memory errors.
Static analyzers catch issues before runtime.
// Run static analyzer (example: cppcheck):
// cppcheck your_file.c
      

29.6 Debugging Memory Issues
Tools like Valgrind detect leaks and invalid memory access.
Always initialize pointers and free memory.
Check array bounds to prevent overflows.
#include <stdlib.h>
int main() {
    int *arr = malloc(5 * sizeof(int));
    for (int i = 0; i < 5; i++) arr[i] = i * 2;
    free(arr); // Prevent memory leak
    return 0;
}
      

29.7 Handling Crashes and Exceptions
Use logs or print statements before crashes to trace cause.
Handle null pointers and invalid access safely.
Exit gracefully with error messages.
#include <stdio.h>
int main() {
    int *ptr = NULL;
    if (ptr == NULL) {
        printf("Null pointer!\n");
        return 1;
    }
    return 0;
}
      

29.8 Debugging Multi-threaded Programs
Race conditions and deadlocks are hard to trace.
Use thread-safe functions and synchronization tools.
Valgrind's helgrind detects race conditions.
#include <pthread.h>
#include <stdio.h>
int count = 0;
void* task(void* arg) {
    for (int i = 0; i < 100000; ++i) count++;
    return NULL;
}
int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, task, NULL);
    pthread_create(&t2, NULL, task, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("Final count: %d\n", count);
    return 0;
}
      

29.9 Writing Test Cases for Debugging
Writing test cases ensures code correctness.
Unit testing frameworks automate tests.
Helps catch bugs early and reliably.
#include <assert.h>
int square(int x) { return x * x; }
int main() {
    assert(square(2) == 4);
    assert(square(3) == 9);
    return 0;
}
      

29.10 Best Practices in Debugging
Isolate bugs and reproduce the issue reliably.
Use version control to track changes.
Comment and log strategically.
// Good practice:
// Log inputs, use assertions, and track down issue scope
#include <stdio.h>
int main() {
    int x = -1;
    if (x < 0) printf("Error: x is negative\n");
    return 0;
}
      

30.1 Multithreading Basics
Multiple threads run concurrently to improve performance.
Threads share resources but have independent execution paths.
Pthreads library is used in C for multithreaded programming.
#include <stdio.h>
#include <pthread.h>
void* run(void* arg) {
    printf("Thread is running!\n");
    return NULL;
}
int main() {
    pthread_t t;
    pthread_create(&t, NULL, run, NULL);
    pthread_join(t, NULL);
    return 0;
}
      

30.2 Synchronization
Synchronization ensures that shared resources are accessed safely.
Mutexes lock code sections to prevent concurrent access.
Prevents data races in multithreaded environments.
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t lock;
int counter = 0;
void* increase(void* arg) {
    pthread_mutex_lock(&lock);
    counter++;
    printf("Counter: %d\n", counter);
    pthread_mutex_unlock(&lock);
    return NULL;
}
int main() {
    pthread_t t1, t2;
    pthread_mutex_init(&lock, NULL);
    pthread_create(&t1, NULL, increase, NULL);
    pthread_create(&t2, NULL, increase, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_mutex_destroy(&lock);
    return 0;
}
      

30.3 Interprocess Communication
IPC allows processes to share data through various methods.
Common IPC methods include pipes, message queues, and shared memory.
Below is an example using unnamed pipe.
#include <stdio.h>
#include <unistd.h>
int main() {
    int fd[2];
    char buffer[20];
    pipe(fd);
    write(fd[1], "Hello", 6);
    read(fd[0], buffer, 6);
    printf("Received: %s\n", buffer);
    return 0;
}
      

30.4 Network Programming
Network programs communicate using sockets.
Sockets use IP and port numbers for communication.
Example below shows a simple TCP client.
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main() {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in server = {AF_INET, htons(8080), inet_addr("127.0.0.1")};
    connect(sock, (struct sockaddr*)&server, sizeof(server));
    send(sock, "Hello", strlen("Hello"), 0);
    close(sock);
    return 0;
}
      

30.5 Introduction to Operating Systems Concepts
Operating systems manage hardware and software resources.
Concepts include processes, memory, scheduling, and I/O.
Example shows process creation using fork().
#include <stdio.h>
#include <unistd.h>
int main() {
    pid_t pid = fork();
    if (pid == 0) printf("Child process\n");
    else printf("Parent process\n");
    return 0;
}
      

30.6 Introduction to Databases
Databases store and retrieve structured data efficiently.
SQL is used for querying and managing databases.
Use libraries like SQLite for embedding databases in C.
// Example only. Requires SQLite library.
#include <sqlite3.h>
sqlite3 *db;
sqlite3_open("test.db", &db);
sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS users(id INT, name TEXT);", 0, 0, 0);
sqlite3_close(db);
      

30.7 Security Basics
Security ensures confidentiality, integrity, and availability.
Buffer overflows and input validation are key areas.
Always sanitize input to avoid vulnerabilities.
#include <stdio.h>
int main() {
    char name[10];
    printf("Enter name: ");
    fgets(name, sizeof(name), stdin);
    printf("Hello %s\n", name);
    return 0;
}
      

30.8 Performance Optimization
Optimize loops and memory access for better performance.
Use algorithms with lower time complexity.
Profile and benchmark code to identify bottlenecks.
#include <stdio.h>
void fast_loop() {
    for (int i = 0; i < 1000000; ++i);
}
int main() {
    fast_loop();
    return 0;
}
      

30.9 Code Documentation
Comments and external docs help explain logic.
Use clear, concise comments above functions and blocks.
Helpful for teams and future maintenance.
#include <stdio.h>
// Function to add two numbers
int add(int a, int b) {
    return a + b;
}
int main() {
    printf("%d\n", add(2, 3));
    return 0;
}
      

30.10 Continuous Learning
Stay updated with evolving technologies and standards.
Practice coding regularly and read documentation.
Join communities and contribute to open source.
// No code, but advice:
// Practice daily challenges on platforms like LeetCode or HackerRank
// Read books like "The C Programming Language" by Kernighan & Ritchie
      

Dynamic memory allocation in C is done using malloc(), calloc(), realloc() and freed using free().
Memory leaks occur when allocated memory is not freed.
Always check if memory was successfully allocated.
#include <stdio.h>
#include <stdlib.h>
int *ptr = (int *)malloc(sizeof(int) * 5);
if (ptr != NULL) {
    ptr[0] = 10;
    printf("%d\n", ptr[0]);
    free(ptr);
}
      

Linked lists are dynamic data structures made of nodes. Each node contains data and a pointer to the next node. They allow efficient insertion/deletion but slow access.
struct Node {
    int data;
    struct Node* next;
};
struct Node* head = NULL;
      

A stack is a LIFO data structure. Elements are added/removed at the top. Useful in parsing, recursion, and undo mechanisms. Can be implemented using arrays or linked lists.
#define MAX 100
int stack[MAX];
int top = -1;
void push(int val) { if (top < MAX-1) stack[++top] = val; }
      

A queue is a FIFO data structure. Elements are added at rear and removed at front. Used in scheduling, buffer management. Can be implemented using arrays or linked lists.
#define SIZE 100
int queue[SIZE], front = 0, rear = -1;
void enqueue(int val) { if (rear < SIZE-1) queue[++rear] = val; }
      

Trees are hierarchical data structures with a root node. Binary trees have at most two children per node. Useful for searching, sorting, and hierarchical data.
struct Node {
    int data;
    struct Node *left, *right;
};
struct Node* root = NULL;
      

Graphs are a set of nodes connected by edges. Can be directed/undirected, weighted/unweighted. Used in networks, social media, and pathfinding.
int graph[10][10];
graph[0][1] = 1; // edge from node 0 to 1
      

Sorting organizes elements in order. Common sorts: bubble, insertion, quick. Sorting improves searching efficiency. Each algorithm has different time complexities.
void bubbleSort(int a[], int n) {
    for (int i = 0; i < n-1; i++)
        for (int j = 0; j < n-i-1; j++)
            if (a[j] > a[j+1]) {
                int temp = a[j];
                a[j] = a[j+1];
                a[j+1] = temp;
            }
}
      

Searching finds an element in data. Common types: linear and binary search. Binary search works on sorted data. Linear search checks each element.
int linearSearch(int arr[], int n, int key) {
    for (int i = 0; i < n; i++)
        if (arr[i] == key) return i;
    return -1;
}
      

Hashing maps data to fixed-size values using hash functions. Used in hash tables and dictionaries. Helps in fast lookup and insert operations.
#define SIZE 10
int hash(int key) { return key % SIZE; }
int table[SIZE] = {0};
table[hash(25)] = 25;
      

Dynamic Programming solves problems by breaking them into overlapping subproblems. Stores solutions to avoid recomputation. Common in optimization problems.
int fib[100];
int dp(int n) {
    if (n <= 1) return n;
    if (fib[n]) return fib[n];
    return fib[n] = dp(n-1) + dp(n-2);
}