CS Engineering Gyan

Pointers in C++

Every variable a program creates has to live somewhere inside the computer's memory, and every single memory location has its own unique address. Most of the time, we do not need to think about these addresses at all, because we simply refer to variables by their names and let the compiler handle the rest behind the scenes. Pointers are the feature that lets a C++ programmer step behind that curtain and work with memory addresses directly.

A pointer in C++ is a special kind of variable that does not store an ordinary value like a number or a character. Instead, it stores the memory address of another variable. This might sound abstract at first, but pointers are one of the most practical tools in the language, used everywhere from passing large data efficiently to functions, to building dynamic data structures, to managing memory that is created while the program is actually running.

In this tutorial, you will learn how to declare and initialize pointers, how to use the address-of and dereference operators, how pointer arithmetic works, how pointers relate to arrays, how dynamic memory allocation works using pointers, and what null and dangling pointers are.


What is a Pointer in C++?

A pointer is a variable whose value is the address of another variable, rather than a direct data value. Just like an ordinary variable must be declared with a data type before use, a pointer must also be declared with a type, which tells the compiler what kind of data the pointer is meant to point to.

Example

#include <iostream>

using namespace std;

int main() {

    string channel = "CS Engineering Gyan";

    int subscribers = 50000;

    int* subscriberPtr = &subscribers;

    cout << channel << " subscriber count: " << subscribers << endl;

    cout << channel << " subscriber address stored in pointer: " << subscriberPtr << endl;

    return 0;

}

Output

CS Engineering Gyan subscriber count: 50000

CS Engineering Gyan subscriber address stored in pointer: 0x61ff08

In this example, subscriberPtr is a pointer that stores the memory address of the variable subscribers, rather than storing 50000 directly. The actual address printed will vary each time the program runs, since it depends on where the operating system places the variable in memory.


Declaring and Initializing Pointers

Declaring a pointer requires specifying the data type it will point to, followed by an asterisk and the pointer's name. A pointer is typically initialized using the address-of operator, written as an ampersand, which retrieves the memory address of a variable.

Syntax

dataType* pointerName;

pointerName = &variableName;

Example

#include <iostream>

using namespace std;

int main() {

    string channel = "CS Engineering Gyan";

    int totalViews = 125000;

    int* viewsPtr;

    viewsPtr = &totalViews;

    cout << channel << " total views: " << totalViews << endl;

    cout << channel << " address of totalViews: " << viewsPtr << endl;

    return 0;

}

Output

CS Engineering Gyan total views: 125000

CS Engineering Gyan address of totalViews: 0x61ff0c

A pointer that is declared but not assigned any address contains an unpredictable value, sometimes called a garbage value, so it is good practice to initialize every pointer either with a valid address or with a null value before it is used further.


The Dereference Operator

Once a pointer holds the address of a variable, the asterisk symbol can also be used in a different way, this time to access or modify the value stored at that address. This is known as dereferencing a pointer.

Example

#include <iostream>

using namespace std;

int main() {

    string channel = "CS Engineering Gyan";

    int videoLikes = 800;

    int* likesPtr = &videoLikes;

    cout << channel << " likes through pointer: " << *likesPtr << endl;

    *likesPtr = 950;

    cout << channel << " updated likes: " << videoLikes << endl;

    return 0;

}

Output

CS Engineering Gyan likes through pointer: 800

CS Engineering Gyan updated likes: 950

Here, *likesPtr refers to the value stored at the address the pointer holds. When that value is changed through the pointer, the original variable videoLikes is changed as well, because both are simply two different ways of referring to the exact same memory location.


Pointer Arithmetic

Unlike ordinary variables, pointers support a limited set of arithmetic operations, and these operations behave differently than they would on plain numbers. Adding one to a pointer does not increase the stored address by exactly one byte, but rather by the size of the data type the pointer refers to.

Example

#include <iostream>

using namespace std;

int main() {

    string channel = "CS Engineering Gyan";

    int weeklyUploads[4] = {2, 3, 1, 4};

    int* uploadPtr = weeklyUploads;

    cout << channel << " first week uploads: " << *uploadPtr << endl;

    uploadPtr++;

    cout << channel << " second week uploads: " << *uploadPtr << endl;

    return 0;

}

