Cpp Module 5: Pointers, Arrays & Exception Handling – BEU B.Tech CSE 3rd Semester Notes

Complete Module 5 notes for BEU B.Tech CSE 3rd Semester (AI & ML). Covers Pointers, Arrays, Dynamic Arrays, Exception Handling, try-throw-catch, custom exception classes, stack unwinding, and all important exam concepts in simple language.

Updated on

For students of Bihar Engineering University (BEU) pursuing B.Tech 3rd Semester in Computer Science Engineering (CSE) with Artificial Intelligence and Machine Learning (AI/ML) specialization, this module plays a very important role in building core programming skills. The topics covered in this part of the syllabus—pointers, arrays,oid Pointers, Pointer to Class, Pointer to Object, Void Pointer, Arrays. The keywords try, throw and catch. Creating own Exception Classes, Exception Handling Techniques (Terminate the Program, Fix the Error and Continue, Log the Error and Continue), Stack Unwinding.

Module 5 covers three very important concepts in C++: pointers, arrays, and exception handling. These concepts form the foundation for memory management, data handling, and reliable program execution. Pointers help access memory directly and make programs efficient. Arrays allow storing multiple values in continuous memory, making data management simple and structured. Exception handling ensures programs can deal with runtime errors safely without crashing. Together, these topics help students understand how C++ handles memory, data structures, and runtime errors in a professional and secure way.

Pointer

A pointer is a variable that stores the memory address of another variable.
Instead of storing a normal value, a pointer stores where the value is located in memory.
Pointers allow direct memory access, dynamic memory management, faster processing, and flexible data structures. They are one of the most important features of C++ for working with arrays, objects, and classes.

Important Concepts About Pointers

  • A pointer stores an address, not a value.

  • The address-of operator (&) gives the memory location of a variable.

  • The dereference operator (*) is used to access the value stored at the address.

  • Pointers must be declared with the datatype they point to.

  • A pointer can point to variables, arrays, objects, and class members.

  • Pointers allow passing large objects efficiently to functions.

  • Incorrect pointer handling can cause errors like segmentation faults.

Pointer Syntax

datatype *pointerName;

Breakdown:

  • datatype → Type of variable the pointer will point to

  • * → Declares a pointer

  • pointerName → Name of the pointer variable

Example:
int *p;
float *q;
char *ch;

Address-of Operator (&)

The & operator is used to get the address of a variable.

int x = 10;
cout << &x;   // prints memory address of x

Dereference Operator (*)

The * operator is used to access the value stored at the address pointed by the pointer.

int x = 10;
int *p = &x;

cout << *p;   // prints value at address stored in p → 10

Example Program Using Pointer

#include <iostream>
using namespace std;

int main() {
    int x = 25;
    int *ptr;

    ptr = &x;   // pointer stores address of x

    cout << "Value of x = " << x << endl;
    cout << "Address of x = " << &x << endl;
    cout << "Pointer ptr stores = " << ptr << endl;
    cout << "Value at ptr = " << *ptr << endl;

    return 0;
}

Output

Value of x = 25
Address of x = 0x7ffd1234abcd
Pointer ptr stores = 0x7ffd1234abcd
Value at ptr = 25

Code Flow Explanation

  • A normal integer variable x is created with value 25.

  • Pointer ptr is declared to store the address of an int.

  • ptr = &x; stores the address of x inside ptr.

  • ptr now contains the memory location of x.

  • Using *ptr, we get the value stored at that location (25).

  • This demonstrates basic pointer usage: declaration, storing address, and dereferencing.

Types of Pointers in C++

  • Null Pointer

  • Void Pointer

  • Wild Pointer

  • Dangling Pointer

  • Pointer to Object

  • Pointer to Class Member

  • Pointer to Array

  • Double Pointer (Pointer to Pointer)

  • Function Pointer

  • Constant Pointer & Pointer to Constant

Void Pointer

A void pointer is a special pointer in C++ that can store the address of any data type.
Because it has no fixed type, it is known as a generic pointer.
However, since the compiler doesn’t know what type of data it points to, a void pointer cannot be directly dereferenced. It must first be converted (type-cast) into a specific pointer type before accessing the value.

Important Points

  • A void pointer can point to any data type (int, float, char, object, etc.).

  • Also called a generic pointer.

  • Cannot be dereferenced directly because no type information is available.

  • Must be type-casted to the correct type before accessing the value.

  • Useful when the datatype is unknown or changes during runtime.

  • Commonly used in generic functions, memory management, and low-level programming.

Syntax

void *ptr;

Assigning address:

int x = 10;
void *ptr = &x;

Dereferencing requires casting:

cout << *(int*)ptr;

