Pointers

Passing Pointers to Functions

When a function wants to access an existing object in the caller, one clear way is to pass the address of the object.

#include <iostream>

void setToZero(int* target)
{
    // Dereference the pointer to modify the pointed-to object
    *target = 0;
}

int main()
{
    int score = 42;

    setToZero(&score);

    std::cout << score << '\n';

    return 0;
}

The function setToZero takes a pointer called target. The line *target = 0 inside the function follows that pointer to the integer it points at, and writes a 0 into it. In the main function, an integer called score is 42.

When we call setToZero(&score), we take the address of score and pass that to the function. After the function returns, score is 0. The function never takes a copy of 42, it takes an address of score and modifies the real thing.

Note that the pointer parameter is a local variable just like any other. It copies the address that was passed to it.

#include <iostream>

void printAddress(int* location)
{
    std::cout << location << '\n';
}

int main()
{
    int lives = 3;

    // Pass the object's address to the function
    printAddress(&lives);

    return 0;
}

The function printAddress takes a pointer called location. The function body just prints the value which location points at. We pass the address of lives to this function.

This is so the function prints the same thing that &lives would produce. If we write location = 0; in the function, it is okay. No change of any pointer in main occurs because we modified only the local pointer variable location. The actual object at the address can be reached.

A function can not only read from the pointer it is given, but can also write to it.

#include <iostream>

void doubleValue(int* number)
{
    *number = *number * 2;
}

int main()
{
    int points = 10;

    doubleValue(&points);

    std::cout << points << '\n';

    // Return the computed value to the caller
    return 0;
}

The function doubleValue takes a pointer number. Using *number * 2 first reads the integer value being pointed at by number, then multiplies it by 2; then with the following assignment, the result is written to the value being pointed at by the same pointer. In main, an integer called points is 10.

After the function call, its value becomes 20. The read operation, and then the write operation, both occurred through the one address that was passed.

If you want the function to be able to read from the object at the given address but not modify it, you can declare the pointer to const int:

#include <iostream>

void show(const int* source)
{
    std::cout << *source << '\n';
}

int main()
{
    int health = 100;

    // Pass the object's address to the function
    show(&health);

    return 0;
}

The function show takes a parameter called source, which is a pointer to an int that cannot be modified. Because of the type const int, the assignment *source = 1; would be disallowed by the compiler, guaranteeing the caller's integer is unmodified by the function. The function still reads the value of the integer at the given address when printing: 100 is displayed.

Note that a function parameter can take a null pointer. If you are going to write a function like this, you should allow for such cases.

#include <iostream>

void safePrint(const int* source)
{
    // Use nullptr when the pointer refers to no object
    if (source != nullptr)
    {
        std::cout << *source << '\n';
    }
    else
    {
        std::cout << "no value\n";
    }
}

int main()
{
    int level = 5;

    safePrint(&level);
    safePrint(nullptr);

    return 0;
}

safePrint first checks if the pointer source is not 0, or nullptr. If it is not, the integer is printed. If source is 0, then the else block executes and the appropriate message is printed. This if statement allows the function to avoid undefined behavior by attempting to access the object pointed to by a null pointer.

You also need to be able to produce the address you want to pass to a function if your argument is an ordinary object. To do this, you use the address of operator &:

#include <iostream>

void reset(int* target)
{
    *target = 0;
}

int main()
{
    int energy = 50;

    // reset(energy);
    reset(&energy);

    std::cout << energy << '\n';

    return 0;
}

The commented call is disallowed because energy is an int and a pointer to an int is needed. Supply the missing ampersand and the call is legal. After the call, energy is 0.

Exercise: Passing Pointers to FunctionsStarter code, hints and solutionOpenClose
Guided practiceBeginner · 10–15 min

Practice: Passing Pointers to Functions

Work through the mission, reveal hints only when needed, and compare your result with the model solution.

Your challenge

Create another integer initialized to 12 and call safePrint with its address. Predict the extra output and explain the existing nullptr case.

Mission checklist

  1. Run the starter as a baseline, then predict the change requested in Your challenge.
  2. Make that change in your editor; use the lesson-specific hints if you need help.
  3. Compile and run your answer, then compare with the worked solution and explain the result.

Starter program

This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.

#include <iostream>

void safePrint(const int* source)
{
    // Use nullptr when the pointer refers to no object
    if (source != nullptr)
    {
        std::cout << *source << '\n';
    }
    else
    {
        std::cout << "no value\n";
    }
}

int main()
{
    int level = 5;

    safePrint(&level);
    safePrint(nullptr);

    return 0;
}

Hints

Hint 1

safePrint receives either &level or nullptr. Its if branch must establish that source is non-null before reading *source.

Hint 2

Declare another int in main and pass its address to safePrint. Keep the function's non-null check and its separate nullptr test.

Worked solution

The added safePrint(&another) call reads the new value 12 through a const pointer. The existing nullptr call still prints no value. safePrint returns void and neither takes ownership nor modifies either integer.

#include <iostream>

void safePrint(const int* source)
{
    // Use nullptr when the pointer refers to no object
    if (source != nullptr)
    {
        std::cout << *source << '\n';
    }
    else
    {
        std::cout << "no value\n";
    }
}

int main()
{
    int level = 5;

    safePrint(&level);
    safePrint(nullptr);

    int another{12};
    safePrint(&another);
    return 0;
}

Example output

5
no value
12

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.