Output

CS Engineering Gyan first week uploads: 2

CS Engineering Gyan second week uploads: 3

In this example, incrementing uploadPtr moves it forward by the size of one integer, which allows it to point to the very next element of the array. This behavior is what makes pointer arithmetic so closely tied to how arrays work internally.


Pointers and Arrays

An array name in C++ behaves very similarly to a pointer to its first element, which is why arrays and pointers are often discussed together. This close relationship allows array elements to be accessed either using regular index notation or through pointer notation.

Example

#include <iostream>

using namespace std;

int main() {

    string channel = "CS Engineering Gyan";

    int dailyViews[5] = {1500, 1800, 2100, 1950, 2200};

    int* viewsPtr = dailyViews;

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

        cout << channel << " day " << (i + 1) << " views: " << *(viewsPtr + i) << endl;

    }

    return 0;

}

Output

CS Engineering Gyan day 1 views: 1500

CS Engineering Gyan day 2 views: 1800

CS Engineering Gyan day 3 views: 2100

CS Engineering Gyan day 4 views: 1950

CS Engineering Gyan day 5 views: 2200

The expression *(viewsPtr + i) is functionally equivalent to dailyViews[i], since array indexing in C++ is internally translated into pointer arithmetic by the compiler. Understanding this relationship makes it much easier to reason about how arrays are handled in memory.


Pointers and Functions

Pointers are frequently used as function parameters, since passing a pointer allows a function to directly access and modify the original variable from the calling code, rather than working with a separate copy of it.

Example

#include <iostream>

using namespace std;

void doubleSubscribers(int* countPtr) {

    *countPtr = *countPtr * 2;

}

int main() {

    string channel = "CS Engineering Gyan";

    int subscribers = 30000;

    doubleSubscribers(&subscribers);

    cout << channel << " subscribers after campaign: " << subscribers << endl;

    return 0;

}

Output

CS Engineering Gyan subscribers after campaign: 60000

Because the function receives the address of subscribers rather than a copy of its value, any change made inside the function through the pointer is reflected in the original variable once the function finishes executing.


Null Pointers

A null pointer is a pointer that has been deliberately set to point to nothing at all. This is useful for indicating that a pointer is not currently associated with any valid memory address, and checking for a null pointer before dereferencing it is considered a safe programming habit.

Example

#include <iostream>

using namespace std;

int main() {

    string channel = "CS Engineering Gyan";

    int* commentPtr = nullptr;

    if (commentPtr == nullptr) {

        cout << channel << " comment pointer is not assigned yet" << endl;

    }

    int latestComment = 1;

    commentPtr = &latestComment;

    cout << channel << " comment pointer now points to a value: " << *commentPtr << endl;

    return 0;

}

Output

CS Engineering Gyan comment pointer is not assigned yet

CS Engineering Gyan comment pointer now points to a value: 1

Attempting to dereference a null pointer, meaning trying to read or modify the value it supposedly points to, leads to undefined behavior and commonly crashes the program, so this check is an important safeguard in real applications.


Dynamic Memory Allocation

Arrays created in the usual way have a fixed size that must be known while writing the code. Pointers allow a program to request memory while it is actually running, using the new keyword, which is especially useful when the required amount of storage is not known in advance.

Example

#include <iostream>

using namespace std;

int main() {

    string channel = "CS Engineering Gyan";

    int videoCount;

    cout << "Enter number of videos to store: ";

    cin >> videoCount;

    int* videoViews = new int[videoCount];

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

        videoViews[i] = (i + 1) * 500;

    }

    cout << channel << " views for video 1: " << videoViews[0] << endl;

    delete[] videoViews;

    return 0;

}

Output

Enter number of videos to store: 4

CS Engineering Gyan views for video 1: 500

Memory that is allocated dynamically using new must be released manually using delete or delete[] for arrays, once it is no longer needed. Failing to release dynamically allocated memory results in what is known as a memory leak, where the memory remains reserved even though the program can no longer access it.


Dangling Pointers

A dangling pointer is a pointer that still holds the address of memory that has already been freed or that no longer belongs to the program. Using a dangling pointer can lead to unpredictable results, since the memory it points to may have already been reused for something else entirely.