Example: Using Void Pointer

#include <iostream>
using namespace std;

int main() {
    int a = 20;
    float b = 5.5;
    
    void *ptr;      // generic pointer

    ptr = &a;
    cout << "Value of a = " << *(int*)ptr << endl;

    ptr = &b;
    cout << "Value of b = " << *(float*)ptr << endl;

    return 0;
}

Output

Value of a = 20
Value of b = 5.5

Code Flow Explanation

  • A void pointer ptr is declared, which can point to any type.

  • First, the address of integer a is stored in ptr.

  • Since void pointers cannot be dereferenced directly, it is type-cast to (int*) and then dereferenced.

  • Next, the address of float b is assigned to the same pointer.

  • It is type-cast to (float*) before printing the value.

  • This shows how a single void pointer can point to different datatypes.

Uses of Void Pointer

  • Useful in generic programming where datatype is not fixed.

  • Used internally in functions like malloc(), which return void*.

  • Helpful in designing generic data structures.

  • Saves memory because one pointer can point to many types.

  • Improves flexibility of functions that accept multiple datatypes.

Null Pointer

A null pointer is a pointer that does not point to any valid memory location. It is used to show that the pointer is empty, unused, or intentionally not assigned. A null pointer helps avoid accidental access to garbage memory and is useful for checking whether a pointer is safe to use before dereferencing it.

Important Points

  • A null pointer stores zero as its address.

  • It represents an empty or invalid memory reference.

  • Used to check whether a pointer is pointing to something or not.

  • Dereferencing a null pointer causes a runtime error.

  • Commonly used in conditions, linked lists, trees, dynamic memory, and error checking.

  • Helps prevent use of uninitialized or wild pointers.

Syntax

int *ptr = NULL;       // old style
int *ptr = 0;          // valid
int *ptr = nullptr;    // modern C++ (recommended)

Example

#include <iostream>
using namespace std;

int main() {
    int *p = nullptr;   // null pointer

    if (p == nullptr) {
        cout << "Pointer is null, no memory assigned." << endl;
    }

    return 0;
}

Output

Pointer is null, no memory assigned.

Code Flow Explanation

  • A pointer p is initialized with nullptr, meaning it does not point to any variable.

  • The program checks if the pointer is null before using it.

  • Since p is null, the message is printed.

  • This prevents errors like dereferencing an invalid pointer.

Uses of Null Pointer

  • To indicate that a pointer currently has no valid target.

  • To check whether memory allocation failed.

  • To mark the end of linked lists or trees.

  • To safely initialize pointers before assigning them.

  • To avoid using uninitialized or wild pointers.

Pointer to Class

A pointer to class is a pointer variable that stores the address of an object of a class. Just like pointers to int or float, a class pointer helps access members of the class using the arrow operator (->). This allows working with objects dynamically, passing objects to functions efficiently, and creating arrays of objects on the heap. A class pointer does not store the object itself, but the memory location of the object.

Important Points

  • A class pointer stores the address of an object.

  • Declared using the class name followed by *.

  • Members of the object are accessed using the -> operator.

  • Useful for dynamic object creation using new.

  • Helps achieve run-time polymorphism when used with virtual functions.

  • Passing object pointers to functions is faster than passing whole objects.

  • Can also point to different objects at different times.

Syntax

class ClassName {
public:
    int a;
    void show();
};

ClassName *ptr;    // pointer to class

Assigning address:

ClassName obj;
ptr = &obj;

Accessing members:

ptr->a;
ptr->show();

Example: Pointer to Class

#include <iostream>
using namespace std;

class Student {
public:
    int roll;
    void display() {
        cout << "Roll = " << roll << endl;
    }
};

int main() {
    Student s;        // normal object
    s.roll = 10;

    Student *ptr;     // pointer to class
    ptr = &s;         // store address of object

    ptr->display();   // accessing using pointer

    return 0;
}

Output

Roll = 10

Code Flow Explanation

  • A class Student with a data member roll and a display() function is created.

  • An object s is declared normally.

  • A pointer Student *ptr is made to store the address of s.

  • ptr = &s; assigns the object's address to the pointer.

  • Using ptr->display(), we access the member function with the arrow operator.

  • The function runs and prints the roll number.

Uses of Pointer to Class

  • Helps in dynamic object creation using new.

  • Supports run-time polymorphism when pointing to derived objects.

  • Makes object passing efficient because only address is passed, not the whole object.

  • Useful in data structures like linked lists, trees, stacks, and queues.

  • Can point to different objects during execution, increasing flexibility.

Pointer to Object

