C++ Function Concepts: Prototypes, Pointers, References, Default Values, and Variadic Arguments

Function Declaration

Defining a user function requires it to be declared or defined before it is called. If the definition is placed after main() or its calls, compilation will fail. For example:

#include <iostream>

int main() {
    do_something("What's truth?);
    do_something("There is no spoon.");
    
    return 0;
}

void do_something(char* s) {
    std::cout << s << std::endl;
}

This results in errors because C++ requires all identifiers to be declared or defined before use. To fix this, declare a function prototype before calling it:

#include <iostream>

void do_something(char*);

int main() {
    do_something("What's truth?);
    do_something("There is no spoon.");
    
    return 0;
}

void do_something(char* s) {
    std::cout << s << std::endl;
}

The function prototype is declared on line 3:

void do_something(char*);

Parameter types are sufficient for the declaration. The compiled output is as follows:

$ g++ u0801_2.cpp
$./a.out
What's truth?
There is no spoon.

Typically, function prototypes are placed in header files, while their implementations reside in other source files.

Pointer Parameters

To modify variables directly within a function, pass pointers to them. For example:

#include <iostream>

void do_something(int* n_ptr) {
    *n_ptr = 10;
}

int main() {
    int a = 22;
    std::cout << a << std::endl;
    do_something(&a);
    std::cout << a << std::endl;
    
    return 0;
}

The function do_something accepts an integer pointer and sets it to 10:

void do_something(int* n_ptr) {
    *n_ptr = 10;
}

Compilasion and execution result:

$ g++ u0802_1.cpp
$./a.out
22
10

When multiple variables need modification, using pointers is a solution. Another example:

#include <iostream>

int do_something2(int* n1_ptr, int* n2_ptr) {
    *n1_ptr *= 2;
    *n2_ptr *= 2;
    return *n1_ptr + *n2_ptr;
}

int main() {
    int a = 22;
    int b = 33;
    std::cout << "a + b: " << do_something2(&a, &b) << std::endl;
    std::cout << "a: " << a << std::endl;
    std::cout << "b: " << b << std::endl;
    
    return 0;
}

Output:

$ g++ u0802_2.cpp
$./a.out
a+b: 110
a: 44
b: 66

Reference Parameters

To modify variables directly, functions can accept reference parameters. For example:

#include <iostream>

void do_something(int& n_ref) {
    n_ref = 10;
}

int main() {
    int a = 22;
    std::cout << a << std::endl;
    do_something(a);
    std::cout << a << std::endl;
    
    return 0;
}

The function do_something takes an integer reference and sets it to 10:

void do_something(int& n_ref) {
    n_ref = 10;
}

Compilation and execution result:

$ g++ u0803_1.cpp
$./a.out
22
10

For multiple variables, references provide a solution. Another example:

#include <iostream>

int do_something2(int &n1_ref, int &n2_ref) {
    n1_ref *= 2;
    n2_ref *= 2;
    return n1_ref + n2_ref;
}

int main() {
    int a = 22;
    int b = 33;
    std::cout << "a + b: " << do_something2(a, b) << std::endl;
    std::cout << "a: " << a << std::endl;
    std::cout << "b: " << b << std::endl;
    
    return 0;
}

Output:

$ g++ u0803_2.cpp
$./a.out
a+b: 110
a: 44
b: 66

Default Parameters

Functions can have default parameters, which set values if not provided. For example:

#include <iostream>

void do_something(int n = 22) {
    std::cout << n << std::endl;
}

int main() {
    do_something(11);
    do_something();
    do_something();
    
    return 0;
}

Setting a default value is done by assigning a value in the parameter list:

void do_something(int n = 22) {

Calling without arguments is allowed:

do_something(11);
do_something();
do_something();

Compilation and output:

$ g++ u0804_1.cpp
$./a.out
11
22
22

Default parameters must appear at the end of the parameter list. For example:

#include <iostream>

void do_something2(int n1, int n2 = 5, int n3 = 0) {
    std::cout << n1 + n2 + n3 << std::endl;
}

int main() {
    do_something2(6);
    do_something2(6, 9);
    do_something2(3, 4, 5);
    
    return 0;
}

The function call requires atleast one argument since n1 has no default:

do_something2(6);
do_something2(6, 9);
do_something2(3, 4, 5);

Compilation and output:

$ g++ u0804_2.cpp
$./a.out
11
15
12

Variadic Arguments

Functions can accept a variable number of arguments using .... For example:

#include <iostream>
#include <cstdarg>

int sum(int n_args, ...) {
    va_list ap;
    va_start(ap, n_args);
    int sum = va_arg(ap, int);
    for (int i = 2; i <= n_args; i++) {
        sum += va_arg(ap, int);
    }
    va_end(ap);
    
    return sum;
}

int main() {
    std::cout << sum(3, 11, 22, 33) << std::endl;
    std::cout << sum(3, 19, 20, 21) << std::endl;
    std::cout << sum(3, 3, 65, 101) << std::endl;
    
    return 0;
}

The sum function calculates the total of passed arguments. The first argument specifies the count, and the rest are summed:

int sum(int n_args, ...) {
    va_list ap;
    va_start(ap, n_args);
    int sum = va_arg(ap, int);
    for (int i = 2; i <= n_args; i++) {
        sum += va_arg(ap, int);
    }
    va_end(ap);
    
    return sum;
}

Compilation and output:

$ g++ u0805.cpp
$./a.out
66
60
169

Tags: C++ functions Prototypes pointers References

Posted on Fri, 16 Oct 2026 16:14:20 +0000 by michalurban