Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
33 views22 pages

C++ Notes For CS Engineers Module 5

Module 5 covers pointers, arrays, and exception handling in C++. It explains the use of pointers for memory management, demonstrates basic array operations, and introduces exception handling techniques to manage runtime errors. Additionally, it includes examples of custom exception classes and the use of void pointers for generic programming.

Uploaded by

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

C++ Notes For CS Engineers Module 5

Module 5 covers pointers, arrays, and exception handling in C++. It explains the use of pointers for memory management, demonstrates basic array operations, and introduces exception handling techniques to manage runtime errors. Additionally, it includes examples of custom exception classes and the use of void pointers for generic programming.

Uploaded by

prahladkumar3489
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 22

Module-5.

Pointers Arrays and Exception Handling

Pointer
In C++, pointers are variables that store the memory address of another variable. They
are a powerful feature that allow you to directly interact with memory, enabling dynamic
memory allocation, efficient array handling, and function arguments by reference.

Basic Concepts of Pointers


1. Declaration:
int* ptr; // ptr is a pointer to an integer
2. Initialization:
int x = 10;
int* ptr = &x; // &x gives the address of variable x
3. Dereferencing:
cout << *ptr; // *ptr gives the value stored at the address ptr is pointing to

Example: Basic Pointer Usage


#include <iostream>
using namespace std;

int main() {
int x = 42;
int* ptr = &x;

cout << "Value of x: " << x << endl; // 42


cout << "Address of x: " << &x << endl; // e.g., 0x61fe14
cout << "Value of ptr (address of x): " << ptr << endl; // same as above
cout << "Value at address ptr (dereferencing): " << *ptr << endl; // 42

// Changing the value of x using pointer


*ptr = 100;
cout << "New value of x: " << x << endl; // 100

return 0;
}

1
Example 2
#include <iostream>
using namespace std;

int main()
{
// declare variables
int var1 = 3;
int var2 = 24;
int var3 = 17;

// print address of var1


cout << "Address of var1: "<< &var1 << endl;

// print address of var2


cout << "Address of var2: " << &var2 << endl;

// print address of var3


cout << "Address of var3: " << &var3 << endl;
}
Output
Address of var1: 0x7fff5fbff8ac
Address of var2: 0x7fff5fbff8a8
Address of var3: 0x7fff5fbff8a4

#include <iostream>
#include <string>

int main() {
// Array of pointers to strings
std::string* names[3];

// Initialize the pointers to string literals


names[0] = new std::string("Alice");
names[1] = new std.string("Bob");
names[2] = new std.string("Charlie");

// Access and print the strings using the pointers

2
for (int i = 0; i < 3; ++i) {
std::cout << "Name " << i + 1 << ": " << *names[i] << std::endl;
}

// Deallocate the memory


for (int i = 0; i < 3; ++i) {
delete names[i];
}

return 0;
}
Example 1: C++ Pointers and Arrays
// C++ Program to display address of each element of an array

#include <iostream>
using namespace std;

int main()
{
float arr[3];

// declare pointer variable


float *ptr;

cout << "Displaying address using arrays: " << endl;

// use for loop to print addresses of all array elements


for (int i = 0; i < 3; ++i)
{
cout << "&arr[" << i << "] = " << &arr[i] << endl;
}

// ptr = &arr[0]
ptr = arr;

cout<<"\nDisplaying address using pointers: "<< endl;

// use for loop to print addresses of all array elements

3
// using pointer notation
for (int i = 0; i < 3; ++i)
{
cout << "ptr + " << i << " = "<< ptr + i << endl;
}

return 0;
}
Example 2: Array name used as pointer
// C++ Program to insert and display data entered by using pointer notation.

#include <iostream>
using namespace std;

int main() {
float arr[5];

// Insert data using pointer notation


cout << "Enter 5 numbers: ";
for (int i = 0; i < 5; ++i) {

// store input number in arr[i]


cin >> *(arr + i) ;

// Display data using pointer notation


cout << "Displaying data: " << endl;
for (int i = 0; i < 5; ++i) {

// display value of arr[i]


cout << *(arr + i) << endl ;

return 0;
}

4
Output
Enter 5 numbers: 2.5
3.5
4.5
5
2
Displaying data:
2.5
3.5
4.5
5
2

1. Arrays in C++
An array is a collection of elements of the same type stored in contiguous memory
locations.
Example: Using Arrays
#include <iostream>
using namespace std;

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

cout << "Array elements:" << endl;


for (int i = 0; i < 5; i++) {
cout << "Element at index " << i << ": " << numbers[i] << endl;
}

return 0;
}

Array and Exception Handling in C++ – Explained with Example


In C++, arrays are used to store a fixed-size sequential collection of elements of the
same type. Exception handling is used to manage runtime errors, allowing the program
to continue running or exit gracefully.
While C++ arrays themselves do not automatically throw exceptions when accessing
out-of-bound elements (unlike some other languages), you can manually handle such
cases using exception handling.

5
1. Array Basics
#include <iostream>
using namespace std;

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

cout << "Array elements are:\n";


for (int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}

return 0;
}
Output:
Array elements are:
10 20 30 40 50