Example

#include <iostream>

using namespace std;

int main() {

    string channel = "CS Engineering Gyan";

    int* tempPtr = new int(100);

    cout << channel << " value before deletion: " << *tempPtr << endl;

    delete tempPtr;

    tempPtr = nullptr;

    cout << channel << " pointer safely reset after deletion" << endl;

    return 0;

}

Output

CS Engineering Gyan value before deletion: 100

CS Engineering Gyan pointer safely reset after deletion

Resetting a pointer to nullptr immediately after freeing its memory is a common and reliable technique for avoiding accidental use of a dangling pointer later in the program.


Pointer to Pointer

C++ also allows a pointer to store the address of another pointer, rather than the address of an ordinary variable. This concept, known as a pointer to pointer, adds another level of indirection and is useful in certain advanced data structures and function designs.

Example

#include <iostream>

using namespace std;

int main() {

    string channel = "CS Engineering Gyan";

    int rank = 1;

    int* rankPtr = &rank;

    int** rankPtrPtr = &rankPtr;

    cout << channel << " rank through double pointer: " << **rankPtrPtr << endl;

    return 0;

}

Output

CS Engineering Gyan rank through double pointer: 1

Here, rankPtrPtr stores the address of rankPtr, which in turn stores the address of rank. Dereferencing it twice, using two asterisks, ultimately retrieves the original value stored in rank.


Advantages and Limitations of Pointers

Advantages Limitations
Allow direct access to and manipulation of memory addresses. Incorrect use can lead to undefined behavior or program crashes.
Enable dynamic memory allocation for flexible, resizable storage. Manual memory management can result in memory leaks if forgotten.
Allow functions to modify the original variables passed to them. Pointer arithmetic errors can cause access to invalid memory locations.

Best Practices While Using Pointers


Common Mistakes Beginners Make

Mistake Correct Practice
Using a pointer without initializing it first. Always assign a valid address or nullptr before using a pointer.
Forgetting to free dynamically allocated memory. Use delete or delete[] for every memory block created with new.
Dereferencing a pointer after its memory has been freed. Reset the pointer to nullptr right after deleting it.
Confusing the address-of operator with the dereference operator. Remember that & retrieves an address, while * accesses the value at that address.

Frequently Asked Interview Questions

  1. What is a pointer in C++?
    A pointer is a variable that stores the memory address of another variable instead of storing a value directly.
  2. What is the difference between the address-of operator and the dereference operator?
    The address-of operator retrieves the memory address of a variable, while the dereference operator accesses the value stored at a given address.
  3. What is a null pointer?
    A null pointer is a pointer that is deliberately set to point to no valid memory location at all.
  4. What is a dangling pointer?
    A dangling pointer is a pointer that still holds the address of memory that has already been freed or is no longer valid.
  5. How is dynamic memory allocated in C++?
    Dynamic memory is allocated using the new keyword and released using delete or delete[] once it is no longer required.
  6. How are pointers related to arrays in C++?
    An array name behaves like a pointer to its first element, and array indexing is internally handled through pointer arithmetic.
  7. Why are pointers used as function parameters?
    Passing a pointer allows a function to directly modify the original variable rather than working on a separate copy of it.
  8. What is a pointer to pointer?
    A pointer to pointer is a pointer that stores the address of another pointer, adding an extra level of indirection.

Summary

Pointers give a C++ programmer direct control over memory, allowing values to be accessed, modified, and shared across different parts of a program without unnecessary copying. Understanding how to declare, initialize, and dereference pointers is an essential step toward writing efficient and flexible C++ code.

The close relationship between pointers and arrays, along with the ability to allocate memory dynamically using new and delete, opens the door to building programs that can adapt their memory usage based on actual requirements at runtime. At the same time, concepts like null pointers and dangling pointers highlight why careful and disciplined memory management is such an important skill in C++.

With a solid understanding of pointers, you are now ready to move deeper into Object-Oriented Programming in C++, where these same ideas of memory and references play an important role in how classes and objects interact with one another.


← Previous: Arrays in C++ Next: Object Oriented Programming →

Home Visit Our YouTube Channel