A pointer to object is a pointer that stores the memory address of an object created either normally or dynamically. Once the object’s address is stored in the pointer, you can access the object’s data members and member functions using the arrow operator (->). Pointer to object is important in situations where objects are created at runtime, passed to functions efficiently, or used for polymorphism.

Important Points

  • A pointer to object stores the address of an object.

  • Declared using the class name followed by *.

  • Members are accessed using -> instead of the dot operator.

  • Can point to objects created normally or using new.

  • Passing objects using pointers is faster than copying whole objects.

  • Used in dynamic memory management and run-time polymorphism.

  • One pointer can point to different objects at different times.

Syntax

ClassName obj;         // normal object
ClassName *ptr;        // pointer to object
ptr = &obj;            // store address of object
ptr->member;           // access member
ptr->function();       // call function

Example: Pointer to Object

#include <iostream>
using namespace std;

class Employee {
public:
    string name;
    int id;

    void show() {
        cout << "Name: " << name << ", ID: " << id << endl;
    }
};

int main() {
    Employee e;        // normal object
    e.name = "Rahul";
    e.id = 101;

    Employee *ptr;     // pointer to object
    ptr = &e;          // store address of object

    ptr->show();       // accessing through pointer

    return 0;
}

Output

Name: Rahul, ID: 101

Code Flow Explanation

  • An Employee object e is created normally.

  • A pointer Employee *ptr is declared to store the address of an Employee object.

  • ptr = &e; assigns address of object e to the pointer.

  • Using ptr->show(), the show() function of the object is called through the pointer.

  • The function prints the name and ID stored in object e.

Uses of Pointer to Object

  • Helps in creating dynamic objects using new.

  • Supports runtime polymorphism by pointing to derived class objects.

  • Makes function calls efficient with large objects.

  • Essential for implementing data structures like linked lists, trees, stacks, queues.

  • Allows flexible object handling since the pointer can change the object it points to.

Pointer to Member Function

A pointer to member function is a special pointer in C++ that stores the address of a member function of a class. Unlike normal function pointers, a member function pointer is always connected to a specific class because member functions need an object to run. The pointer only stores the address of the function, and an object (or pointer to object) is required to call it.

Important Points

  • Stores the address of a member function of a class.

  • Must be declared using the class name.

  • Cannot be called directly—needs an object or object pointer.

  • Accessed using object.*pointer or objectPtr->*pointer.

  • Useful for callback mechanisms and dynamic function selection.

  • Syntax is more complex than normal function pointers.

  • Return type, class name, and parameter types must match exactly.

Syntax

Declaration:

return_type (ClassName::*ptrName)(parameter_list);

Assigning address:

ptrName = &ClassName::functionName;

Calling using object:

(obj.*ptrName)(arguments);

Calling using pointer to object:

(objPtr->*ptrName)(arguments);

Example: Pointer to Member Function

#include <iostream>
using namespace std;

class Test {
public:
    void show() {
        cout << "Member function called using pointer" << endl;
    }
};

int main() {
    Test t;
    Test *ptrObj = &t;

    // pointer to member function
    void (Test::*funcPtr)() = &Test::show;

    // calling through object
    (t.*funcPtr)();

    // calling through object pointer
    (ptrObj->*funcPtr)();

    return 0;
}

Output

Member function called using pointer
Member function called using pointer

Code Flow Explanation

  • The class Test contains a member function show().

  • A member function pointer funcPtr is declared with the class name Test.

  • The address of the function is assigned using &Test::show.

  • Using (t.*funcPtr)(), the function is called through a normal object.

  • Using (ptrObj->*funcPtr)(), the function is called through a pointer to object.

  • Both calls successfully run the show() function and print the output.

Uses of Pointer to Member Function

  • Useful in callback mechanisms inside classes.

  • Allows dynamic selection of which member function to call at runtime.

  • Used in event-driven programming and state machines.

  • Helpful in frameworks and libraries where behavior depends on runtime conditions.

  • Supports flexible code by separating object and function logic.

Array

An array is a collection of elements of the same datatype stored in continuous (contiguous) memory locations and accessed using an index. It allows storing multiple values under one name, making data handling easier. Arrays help manage lists, tables, and large sets of values efficiently.

Important Points

  • Stores multiple values of the same type.

  • Memory is contiguous, meaning elements are stored one after another.

  • Index starts from 0 (first element) and goes till size–1.

  • Accessing elements is fast because each element has a fixed position in memory.

  • The array size must be known before use (static array).

  • Array name represents the address of the first element.

  • Can be 1-D, 2-D, or multi-dimensional.

  • Works well with pointers since array elements sit in continuous memory.

Syntax

datatype arrayName[size];

Examples:

int arr[5];
float marks[10];
char name[20];
Accessing Elements
arr[0] = 10;
arr[1] = 20;
cout << arr[0];