2. Exception Handling
C++ uses try, catch, and throw to handle exceptions.
#include <iostream>
using namespace std;

int main() {
try {
int age = -5;
if (age < 0)
throw "Age cannot be negative!";
cout << "Age is " << age;
} catch (const char* msg) {
cout << "Exception: " << msg << endl;
}

return 0;
}
Output:
Exception: Age cannot be negative!

6
3. Combining Array and Exception Handling
Since native C++ arrays don’t check bounds, you can manually check and throw an
exception:
#include <iostream>
using namespace std;

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

cout << "Enter index (0 to 4): ";


cin >> index;

try {
if (index < 0 || index >= 5)
throw out_of_range("Index out of range");

cout << "Element at index " << index << " is " << arr[index] << endl;
} catch (const out_of_range& e) {
cout << "Exception caught: " << e.what() << endl;
}

return 0;
}

Sample Input/Output:
Enter index (0 to 4): 7
Exception caught: Index out of range

void Pointers
A void* pointer (also called a generic pointer) in C++ is a special type of pointer that
can point to any data type, but it cannot be dereferenced directly without
typecasting.

Key Features of void* in C++


• It can store the address of any data type.

7
• You must cast it to the correct data type before dereferencing.
• Useful in low-level programming, writing generic functions, and interfacing with C
libraries.

Example: Using void* in C++


#include <iostream>
using namespace std;

int main() {
int a = 10;
float b = 5.5;
char c = 'X';

void* ptr; // Generic pointer

// Point to integer
ptr = &a;
cout << "Integer value: " << *(static_cast<int*>(ptr)) << endl;

// Point to float
ptr = &b;
cout << "Float value: " << *(static_cast<float*>(ptr)) << endl;

// Point to char
ptr = &c;
cout << "Char value: " << *(static_cast<char*>(ptr)) << endl;

return 0;
}

Output:
Integer value: 10
Float value: 5.5
Char value: X

8
Pointer to a Class
In C++, you can create a pointer to a class object, just like you create a pointer to any
other data type. This is useful when working with dynamic memory or when passing
large objects efficiently.

Key Concepts:
• You declare a class pointer using:
ClassName* ptr;
• You use the -> operator to access members through the pointer.
• Can be used with new to create objects dynamically.

Example: Basic Pointer to Class


#include <iostream>
using namespace std;

class Student {
public:
string name;
int age;

void display() {
cout << "Name: " << name << ", Age: " << age << endl;
}
};

int main() {
Student s1;
s1.name = "Alice";
s1.age = 21;

Student* ptr = &s1; // Pointer to object

// Access members using pointer


ptr->display(); // OR (*ptr).display();

return 0;
}

9
Pointer to Object
A pointer to an object in C++ is a pointer that stores the address of a class instance.
This allows access to the object’s members and methods via the pointer using the ->
operator.
This is useful for:
• Efficient memory use
• Working with dynamic memory (new)
• Polymorphism and virtual functions

Syntax:
ClassName obj;
ClassName* ptr = &obj;
ptr->memberFunction();

Example: Pointer to Object


#include <iostream>
using namespace std;

class Car {
public:
string brand;
int year;

void showDetails() {
cout << "Brand: " << brand << ", Year: " << year << endl;
}
};

int main() {
Car car1;
car1.brand = "Toyota";
car1.year = 2020;

Car* ptr = &car1; // Pointer to object

// Access object members using pointer


ptr->showDetails();

10
return 0;
}

Output:
Brand: Toyota, Year: 2020

Example: Pointer to Dynamically Created Object


#include <iostream>
using namespace std;

class Car {
public:
string brand;
int year;

void input() {
cout << "Enter brand: ";
cin >> brand;
cout << "Enter year: ";
cin >> year;
}

void showDetails() {
cout << "Brand: " << brand << ", Year: " << year << endl;
}
};

int main() {
Car* ptr = new Car; // Dynamic object

ptr->input(); // Access via pointer


ptr->showDetails();

delete ptr; // Free memory


return 0;
}
void Pointers and Arrays
In C++, a void* pointer (generic pointer) can point to any data type, including arrays.

11
However, to access or manipulate array elements, you must cast the void* pointer to
the correct type, since void has no size or direct dereference capability.

Key Concepts:
• A void* pointer can store the address of any data type or array.
• You must cast the void* to the correct type before using it.
• Useful in writing generic functions that can work with any data type.

