Unit 2 - Notes

CSE202

Unit 2: Pointers, Reference Variables, Arrays and String Concepts

1. Fundamentals of Pointers

Void Pointer (void*)

A void pointer is a special type of pointer that can hold the address of any data type (int, float, char, user-defined classes). It is often referred to as a generic pointer.

  • Characteristics:
    • It has no associated data type.
    • Dereferencing: You cannot directly dereference a void pointer because the compiler does not know how many bytes to read. It must be explicitly typecast to another pointer type first.
    • Pointer Arithmetic: Standard C++ does not allow arithmetic on void pointers (because the size of the underlying type is unknown), though some compilers (like GCC) allow it as an extension (treating it as char*).

Example:

CPP
int n = 10;
void* ptr = &n;  // Valid: holding int address

// cout << *ptr; // ERROR: Cannot dereference 'void*'
cout << *(static_cast<int*>(ptr)); // Valid: Output 10

Pointer Arithmetic

Pointer arithmetic operations allow moving the pointer to point to different memory locations. Pointers do not increment by "1" literally; they increment by the sizeof(type).

Formula:

  • Valid Operations:
    • Increment (++): Moves to the next element.
    • Decrement (--): Moves to the previous element.
    • Addition (+ integer): Moves forward by elements.
    • Subtraction (- integer): Moves backward by elements.
    • Subtraction of two pointers: Returns the number of elements between two addresses.

Pointer to Pointer (Double Pointer)

A pointer to a pointer is a form of multiple indirection (or a chain of pointers). It is a variable that keeps the address of another pointer.

  • Declaration: int **pptr;
  • Logic: pptr stores the address of ptr. ptr stores the address of variable var. var stores the value.

Example:

CPP
int var = 3000;
int *ptr = &var;
int **pptr = &ptr;

// Accessing value:
// *ptr == 3000
// **pptr == 3000


2. Common Pointer Problems

Improper use of pointers is a common source of bugs in C++.

Dangling Pointer

A pointer points to a memory location that has been deleted (freed) or is no longer valid.

  • Causes:
    1. Deallocating memory using delete without setting the pointer to nullptr.
    2. Returning the address of a local variable from a function (the local variable is destroyed when the function exits).
  • Fix: Always set pointers to nullptr immediately after deletion.

Wild Pointer

A pointer that has been declared but not initialized. It points to an arbitrary (random) memory location.

  • Risk: Dereferencing a wild pointer causes undefined behavior or program crashes.
  • Fix: Initialize pointers to nullptr or a valid address upon declaration.

Null Pointer Assignment

Trying to dereference a pointer that holds a NULL or nullptr value.

  • Consequence: Results in a runtime error (Segmentation Fault).
  • Best Practice: Always check if a pointer is not null before dereferencing. if (ptr != nullptr) { ... }

3. Pointers and References

Differences Between Pointer and Reference Variables

Feature Pointer Reference
Definition A variable that holds the memory address of another variable. An alias (another name) for an existing variable.
Syntax int* ptr = &a; int& ref = a;
Reassignment Can be reassigned to point to different variables. Cannot be reassigned to refer to another variable after initialization.
Memory Has its own memory address and occupies storage. Does not strictly occupy memory (conceptually); shares the address of the referent.
Nullability Can be assigned nullptr. Must reference a valid object; cannot be null.
Indirection Requires * to access the value. Accessed directly like a normal variable.
Arithmetic Supports arithmetic operations. No arithmetic operations performable on the reference address itself.

4. Arrays and Multidimensional Arrays

Declaration and Processing of Multidimensional Arrays

1. Inside main (Procedural approach)

Multidimensional arrays are arrays of arrays. In memory, they are stored in row-major order.