Example Program: Basic Array

#include <iostream>
using namespace std;

int main() {
    int arr[5] = {10, 20, 30, 40, 50};

    cout << "First element = " << arr[0] << endl;
    cout << "Third element = " << arr[2] << endl;

    return 0;
}

Output

First element = 10
Third element = 30

Code Flow Explanation

  • An array of 5 integers is declared and initialized.

  • arr[0] accesses the first element (10).

  • arr[2] accesses the third element (30).

  • Because arrays have contiguous memory, each element is accessed using its index position.

Types of Arrays in C++

  • One-Dimensional Array

  • Two-Dimensional Array (Matrix)

  • Multi-Dimensional Array

  • Array of Objects

  • Pointer and Array combination

One-Dimensional Array

A one-dimensional array stores a list of elements of the same datatype in a single row of continuous memory. It allows you to store multiple values using one variable name and access them using an index. One-dimensional arrays are the simplest form of arrays and are commonly used for storing lists, marks, numbers, names, and other linear data.

Important Points

  • Stores data in a single row.

  • All elements must be of the same datatype.

  • Index starts from 0 and ends at size – 1.

  • Memory is continuous, allowing fast element access.

  • Array size must be declared before use.

  • Array name gives the address of the first element.

  • Can be initialized during declaration or later.

Syntax

datatype arrayName[size];

Example:

int arr[5];

Initialization

int arr[5] = {10, 20, 30, 40, 50};

Or partially:

int arr[5] = {10, 20};   // remaining elements become 0

Or at runtime:

int arr[3];
arr[0] = 5;
arr[1] = 10;
arr[2] = 15;

Accessing Elements

arr[0];
arr[1];
arr[4];

Example Program

#include <iostream>
using namespace std;

int main() {
    int num[5] = {5, 10, 15, 20, 25};

    cout << "First element = " << num[0] << endl;
    cout << "Last element = " << num[4] << endl;

    return 0;
}

Output

First element = 5
Last element = 25

Code Flow Explanation

  • A one-dimensional array num of size 5 is declared.

  • It stores values: 5, 10, 15, 20, 25 in continuous memory.

  • num[0] gives the first element (5).

  • num[4] gives the last element (25).

  • Elements are accessed using index numbers because the array uses fixed positions in memory.

Uses of One-Dimensional Arrays

  • Storing lists like marks, prices, scores, ages, etc.

  • Processing sequential data.

  • Searching and sorting operations.

  • Basic data structures like stacks and queues use 1-D arrays internally.

  • Passing multiple values to functions in a simple way.

Two-Dimensional Array

A two-dimensional array stores data in the form of rows and columns, similar to a table or matrix. It can be imagined as an array of one-dimensional arrays. Two-dimensional arrays are useful for storing structured data like matrices, tables, grids, marksheets, and seating charts. Each element is accessed using two indexes: one for the row and one for the column.

Important Points

  • Stores data in rows and columns (matrix form).

  • All elements must be of the same datatype.

  • Declared using two sizes: rows and columns.

  • Indexing starts from 0 for both rows and columns.

  • Memory is stored continuously row-wise.

  • Access requires two indexes: array[row][column].

  • Useful for matrices, 2D tables, and structured datasets.

Syntax

datatype arrayName[rows][columns];

Example:

int matrix[3][3];

Initialization

Complete initialization:

int matrix[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};

Single-line initialization:

int matrix[2][3] = {1, 2, 3, 4, 5, 6};

Partial initialization:

int matrix[2][3] = { {1, 2}, {3} };
Accessing Elements
matrix[0][0];   // first row, first column
matrix[1][2];   // second row, third column

Example Program

#include <iostream>
using namespace std;

int main() {
    int arr[2][3] = {
        {10, 20, 30},
        {40, 50, 60}
    };

    cout << "Element at [0][1] = " << arr[0][1] << endl;
    cout << "Element at [1][2] = " << arr[1][2] << endl;

    return 0;
}

Output

Element at [0][1] = 20
Element at [1][2] = 60

Code Flow Explanation

  • A 2×3 matrix is created with 2 rows and 3 columns.

  • {10, 20, 30} is the first row, {40, 50, 60} is the second row.

  • Index [0][1] accesses row 0, column 1 (value = 20).

  • Index [1][2] accesses row 1, column 2 (value = 60).

  • Two-dimensional arrays allow storing and accessing tabular data efficiently.

Uses of Two-Dimensional Arrays

  • Storing matrices in mathematics.

  • Storing tables such as marksheets or bills.

  • Creating board games like chess, tic-tac-toe, sudoku.

  • Representing grids, pixel data, and maps in programming.

  • Handling multi-dimensional datasets in applications.

