InfoCode

C++ · Functions

Functions and parameters

Trace an int copy, an int reference and a received return value using the same data, then check which variables change after each call.

Written by InfoCode · Technical references below

Practice this topic in the app Read the lesson in the app

Function inputs, processing and return values

Imagine a function is a vending machine: you insert money (arguments), the machine runs according to a fixed procedure (the function body), and finally dispenses a drink (the return value). Each time you insert a different denomination, you get a different result.

When to use it? When the same piece of logic needs to be used several times in a program, extract it into a function to avoid copy-pasting.

Pass by value vs pass by reference: an ordinary parameter is a "photocopy"—changing it inside the function does not affect the outside; a reference parameter with & added is the "original"—changing it inside the function also changes the outside.

Pointer parameters: void update(int* p) receives a copy of the address; through *p you can modify the object the address points to; but directly assigning another address to p only changes the pointer copy inside the function. You can use a const reference to pass in a large read-only object and reduce copying. Defining a function should also state the parameter constraints, the return result, and how null pointers are handled.

Pass by value vs pass by reference: trace the same input

A function receives inputs and executes its body. If it returns a value, the caller can receive that result. Start with n=4: an ordinary int parameter holds a copy, so changing it does not change the caller's n. An int& parameter is another name for the caller's object. Predict n and result before tracing the calls.

StepInside the functionn afterwardresult
int n = 4;No call yet4Not declared
int result = byValue(n);Copy 4, add 2, return 646
byReference(n);Add 2 to n through the reference66
n = byValue(n);Copy 6, add 2, then assign returned 8 to n86

First error: assuming a parameter with the same name as an outer variable must be the same object. Check its declared type and binding. Second error: assuming a call automatically updates the original variable just because it returns a value. Receive or explicitly assign the result if it should be saved.

This example concerns ordinary int value parameters, not a claim that all by-value functions lack external effects: a copied pointer can still point to an external object. Assigning to a bound reference changes its object rather than rebinding the reference. Returning a value is often clear when calculating a new result; choose a suitable writable reference when modifying the caller's object is intended. Do not return a reference to a local variable. These fixed-count integer operations take O(1); not all functions or copies of all types do.

Run and check: change a copy, change the original, receive a result

#include <iostream>
int byValue(int value) { value += 2; return value; }
void byReference(int& value) { value += 2; }
int main() {
    int n = 4;
    int result = byValue(n);
    std::cout << n << " " << result << "\n";
    byReference(n);
    std::cout << n << " " << result << "\n";
    n = byValue(n);
    std::cout << n << " " << result << "\n";
}

The lines are 4 6, 6 6 and 8 6. result stores its own value; it does not track later changes to n.

Try before revealing each answer

If the parameter value is renamed n, does pass by value become pass by reference?

No. Names do not change the parameter rules for int versus int&; identical names can denote different objects.

With n=10, what is n after calling byValue(n) without receiving the return value?

Still 10. The function returns 12, but that result is not assigned back to n.

After int& alias=n; alias=9;, is n changed or is alias rebound to 9?

n becomes 9, and alias still refers to that n. Assignment does not rebind an initialized reference.

Calls, references and parameter examples

Example 1: Calculate the sum of two numbers

#include <iostream>
using namespace std;

// Define a function: receive two integers and return their sum
int add(int a, int b) {
    return a + b;
}

int main() {
    int x = 3, y = 5;
    cout << add(x, y) << endl;  // output 8
    cout << add(10, 20) << endl; // output 30
    return 0;
}

When calling add(3,5), a=3 and b=5 are photocopies, and the function returns 8; when calling add(10,20) again, the arguments are replaced with 10 and 20, the function still runs, and returns 30.

Example 2: Swap two numbers using reference parameters

#include <iostream>
using namespace std;

// Reference parameters: a and b refer to caller objects
void swap2(int &a, int &b) {
    int tmp = a;
    a = b;
    b = tmp;
}

int main() {
    int x = 3, y = 7;
    swap2(x, y);
    cout << x << " " << y << endl; // output 7 3
    return 0;
}

Because reference parameters are used, modifications to a and b inside the function directly affect x and y in main, so the output becomes 7 3.

Example 3: Modifying the pointed-to object vs modifying the pointer itself

#include <iostream>
using namespace std;
void update(int* p) {
    if (p != nullptr) *p += 1;
    p = nullptr; // Change only the local pointer copy
}
int main() {
    int x = 4; int* p = &x;
    update(p); update(nullptr);
    cout << x << " " << (p == &x) << endl;
}

Output 5 1. x is modified through dereferencing, and the outer p still points to x; the function also checks for null pointers.

Tips

Technical references

Scope note

This lesson is written by InfoCode and is not an official syllabus; the reference level is a site mapping and does not represent official levels, certification or eligibility. Practice records do not establish mastery or eligibility.

Reference level: GESP Level 4 (site mapping)

See contest pathways