C++ References and Function Parameter Passing

Reference Aliases

A reference provides an alternative name for an existing variable, commonly utilized for function arguments and return values. Upon declaration, a reference must be initialized, and it cannot be re-seated to alias a different variable later. Internally, the C++ compiler implements references as constant pointers.

int value = 15;

// Bind reference 'ref' to 'value'
int& ref = value;

// Accessing the original value through the alias
std::cout << "Ref: " << ref << std::endl;

// Modifying the alias alters the original variable, and vice versa
ref += 5;
std::cout << "Ref: " << ref << " | Value: " << value << std::endl;

int other = 50;
// This assigns 50 to value; it does not make 'ref' point to 'other'
ref = other;
std::cout << "Ref: " << ref << " | Value: " << value << std::endl;

Function Fundamentals

Functions encapsulate reusable blocks of logic. A function definition consists of a header (return type, identifier, parameter list) and a body enclosed in braces. Return types can be any valid C++ type, or void if no value is returned. Parameters defined in the function signature are formal parameters, while those passed during the call are actual arguments.

int calculateProduct(int limit) { // 'limit' is a formal parameter
    int product = 1;
    for (int idx = 2; idx <= limit; ++idx) {
        product *= idx;
    }
    return product;
}

void displayGreeting() {
    std::cout << "Greetings!" << std::endl;
}

int getLarger(int x, int y); // Forward declaration

int main() {
    int num = 4;
    int result = calculateProduct(num); // 'num' is an actual argument
    std::cout << "Result: " << result << std::endl;

    std::cout << getLarger(12, 8) << std::endl;
    return 0;
}

int getLarger(int x, int y) {
    return (x > y) ? x : y;
}

Parameter Passing Mechanisms

During a function call, memory is allocated for formal parameters, and values from actual arguments are transferred. C++ supports passing by value, pointer, and reference.

Pass by Value

When passing by value, the actual and formal parameters occupy distinct memory locations. A copy of the argument is created, preventing modifications inside the function from affecting the original variable.

void swapByValue(int x, int y) {
    int temp = x;
    x = y;
    y = temp;
}

Pass by Pointer

Passing by pointer transmits the memory address of the argument. This allows the function to directly manipulate the caller's variables by dereferencing the pointers.

void swapByPointer(int* ptr1, int* ptr2) {
    int temp = *ptr1;
    *ptr1 = *ptr2;
    *ptr2 = temp;
}

Pass by Reference

Passing by reference also passes the underlying address, but using alias syntax. It enables direct modification of the caller's variables without the need for dereferencing operators.

void swapByReference(int& x, int& y) {
    int temp = x;
    x = y;
    y = temp;
}

Arrays as Parameters and Return Values

When an array name is passed to a function, the base address of the array is transmitted. The formal parameter can be declared as a pointer (int*) or an unsized array (int[]). C++ does not allow functions to return native arrays directly; instead, a pointer to dynamically allocated memory must be returned.

void incrementElements(int* arr, int size);
void incrementAlt(int arr[], int size);
void printArray(const int* arr, int size);

int* generateArray(int size);

int main() {
    int data[] = {10, 20, 30, 40, 50};

    incrementElements(data, 5);
    printArray(data, 5);

    // Passing a sub-array starting from index 2
    incrementAlt(&data[2], 3);
    printArray(data, 5);

    int* dynamicArr = generateArray(15);
    printArray(dynamicArr, 15);
    delete[] dynamicArr;

    return 0;
}

void incrementElements(int* arr, int size) {
    for (int i = 0; i < size; ++i) {
        arr[i] += 10;
    }
}

void incrementAlt(int arr[], int size) {
    for (int i = 0; i < size; ++i) {
        arr[i] += 10;
    }
}

int* generateArray(int size) {
    int* arr = new int[size];
    for (int i = 0; i < size; ++i) {
        arr[i] = i * 2;
    }
    return arr;
}

Variable Scope and Lifetime

Scope Categories

Variables declared inside a block are local variables, accessible only within that block. Variables declared outside all blocks are global variables, accessible throughout the file. If a local variable shares a name with a global variable, the local variable takes precedence. The scope resolution operator (::) can be used to access the global variable when shadowed.

int counter = 500; // Global variable

void checkScope();

int main() {
    int counter = 50; // Local variable shadows global
    checkScope();

    std::cout << "Local counter: " << counter << std::endl;
    std::cout << "Global counter: " << ::counter << std::endl;
    return 0;
}

void checkScope() {
    // Accesses the global variable directly
    std::cout << "Global inside func: " << counter << std::endl;
    counter += 10;
}

Storage Duration

Dynamic storage allocates and deallocates memory as needed during execution. Static storage allocates fixed memory for the entire runtime of the program. Declaring a local variable with the static keyword ensures it is initialized only once and retains its value between function calls.

int accumulate(int val) {
    static int total = 0; // Initialized only on the first call
    total += val;
    return total;
}

int main() {
    std::cout << accumulate(1) << std::endl; // 1
    std::cout << accumulate(2) << std::endl; // 3
    std::cout << accumulate(3) << std::endl; // 6
    std::cout << accumulate(0) << std::endl; // 6
    return 0;
}

Tags: C++ References functions parameter passing scope

Posted on Fri, 23 Oct 2026 16:04:43 +0000 by csplrj