Multi-Dimensional Array

A multi-dimensional array is an array with more than two dimensions, such as 3-D, 4-D, or higher. While a 2-D array stores data in rows and columns, a multi-dimensional array stores data in multiple levels, similar to stacking multiple 2-D tables on top of each other. Multi-dimensional arrays are useful in advanced problems like 3-D matrices, cube-shaped data, scientific calculations, graphics, and simulations.

Important Points

  • Contains more than two dimensions (3-D, 4-D, etc.).

  • All elements must be of the same datatype.

  • Elements are stored in continuous memory, row-wise.

  • Access requires multiple indexes: arr[i][j][k]....

  • Can represent cube-like or layered structures.

  • Used for applications like 3-D modeling, simulations, and scientific data.

Syntax

datatype arrayName[size1][size2][size3] ... ;

Example of a 3-D array:

int arr[2][3][4];

This means:

  • 2 blocks

  • each block has 3 rows

  • each row has 4 columns

Initialization

int arr[2][2][2] = {
    {
        {1, 2},
        {3, 4}
    },
    {
        {5, 6},
        {7, 8}
    }
};

Accessing Elements

arr[0][1][1];   // first block, second row, second column
arr[1][0][0];   // second block, first row, first column

Example Program

#include <iostream>
using namespace std;

int main() {
    int cube[2][2][2] = {
        {
            {1, 2},
            {3, 4}
        },
        {
            {5, 6},
            {7, 8}
        }
    };

    cout << "cube[0][1][0] = " << cube[0][1][0] << endl;
    cout << "cube[1][0][1] = " << cube[1][0][1] << endl;

    return 0;
}

Output

cube[0][1][0] = 3
cube[1][0][1] = 6

Code Flow Explanation

  • A 3-D array cube contains 2 blocks, each with 2 rows and 2 columns.

  • Each element is accessed using three indexes.

  • cube[0][1][0] means block 0 → row 1 → column 0 → value 3.

  • cube[1][0][1] means block 1 → row 0 → column 1 → value 6.

  • Multi-dimensional arrays allow storing multi-layered data structures.

Uses of Multi-Dimensional Arrays

  • 3-D mathematical matrices.

  • Representing cube structures.

  • Pixel values in 3-D graphics or animations.

  • Scientific calculations and simulations.

  • Storing layered data like images, maps, or games.

  • Representing positions in 3-D space (x, y, z).

Dynamic Arrays

A dynamic array is an array whose size is decided at runtime, not at compile time. It is created using dynamic memory allocation with the new operator. Dynamic arrays are useful when the number of elements is not known in advance. The memory for a dynamic array is taken from the heap, and it remains until it is manually released using delete.

Important Points

  • Size is decided during program execution, not before.

  • Created using the new operator.

  • Memory is stored in the heap, not in the stack.

  • Accessing elements is the same as a normal array.

  • Must release memory using delete[] to avoid memory leaks.

  • Useful when the required size changes or depends on user input.

  • Can be combined with classes, pointers, and functions easily.

Syntax

datatype *arrayName = new datatype[size];

Releasing Memory

delete[] arrayName;

Example: Creating a Dynamic Array

#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "Enter size: ";
    cin >> n;

    int *arr = new int[n];   // dynamic array

    for (int i = 0; i < n; i++) {
        arr[i] = i + 1;
    }

    cout << "Array elements: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }

    delete[] arr;   // freeing memory

    return 0;
}

Output (example)

Enter size: 5
Array elements: 1 2 3 4 5

Code Flow Explanation

  • Program asks the user for the array size at runtime.

  • Memory is created dynamically using new int[n].

  • Values are stored in the dynamically allocated array.

  • Elements are printed using normal indexing.

  • Memory is released using delete[] arr to avoid memory leaks.

  • Dynamic arrays allow flexible sizing based on program needs.

Uses of Dynamic Arrays

  • When size is not fixed or depends on user input.

  • For large data that cannot fit in stack memory.

  • Dynamic data structures like dynamic lists, matrices, and graphs.

  • Useful in competitive programming and large applications.

  • Helpful when creating objects dynamically using classes.

Relationship Between Pointers and Arrays

In C++, the name of an array acts like a constant pointer to the first element of the array. This means arrays and pointers are closely related, and in many situations, a pointer can be used to access array elements just like array indexing. Although arrays and pointers are not the same thing, they behave similarly because the array name holds the base address, and pointer arithmetic lets you move through the array.