Example: Using void* to Access an Array


#include <iostream>
using namespace std;

void printIntArray(void* arr, int size) {


int* intArr = static_cast<int*>(arr); // Cast to correct type
for (int i = 0; i < size; i++) {
cout << intArr[i] << " ";
}
cout << endl;
}

void printFloatArray(void* arr, int size) {


float* floatArr = static_cast<float*>(arr); // Cast to correct type
for (int i = 0; i < size; i++) {
cout << floatArr[i] << " ";
}
cout << endl;
}

int main() {
int intArr[] = {1, 2, 3, 4, 5};
float floatArr[] = {1.1, 2.2, 3.3};

cout << "Integer Array: ";


printIntArray(intArr, 5); // Passing int array as void*

cout << "Float Array: ";


printFloatArray(floatArr, 3); // Passing float array as void*

12
return 0;
}

Output:
Integer Array: 1 2 3 4 5
Float Array: 1.1 2.2 3.3

try, throw, and catch in C++ – Explained with Example


C++ uses exception handling to manage runtime errors using three main keywords:

Keywords:
Keyword Purpose
try Defines a block of code to test for errors.
throw Throws an exception when a problem occurs.
catch Defines a block of code that handles the exception.

Syntax:
try {
// Code that may cause an exception
if (error)
throw exception; // throw an exception
}
catch (Type e) {
// Code to handle the exception
}

Example 1: Simple Exception Handling


#include <iostream>
using namespace std;

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

try {
if (b == 0)
throw "Division by zero error!";
cout << "Result: " << a / b << endl;
}

13
catch (const char* msg) {
cout << "Exception caught: " << msg << endl;
}

return 0;
}

Output:
Exception caught: Division by zero error!

Example 2: Using Multiple catch Blocks


#include <iostream>
using namespace std;

int main() {
try {
int x;
cout << "Enter a positive number: ";
cin >> x;

if (x < 0)
throw x;
else if (x == 0)
throw "Zero is not allowed!";
else
cout << "You entered: " << x << endl;
}
catch (int n) {
cout << "Caught an integer exception: " << n << endl;
}
catch (const char* msg) {
cout << "Caught a string exception: " << msg << endl;
}

return 0;
}

14
Sample Output:
Enter a positive number: -5
Caught an integer exception: -5

Creating Your Own Exception Classes


C++ allows you to define custom exception classes to throw and handle application-
specific errors more clearly and robustly. This is done by creating a class (usually derived
from the standard std::exception class) and overriding the what() function to provide an
error message.

Steps to Create a Custom Exception Class:


1. Create a class (optionally inherit from std::exception)
2. Override the what() method to return a message
3. Use throw to raise an instance of that class
4. Use catch to handle the exception

Example: Custom Exception Class in C++


#include <iostream>
#include <exception> // for std::exception
using namespace std;

// Custom exception class


class NegativeNumberException : public exception {
public:
const char* what() const noexcept override {
return "Negative numbers are not allowed!";
}
};

int main() {
int number;

cout << "Enter a positive number: ";


cin >> number;

try {
if (number < 0)
throw NegativeNumberException(); // throw custom exception

15
cout << "You entered: " << number << endl;
}
catch (const NegativeNumberException& e) {
cout << "Exception caught: " << e.what() << endl;
}

return 0;
}

Sample Output:
Enter a positive number: -3
Exception caught: Negative numbers are not allowed!

Exception Handling Techniques in C++


C++ provides flexible exception handling strategies depending on how you want your
program to respond to runtime errors. Here are the 3 common techniques:

1. Terminate the Program (Fail-Fast)


Use Case:
When the error is critical and cannot be recovered from (e.g., corrupted memory, fatal
logic error).
Example:
#include <iostream>
#include <exception>
using namespace std;

int main() {
try {
throw runtime_error("Fatal error! Cannot continue.");
}
catch (const exception& e) {
cerr << "Error: " << e.what() << endl;
exit(EXIT_FAILURE); // Forcefully terminate the program
}

cout << "This won't be printed." << endl;


return 0;

16
}
Output:
Error: Fatal error! Cannot continue.

2. Fix the Error and Continue


Use Case:
When the error is recoverable and you can retry, substitute a default value, or fix input.
Example:
#include <iostream>
#include <exception>
using namespace std;

int safeDivide(int a, int b) {


if (b == 0)
throw invalid_argument("Division by zero");
return a / b;
}

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

try {
cout << "Trying to divide..." << endl;
cout << "Result: " << safeDivide(a, b) << endl;
}
catch (const exception& e) {
cerr << "Error: " << e.what() << endl;
cout << "Fixing the error by setting b = 2\n";
b = 2;
cout << "New result: " << safeDivide(a, b) << endl;
}

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


