Understanding Pointers in C++

Karim Adel
Nov 11
3 min read
post_comment1 Comments
post_like2 Likes

In the world of programming, pointers serve as powerful tools, offering a way to manipulate memory addresses and enabling the creation of dynamic data structures. In C++, pointers are symbolic representations of addresses, facilitating call-by-reference simulations and the efficient handling of data structures like arrays. This article aims to delve into the syntax, applications, and intricacies of pointers in C++.

#Basics of Pointers

#Syntax:

datatype *var_name;
int *ptr;   // ptr can point to an address holding int data

In C++, a pointer variable is declared with the datatype it will point to, such as an int or string. This pointer is intended to store the address of another variable of the specified datatype.

#How Pointers Work:

  1. Define a pointer variable.
  2. Assign the address of a variable to the pointer using the unary operator (&).
  3. Access the value stored in the address using the unary operator (*) to retrieve the variable's value.

Associating a data type with a pointer is crucial, as it determines the size of the data being pointed to. When a pointer is incremented, it moves by the size of the data type it points to.

int var = 20;
int* ptr = &var;
cout << "Value at *ptr = " << *ptr << "\n";

Output:

Value at *ptr = 20

#References and Pointers in C++

C++ provides three ways to pass arguments to functions:

  1. Call-By-Value
  2. Call-By-Reference with a Pointer Argument
  3. Call-By-Reference with a Reference Argument
int square1(int n) { /*...*/ }
void square2(int* n) { /*...*/ }
void square3(int& n) { /*...*/ }

By default, C++ uses call-by-value. Changes made to parameters in the called function do not reflect in the original variables. To modify the original directly and avoid cloning overhead, pass-by-reference with pointer or reference arguments is employed.

void fn() {
    int n1 = 8;
    cout << "address of n1 in main(): " << &n1 << "\n";
    cout << "Square of n1: " << square1(n1) << "\n";
    cout << "No change in n1: " << n1 << "\n";

    int n2 = 8;
    cout << "address of n2 in main(): " << &n2 << "\n";
    square2(&n2);
    cout << "Square of n2: " << n2 << "\n";
    cout << "Change reflected in n2: " << n2 << "\n";

    int n3 = 8;
    cout << "address of n3 in main(): " << &n3 << "\n";
    square3(n3);
    cout << "Square of n3: " << n3 << "\n";
    cout << "Change reflected in n3: " << n3 << "\n";
}

Output:

address of n1 in main(): 0x7fffa7e2de64
address of n1 in square1(): 0x7fffa7e2de4c
Square of n1: 64
No change in n1: 8

address of n2 in main(): 0x7fffa7e2de68
address of n2 in square2(): 0x7fffa7e2de68
Square of n2: 64
Change reflected in n2: 64

address of n3 in main(): 0x7fffa7e2de6c
address of n3 in square3(): 0x7fffa7e2de6c
Square of n3: 64
Change reflected in n3: 64

#Function Pointers in C++

C++ supports function pointers, allowing the storage and manipulation of functions as variables.

int square(int n) { /*...*/ }
int (*ptr)(int) = &square;
cout << "Square of 5 is: " << ptr(5) << "\n";

Output:

Square of 5 is: 25

#Array Name as Pointers

In C++, the name of an array acts as a constant pointer, holding the address of its first element. This concept is fundamental for understanding pointer arithmetic and array manipulation.

void fn() {
    int val[3] = {5, 10, 20};
    int* ptr = val;
    cout << "Elements of the array are: " << ptr[0] << " " << ptr[1] << " " << ptr[2] << "\n";
}

Output:

Elements of the array are: 5 10 20

#Pointer Arithmetic

A limited set of arithmetic operations can be performed on pointers, such as incrementing, decrementing, and adding or subtracting integers. Pointer arithmetic is especially meaningful when applied to arrays.

void fn() {
    int v[3] = {10, 100, 200};
    int* ptr = v;
    for (int i = 0; i < 3; i++) {
        cout << "Value at ptr = " << ptr << "\n";
        cout << "Value at *ptr = " << *ptr << "\n";
        ptr++;
    }
}

Output:

Value at ptr = 0x7ffe5a2d8060
Value at *ptr = 10
Value at ptr = 0x7ffe5a2d8064
Value at *ptr = 100
Value at ptr = 0x7ffe5a2d8068
Value at *ptr = 200

#Advanced Pointer Notation

Understanding pointer notation for two-dimensional arrays and delving into the representation of data in memory enhances a programmer's ability to work with complex data structures.

int nums[2][3] = { { 16, 18, 20 }, { 25, 26, 27 } };
int value = *(*(nums + i) + j);

#Pointers and Strings

String literals are arrays containing null-terminated character sequences. Pointers can be used to access and manipulate characters within string literals.

char* ptr = "karim";
char x = *(ptr + 3);
char y = ptr[3];

Output:

x: i
y: i

#Pointers to Pointers

C++ allows the creation of pointers to pointers, providing a higher level of indirection. This is particularly useful in scenarios where multiple layers of data or pointers need to be managed.

char a;
char* b;
char** c;
a = 'g';
b = &a;
c = &b;

#Void Pointers

Void pointers, while not directly dereferencable, offer great flexibility by being able to point to data of any type. Transforming them into a concrete pointer type before dereferencing is necessary.



void increase(void* data, int ptrsize) {
    if (ptrsize == sizeof(char)) {
        char* ptrchar = static_cast<char*>(data);
        (*ptrchar)++;
        cout << "*data points to a char" << "\n";
    } else if (ptrsize == sizeof(int)) {
        int* ptrint = static_cast<int*>(data);
        (*ptrint)++;
        cout << "*data points to an int" << "\n";
    }
}

void fn() {
    char c = 'x';
    int i = 10;
    increase(&c, sizeof(c));
    cout << "The new value of c is: " << c << "\n";
    increase(&i, sizeof(i));
    cout << "The new value of i is: " << i << "\n";
}

Output:

*data points to a char
The new value of c is: y
*data points to an int
The new value of i is: 11

#Invalid Pointers and NULL Pointers

Understanding the concept of invalid pointers (uninitialized or out-of-bounds) and null pointers is crucial for writing robust and error-free code.

int *ptr1;
int arr[10];
int *ptr2 = arr + 20;

#Advantages of Pointers

  1. Code Reduction and Improved Performance:

    • Pointers streamline code, enhancing performance.
    • Facilitate the manipulation of strings, trees, arrays, structures, and functions.
  2. Multiple Return Values:

    • Enable functions to return multiple values, enhancing flexibility.
  3. Memory Access:

    • Provide direct access to memory locations, crucial for efficient memory management.

In conclusion, mastering pointers in C++ is essential for unlocking the language's full potential. Whether dealing with dynamic data structures, function manipulation, or efficient memory access, a solid understanding of pointers elevates a programmer's capabilities.

You are not logged in.