Important Points

  • The array name represents the address of the first element.

  • If arr is an array, then arr is equal to &arr[0].

  • A pointer can store the address of an array’s first element.

  • Array elements can be accessed using either indexing (arr[i]) or pointer notation (*(ptr + i)).

  • Arrays use continuous memory, so pointer arithmetic works naturally.

  • You cannot change the array name (it is a constant pointer), but you can change a pointer.

  • Passing arrays to functions actually sends a pointer to the first element.

Syntax

int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr;   // ptr stores address of first element

Accessing Elements (Two Ways)

Using array indexing:

arr[2];

Using pointer arithmetic:

*(ptr + 2);

Both give the same result.

Example

#include <iostream>
using namespace std;

int main() {
    int arr[5] = {10, 20, 30, 40, 50};

    int *ptr = arr;   // pointer stores base address

    cout << "arr[0] = " << arr[0] << endl;
    cout << "ptr[0] = " << ptr[0] << endl;

    cout << "arr[2] = " << arr[2] << endl;
    cout << "*(ptr + 2) = " << *(ptr + 2) << endl;

    return 0;
}

Output

arr[0] = 10
ptr[0] = 10
arr[2] = 30
*(ptr + 2) = 30

Code Flow Explanation

  • The array arr contains 5 integers in continuous memory.

  • When ptr = arr; is used, the pointer stores the address of the first element.

  • ptr[0] and arr[0] give the same value because they refer to the same location.

  • *(ptr + 2) accesses the third element, similar to arr[2].

  • This shows that pointers can traverse arrays using pointer arithmetic.

Why Arrays and Pointers Are Related

  • The array name acts like a constant pointer to element 0.

  • Pointer arithmetic allows moving from one element to the next.

  • Passing an array to a function sends a pointer, not the full array.

  • Many operations with arrays depend on pointer behavior.

Exception Handling

Exception handling in C++ is a mechanism that allows a program to detect and handle runtime errors without crashing. Instead of stopping the program immediately when an error occurs, C++ gives you a way to “catch” the error and handle it safely. This makes programs more reliable, secure, and user-friendly. Exception handling separates normal program logic from error-handling logic so that errors do not disrupt the flow of the program.

Important Points

  • Handles runtime errors like division by zero, invalid input, file failure, etc.

  • Uses three main keywords: try, throw, and catch.

  • try block contains code where an exception might occur.

  • throw keyword sends (raises) the exception when an error is detected.

  • catch block receives and processes the thrown exception.

  • More than one catch block can be used.

  • You can create your own exception classes.

  • Exception handling improves program stability and reliability.

Keywords Used in Exception Handling

try

A try block contains the code where an exception may occur.
If an exception happens inside the try block, control jumps to the matching catch block.

Syntax:
try {
    // code that might cause an error
}

throw

The throw keyword generates an exception and transfers control to the catch block.

Syntax:
throw value;   // value can be int, string, or object

catch

A catch block handles the exception thrown by the throw statement.

Syntax:
catch(type variable) {
    // handling code
}

Basic Example Division by Zero

#include <iostream>
using namespace std;

int main() {
    int a = 10, b = 0;

    try {
        if (b == 0) {
            throw "Division by zero error";
        }
        cout << a / b;
    }
    catch (const char *msg) {
        cout << msg;
    }

    return 0;
}

Output

Division by zero error

Code Flow Explanation

  • The try block checks if b is zero.

  • Since b is zero, throw sends an exception message.

  • The catch block receives this message and prints it.

  • The program does not crash; it handles the error safely.

Creating Own Exception Classes

C++ allows you to create your own exception classes to handle errors in a more meaningful and organized way. Instead of throwing simple integers or strings, you can create a class that stores detailed information about the error. This gives better control, clearer messages, and more professional error handling. User-defined exception classes work just like built-in exceptions but give you the freedom to define your own error type, message, and behavior.

Important Points

  • A user-defined exception class is created like a normal class.

  • It usually stores an error message or details about the problem.

  • Objects of this class are thrown using throw.

  • The catch block receives the object and accesses its data members.

  • Helps in creating structured and readable error-handling systems.

  • Can include functions to display custom error messages.

  • Allows multiple types of custom errors in large programs.

Basic Syntax

class ExceptionName {
public:
    string message;

    ExceptionName(string msg) {
        message = msg;
    }
};

Throwing the exception:

throw ExceptionName("Error message");

Catching it:

catch (ExceptionName e) {
    cout << e.message;
}

Example: Creating and Using a Custom Exception Class

#include <iostream>
using namespace std;

class MyException {
public:
    string message;

    MyException(string msg) {
        message = msg;
    }
};

int main() {
    int age;

    try {
        cout << "Enter age: ";
        cin >> age;

        if (age < 0) {
            throw MyException("Age cannot be negative");
        }

        cout << "Valid age entered: " << age << endl;
    }
    catch (MyException e) {
        cout << "Exception: " << e.message << endl;
    }

    return 0;
}