return 0;
}
Output:
Trying to divide...
Error: Division by zero

17
Fixing the error by setting b = 2
New result: 5
Program continues...

3. Log the Error and Continue


Use Case:
When the program should continue running but errors need to be recorded (e.g., in
logs).
Example:
#include <iostream>
#include <fstream>
#include <exception>
using namespace std;

void riskyOperation(int x) {
if (x < 0)
throw logic_error("Negative number not allowed");
}

int main() {
ofstream log("error_log.txt");

int numbers[] = {5, -1, 10};

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


try {
riskyOperation(numbers[i]);
cout << "Processed: " << numbers[i] << endl;
}
catch (const exception& e) {
cerr << "Handled error: " << e.what() << endl;
log << "Error with input " << numbers[i] << ": " << e.what() << endl;
}
}

log.close();
cout << "Program completed with error logging.\n";
return 0;

18
}
Sample Console Output:
Processed: 5
Handled error: Negative number not allowed
Processed: 10
Program completed with error logging.
Contents of error_log.txt:
Error with input -1: Negative number not allowed

Stack Unwinding
Stack unwinding is a process that occurs when an exception is thrown in C++. During
this process, the program starts to exit from the functions (or stack frames) that were
called before the exception, cleaning up resources such as local variables, destructors,
etc., as it goes. Stack unwinding ensures that destructors of objects created in those
functions are called, which is important for resource management.
How Stack Unwinding Works:
• When an exception is thrown, the control jumps to the nearest catch block.
• As the control leaves each function (due to the exception), destructors for objects
in those functions are called automatically.
• This happens for all the function calls on the call stack until the exception is
caught or the program terminates.

Example: Stack Unwinding with Multiple Function Calls


#include <iostream>
using namespace std;

class Resource {
public:
Resource(const string& name) {
cout << "Resource " << name << " acquired.\n";
}

~Resource() {
cout << "Resource released.\n";
}
};

void funcA() {

19
Resource resA("A");
cout << "Inside funcA\n";
throw runtime_error("Exception thrown from funcA");
}

void funcB() {
Resource resB("B");
cout << "Inside funcB\n";
funcA(); // Call to funcA, which throws an exception
cout << "This won't be printed due to the exception.\n";
}

int main() {
try {
funcB(); // Call to funcB
}
catch (const exception& e) {
cout << "Caught exception: " << e.what() << endl;
}

return 0;
}

Output Explanation:
Resource B acquired.
Inside funcB
Resource A acquired.
Inside funcA
Resource released.
Caught exception: Exception thrown from funcA
Resource released.

Explanation of Stack Unwinding:


1. In funcB:
o A Resource object named resB is created. The message "Resource B
acquired." is printed.
o The program calls funcA(), where another Resource object named resA is
created.

20
2. In funcA:
o The message "Inside funcA" is printed.
o An exception (runtime_error) is thrown.
3. Stack Unwinding:
o Control jumps to the nearest catch block.
o Before leaving funcA, its destructor (~Resource()) is automatically called,
releasing resA.
o The program then continues to unwind and leaves funcB. Before exiting
funcB, its destructor (~Resource()) is called, releasing resB.
4. Exception Handling:
o The exception is caught in the catch block, and the message "Caught
exception: Exception thrown from funcA" is printed.
o The program finishes execution.
Why Stack Unwinding Is Important:
• Automatic resource cleanup: The destructors are called, ensuring that resources
like memory, file handles, network connections, etc., are cleaned up properly.
• Prevents memory leaks: Even if an exception is thrown, the program ensures
that all allocated resources are released.

Custom Resource Management Example: File Handling


Here's an example using file handling:
#include <iostream>
#include <fstream>
using namespace std;

class FileHandler {
public:
fstream file;

FileHandler(const string& filename) {


file.open(filename, ios::out);
if (!file) {
throw runtime_error("File opening failed");
}
cout << "File opened successfully.\n";
}

~FileHandler() {

21
if (file.is_open()) {
file.close();
cout << "File closed.\n";
}
}
};

void writeToFile() {
FileHandler fileHandler("example.txt");
fileHandler.file << "Hello, world!" << endl;
throw runtime_error("An error occurred while writing.");
}

int main() {
try {
writeToFile();
}
catch (const exception& e) {
cout << "Exception caught: " << e.what() << endl;
}

return 0;
}

Output:
File opened successfully.
Exception caught: An error occurred while writing.
File closed.
Explanation:
• A FileHandler object is created in writeToFile(), which opens a file.
• An exception is thrown while writing to the file.
• Stack unwinding ensures that the destructor of FileHandler is called, which
automatically closes the file, even though an exception occurred.

22

You might also like