How To Pass An Array To A Function In C++

6 min read

Passing Arrays to Functions in C++: A Complete Guide

Learning how to pass an array to a function in C++ is a fundamental skill that every programmer must master to write efficient, bug-free code. Unlike some higher-level languages that treat arrays as first-class objects, C++ relies on pointers and references to manipulate array data across function boundaries. And understanding the various methods—from traditional pointer decay to modern C++ alternatives like std::array and std::vector—not only solves compilation errors but also deepens your grasp of memory management and performance optimization. This article walks you through every practical approach, explains the underlying mechanics, and provides clear, runnable examples to solidify your confidence Worth knowing..

Introduction

In C++, an array name decays into a pointer to its first element when passed to a function. Modern C++ standards have introduced safer alternatives that retain size information and provide bounds checking, reducing the risk of out-of-bounds access. While it allows functions to modify the original array data directly, it also removes the size information, requiring developers to pass the array size separately. This behavior, known as array decay, is the root of both simplicity and complexity in C++ array handling. Throughout this guide, we'll explore these methods step by step, ensuring you can choose the right approach for your specific use case.

Honestly, this part trips people up more than it should.

Steps to Pass an Array to a Function in C++

Method 1: Passing a Pointer (Array Decay)

The most traditional way to pass an array to a function is to let it decay automatically into a pointer. This method is concise and works in all C++ versions, but it requires the caller to manage the array size It's one of those things that adds up..

#include 

void printArray(int* arr, int size) {
    for (int i = 0; i < size; ++i) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
}

int main() {
    int numbers[] = {10, 20, 30, 40, 50};
    int n = sizeof(numbers) / sizeof(numbers[0]);
    printArray(numbers, n);
    return 0;
}

In this example, numbers decays to int* inside printArray. The size parameter is crucial; without it, the function has no way of knowing how many elements the array contains. This approach is efficient because it passes only the address of the first element, incurring no overhead for copying the entire array.

Method 2: Passing by Reference with std::array

C++11 introduced std::array, a wrapper that encapsulates a C-style array and carries its size as a compile-time constant. Passing a std::array by reference preserves the size information, making it safer and more expressive That's the whole idea..

#include 
#include 

void printConstArray(const std::array& arr) {
    for (const auto& elem : arr) {
        std::cout << elem << " ";
    }
    std::cout << std::endl;
}

int main() {
    std::array numbers = {10, 20, 30, 40, 50};
    printConstArray(numbers);
    return 0;
}

Because std::array knows its size, the function signature const std::array<int, 5>& arr is both self-documenting and safe. That's why the reference parameter ensures no copy is made, maintaining performance while adding bounds safety through member functions like . size(). This method is ideal when the array size is fixed and known at compile time Took long enough..

Method 3: Using std::vector for Dynamic Sizes

When the number of elements is not known before runtime, std::vector is the modern C++ idiom. Vectors are dynamic arrays that can grow or shrink, and they carry their size internally That's the part that actually makes a difference. Still holds up..

#include 
#include 

void printVector(const std

### Method 3: Using `std::vector` for Dynamic Sizes

When the number of elements is not known before runtime, `std::vector` is the modern C++ idiom. Vectors are dynamic arrays that can grow or shrink, and they carry their size internally.

```cpp
#include 
#include 

void printVector(const std::vector& vec) {
    for (const auto& elem : vec) {
        std::cout << elem << " ";
    }
    std::cout << std::endl;
}

In main, we can create a vector with an initializer list or after dynamic sizing:

int main() {
    std::vector numbers = {10, 20, 30, 40, 50};
    printVector(numbers);

    // Dynamic sizing
    std::vector dynamicVec;
    dynamicVec.reserve(5);
    for (int i = 0; i < 5; ++i) {
        dynamicVec.push_back(i * 10);
    }
    printVector(dynamicVec);

The `std::vector` example above demonstrates how the container itself carries the element count, so the function can simply iterate from begin to end without an extra parameter. Because the range‑based for‑loop uses the iterator pair `vec.begin()` and `vec.end()`, the code remains concise and automatically adapts to any vector size, even when the size changes at runtime.

Not obvious, but once you see it — you'll see it everywhere.

### Extending the vector approach with `std::span`

Starting with C++20, `std::span` offers a lightweight, non‑owning view of a contiguous memory region. It can be constructed from a raw array, a `std::vector`, a `std::array`, or any pointer‑length pair, making it a versatile way to write functions that accept “any array‑like” object.

Real talk — this step gets skipped all the time.

```cpp
#include 
#include 

template 
void printSpan(std::span s) {
    for (const auto& elem : s) {
        std::cout << elem << ' ';
    }
    std::cout << '\n';
}

int main() {
    int rawArray[] = {1, 2, 3, 4, 5};
    std::vector vec = {6, 7, 8, 9};
    std::array arr = {10, 11, 12};

    printSpan(rawArray);   // works directly on the raw array
    printSpan(vec);        // view into the vector’s storage
    printSpan(arr);        // view into the std::array’s elements

    return 0;
}

std::span eliminates the need to pass a separate size because the span object stores both a pointer and a length. The function operates on the view without copying data, preserving the performance benefits of raw pointers while adding safety: out‑of‑bounds access is detected in debug builds, and the compiler can enforce that the span’s lifetime exceeds the view’s usage.

Generic algorithms with templates

When the exact container type is irrelevant, a template that works with any iterable container can be employed. This pattern is common in the Standard Library itself.

template 
void printGeneric(const Container& c) {
    for (const auto& elem : c) {
        std::cout << elem << ' ';
    }
    std::cout << '\n';
}

Because the template deduces the container’s element type and iterators, it can handle std::array, std::vector, std::list, or even user‑defined containers that provide begin() and end() members. The only requirement is that the container be iterable, which makes the function highly reusable while still being type‑safe And that's really what it comes down to..

Choosing the right tool

  • Fixed‑size, compile‑time known – std::array (or a raw array when the size truly never changes) is the clearest choice; the size is part of the type, and no extra argument is needed.
  • Dynamic, runtime‑determined length – std::vector is the idiomatic container; its size is available via .size(), and it manages memory automatically.
  • Interoperability with legacy code – std::span bridges raw pointers/arrays and modern containers, allowing a single function signature to serve many different underlying types.
  • Maximum flexibility – a templated function that works with any iterable container provides the most generic solution, at the cost of a slightly more abstract interface.

Conclusion

C++ offers a rich set of tools for handling arrays of varying lifetimes and sizes. Relying on raw arrays with a separate size parameter works, but it is prone to errors and lacks the safety and expressiveness of the standard library. Consider this: std::array gives compile‑time guarantees for fixed‑size collections, std::vector handles dynamic data without manual memory management, and std::span (or generic templates) unifies the handling of disparate contiguous ranges. By selecting the appropriate abstraction, developers can write code that is both efficient — avoiding unnecessary copies — and strong, with clear intent and built‑in bounds checking where applicable.

The official docs gloss over this. That's a mistake Not complicated — just consistent..

Right Off the Press

Just Went Live

More in This Space

Familiar Territory, New Reads

Thank you for reading about How To Pass An Array To A Function In C++. We hope the information has been useful. Feel free to contact us if you have any questions. See you next time — don't forget to bookmark!
⌂ Back to Home