Output (example)

Enter age: -5
Exception: Age cannot be negative

Code Flow Explanation

  • A custom exception class MyException is created.

  • It stores an error message inside the message variable.

  • In the try block, the program takes input for age.

  • If age is negative, throw MyException("Age cannot be negative") sends a custom exception object.

  • The catch block receives the object and prints its message.

  • This allows clean and meaningful error handling without crashing the program.

Why Use Custom Exception Classes?

  • Gives more descriptive and meaningful error messages.

  • Helps manage different types of errors with different classes.

  • Provides better structure for large programs.

  • Can include extra information like error codes, timestamps, or recovery steps.

  • Improves debugging and error tracing.

Multiple Catch Blocks

Multiple catch blocks allow you to handle different types of exceptions separately.
A single try block can have more than one catch block, and each catch block handles a different datatype or different error condition.
This gives more control, because each type of exception can be processed in its own way.

Important Points

  • One try block can be followed by multiple catch blocks.

  • Only the matching catch block gets executed.

  • Order matters: more specific exceptions should be written before general ones.

  • Helps handle different error types individually.

  • You cannot have two catch blocks for the same exact type.

Syntax

try {
    // risky code
}
catch (int x) {
    // handles integer exception
}
catch (char c) {
    // handles character exception
}
catch (string s) {
    // handles string exception
}

Example

#include <iostream>
using namespace std;

int main() {
    try {
        throw 10;   // throws an int
    }
    catch (int x) {
        cout << "Caught integer: " << x << endl;
    }
    catch (char c) {
        cout << "Caught character: " << c << endl;
    }
    catch (string s) {
        cout << "Caught string: " << s << endl;
    }

    return 0;
}

Output

Caught integer: 10

Explanation

  • The try block throws an integer (10).

  • The catch block that matches int runs.

  • Other catch blocks are ignored.

catch(...) (General Catch Block)

The catch(...) block is a catch-all handler.
It catches any type of exception that does not match previous catch blocks.
It is used as the last catch block for unexpected or unknown errors.

Important Points

  • Catches any exception if no other catch matches.

  • Must always appear last in the sequence of catch blocks.

  • Used for unknown or unexpected errors.

  • Does not provide information about the actual exception type.

Syntax

catch(...) {
    // handles any exception
}

Example

#include <iostream>
using namespace std;

int main() {
    try {
        throw 5.5;   // throws double
    }
    catch (int x) {
        cout << "Integer exception" << endl;
    }
    catch (...) {
        cout << "Unknown exception caught" << endl;
    }

    return 0;
}

Output

Unknown exception caught

Explanation

  • The try block throws a double value.

  • There is no catch block for double.

  • The catch(...) block catches it because it handles all exceptions.

When to Use Multiple Catch Blocks and catch(...)

Use multiple catch blocks when:

  • You want to handle different error types differently.

  • You need specific messages for int, string, float, custom exception, etc.

Use catch(...) when:

  • You want to catch all remaining unknown exceptions.

  • You want to prevent the program from crashing due to unexpected errors.

Exception Handling Techniques

Exception handling techniques are the different ways a program can respond after detecting an error during execution. Depending on how serious the error is and what the program needs to do next, the programmer can choose to stop the program, fix the problem, or record the error and continue running. These techniques help in managing runtime errors safely without crashing the program.

1. Terminate the Program

This technique is used when the error is serious, and the program cannot continue safely.
After catching the exception, the program displays a message and stops execution.

When used

  • Division by zero

  • File not found

  • Invalid memory access

  • Critical data missing

Example

try {
    throw "Fatal Error!";
}
catch (const char *msg) {
    cout << msg;
    // stop the program
}

The program ends after the catch block because the error is not recoverable.


2. Fix the Error and Continue

In this technique, the catch block corrects the mistake and allows the program to keep running.
This is used when the error is simple or predictable, and you can take corrective action.

When used

  • Invalid user input

  • Negative values when only positive values are allowed

  • Asking user to retry the operation

Example

int x;

try {
    cout << "Enter positive number: ";
    cin >> x;

    if (x < 0) {
        throw x;
    }

    cout << "You entered: " << x;
}
catch (int n) {
    cout << "Negative value entered. Converting to positive." << endl;
    x = -n;   // fixing the error
    cout << "Using fixed value: " << x;
}

The program corrects the value and continues.


3. Log the Error and Continue

In this technique, the error is recorded (logged) for reference, but the program continues running normally.
The catch block does not fix the problem—it only stores or displays the error message.

When used

  • Large applications where errors must be recorded

  • Background tasks

  • Server applications

  • Situations where small errors should not stop the entire program