CPP
int main() {
    // Declaration and Initialization
    int matrix[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };

    // Processing (Nested Loops)
    for(int i = 0; i < 2; i++) {
        for(int j = 0; j < 3; j++) {
            cout << matrix[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

2. Inside a Class (OOP approach)

Arrays can be data members of a class.

CPP
class Matrix {
    int data[3][3];
public:
    void input() {
        // Logic to accept input into data[i][j]
    }
    void display() {
        // Logic to print data[i][j]
    }
};


5. The Standard C++ String Class (std::string)

The <string> library provides a safe and convenient way to handle text compared to C-style character arrays (char*).

Defining and Assigning

CPP
#include <string>
// ...
std::string s1;              // Default constructor (empty string)
std::string s2 = "Hello";    // Assignment
std::string s3("World");     // Constructor
std::string s4 = s2;         // Copy constructor

Member Functions

  • length() or size(): Returns the number of characters.
  • at(index): Returns the character at a specific index (with bounds checking).
  • substr(start, length): Returns a substring.
  • find(substring): Returns the index of the first occurrence of the substring.
  • compare(str): Compares two strings lexicographically.

Modifiers of String Class

  • append(str) or +=: Adds text to the end of the string.
  • push_back(char): Adds a single character to the end.
  • pop_back(): Removes the last character.
  • insert(pos, str): Inserts a string at a specified position.
  • erase(pos, len): Removes characters from a specific position.
  • replace(pos, len, str): Replaces a portion of the string.

6. Pointers and Objects (OOP Integration)

Pointer to Objects

Just as we have pointers to primitive types, we can have pointers to class objects.

  • Syntax: ClassName *ptr;
  • Accessing Members: When using a pointer to an object, members are accessed using the arrow operator (->) instead of the dot operator (.).

CPP
class Box {
public:
    int length;
    void show() { cout << length; }
};

int main() {
    Box b;
    b.length = 10;
    
    Box *ptr = &b; // Pointer to object
    ptr->length = 20; // Using arrow operator
    ptr->show();      // Output: 20
}

Array of Objects

An array where each element is an object of a class.

  • Memory: Allocated contiguously.
  • Requirement: If the class has a constructor, it must have a default (no-argument) constructor to initialize the array elements.

CPP
Box boxes[3]; // Creates 3 Box objects
boxes[0].show(); // Access via dot operator

Classes Containing Pointers

When a class contains a pointer as a data member, memory management becomes critical.

  • Deep Copy vs. Shallow Copy: The default copy constructor performs a shallow copy (copies the address only). If one object deletes the memory, the other object has a dangling pointer.
  • Rule of Three: If a class needs to manage dynamic memory (via pointers), you almost always need to implement:
    1. Destructor (to delete memory).
    2. Copy Constructor (to perform Deep Copy).
    3. Copy Assignment Operator (to perform Deep Copy).

Pointer to Data Member

C++ allows pointers to point specifically to members within a class definition, rather than a specific instance's data.

  • Concept: It stores the "offset" of the member within the class layout. It can only be used in conjunction with a specific object.
  • Declaration: DataType ClassName::*PointerName;
  • Assignment: PointerName = &ClassName::MemberName;
  • Access:
    • With object: object.*pointer
    • With object pointer: objPointer->*pointer

Example:

CPP
class Data {
public:
    int val;
};

int main() {
    int Data::*ptrToVal = &Data::val; // Pointer to data member
    
    Data d;
    d.val = 10;
    
    cout << d.*ptrToVal; // Accessing via pointer to member. Output: 10
}

The this Pointer

The this pointer is a special implicit pointer available inside all non-static member functions of a class.

  • Definition: It points to the object that invoked the member function.
  • Type: ClassName * const this (A constant pointer to the class type).

Key Uses:

  1. Distinguish parameters from member variables:
    CPP
        void setValues(int x) {
            this->x = x; // 'this->x' is member, 'x' is parameter
        }
        
  2. Method Chaining: Returning the current object reference allows cascading calls.
    CPP
        Class& add(int a) {
            this->val += a;
            return *this; // Returns reference to current object
        }
        // Usage: obj.add(5).add(10);