Example

try {
    throw "File missing";
}
catch (const char *msg) {
    cout << "Error logged: " << msg << endl;
    // continue program normally
}

The program logs the error and continues executing the remaining code.

Summary of Techniques

  • Terminate the Program: stop execution after handling a serious error.

  • Fix the Error and Continue: correct the mistake and keep running.

  • Log the Error and Continue: record the error without interrupting program flow.

These techniques help programmers handle different types of errors in the most appropriate way based on the situation.

Simple Example of All Three Exception Handling Techniques

#include <iostream>
using namespace std;

int main() {
    int n;

    try {
        cout << "Enter number: ";
        cin >> n;

        // Technique 1: Terminate the Program
        if (n == 0) {
            throw 0;   
        }

        // Technique 2: Fix the Error and Continue
        if (n < 0) {
            throw -1;  
        }

        // Technique 3: Log the Error and Continue
        if (n > 100) {
            throw "big";   
        }

        cout << "Valid number: " << n << endl;
    }

    catch (int x) {
        if (x == 0) {
            cout << "Error: Zero is not allowed. Program terminated." << endl;
            return 0;   // terminate program
        }
        if (x == -1) {
            cout << "Negative number fixed to positive." << endl;
            n = -n;     // fix error
            cout << "Fixed value: " << n << endl;
        }
    }

    catch (const char *msg) {
        cout << "Warning: Number too large (logged). Continuing..." << endl;
    }

    cout << "Program continues..." << endl;

    return 0;
}

Output Examples (Short)

If user enters 0

Error: Zero is not allowed. Program terminated.

If user enters -5

Negative number fixed to positive.
Fixed value: 5
Program continues...

If user enters 150

Warning: Number too large (logged). Continuing...
Program continues...

If user enters 20

Valid number: 20
Program continues...

Stack Unwinding

Stack unwinding is the process in C++ where, after an exception is thrown, the program goes backward through the function call stack, destroying all the local variables of each function, until it finds a matching catch block.
It “unwinds” the stack by cleaning up each function one by one, ensuring no memory is leaked and all objects are properly destroyed.
This happens automatically whenever an exception is thrown.

Important Points

  • Occurs when an exception is thrown inside a nested function call.

  • C++ destroys all local variables in each function while moving backward.

  • Stops unwinding when it finds a matching catch block.

  • Ensures proper cleanup of objects before exiting the function.

  • Prevents memory leaks and ensures safe exit from functions.

  • If no catch block is found, the program terminates.

  • Only automatic (local) objects are destroyed; dynamic memory must be manually handled.

Simple Concept Explanation

Imagine three functions: A → B → C
If an error happens in C and C cannot handle it:

  • C ends and its local variables are destroyed

  • Control moves to B → B’s local variables destroyed

  • Then to A → if A has the correct catch block, the error is handled

  • This backward cleaning is called Stack Unwinding

Example Program

#include <iostream>
using namespace std;

void C() {
    cout << "Inside C" << endl;
    throw 1;             // exception thrown here
}

void B() {
    cout << "Inside B" << endl;
    C();                // call C
}

void A() {
    cout << "Inside A" << endl;
    B();                // call B
}

int main() {
    try {
        A();            // start from A
    }
    catch (int x) {
        cout << "Exception caught in main" << endl;
    }
    return 0;
}

Output

Inside A
Inside B
Inside C
Exception caught in main

Code Flow (Stack Unwinding Explained)

  • main() calls A

  • A calls B

  • B calls C

  • C throws an exception

  • C ends → its local variables destroyed

  • Control goes back to B → B ends → its variables destroyed

  • Then A ends → its variables destroyed

  • Finally reaches main → catch block handles the exception

  • This backward cleanup is stack unwinding

Why Stack Unwinding Is Important

  • Prevents memory leaks by destroying objects in reverse order.

  • Ensures safe cleanup of resources when an error occurs.

  • Helps the program exit from multiple function calls safely.

  • Makes exception handling reliable across nested function calls.

Module 5 of the Bihar Engineering University (BEU) syllabus is one of the most important parts of the B.Tech 3rd Semester CSE course for all branches including AI, ML, Data Science, Software Engineering, and CSIT. Understanding pointers, arrays, dynamic memory, and exception handling helps students build strong programming fundamentals that are essential for advanced subjects and real-world coding. These notes are prepared in a simple and clear format to make learning easier for BEU students and to support quick exam revision. With a proper grasp of Module 5 concepts, CSE students can confidently move forward into more complex topics and practical applications in upcoming semesters.

NotesNav

NotesNav

Madhepura college of engineering

Frequently Asked Questions