Ch6.9: C-Style Array Parameters

Overview

In C++, arrays behave differently from most other types. When you pass an array to a function, the array decays into a pointer to its first element. This means that functions cannot know the size of the array unless you pass it explicitly.

In this chapter, you will learn:

1. Arrays decay to pointers

When you pass an array to a function, it automatically converts (decays) into a pointer to its first element.


void print_first(int const* p)
{
    ::fast_io::println("first element = ", *p);
}

int main()
{
    int arr[3]{10, 20, 30};
    print_first(arr);  // arr decays to int*
}

Inside print_first, the function has no idea how large the original array was.

2. Do not let [] fool you

In a function parameter list, the syntax int arr[] or int const arr[] does not mean “an array is passed.” It is simply another way of writing a pointer.

These three declarations are 100% identical:


void f(int const arr[]);
void f(int const arr[10]);
void f(int const* arr);

All three mean:

“f takes a pointer to const int.”

The brackets in a function parameter list do not preserve array size and do not change the type. They exist only for readability.

In this tutorial, we will avoid the misleading arr[] syntax in real code, but we will still show it in examples when teaching pitfalls—because you will see it in the wild.

3. Array size is lost

Because arrays decay to pointers, the function cannot know the array size.

This example intentionally uses int const arr[] to demonstrate the trap:


void print_size(int const arr[])
{
    ::fast_io::println(sizeof(arr));
    // prints sizeof(int*) — NOT the size of the array!
}

This is why you should not use arr[] in real code.

You must pass the size explicitly:


void print_all(int const* arr, std::size_t n)
{
    for(std::size_t i{}; i != n; ++i)
        ::fast_io::println(arr[i]);
}

4. Multidimensional arrays

Only the first dimension decays. The remaining dimensions must be known.


void print_matrix(int const (*arr)[4], std::size_t rows)
{
    for(std::size_t r{}; r != rows; ++r)
        for(std::size_t c{}; c != 4; ++c)
            ::fast_io::print(arr[r][c], ' ');
}

This is valid because the compiler knows the size of each row.

But this is invalid:


void bad(int const arr[][]);  // ❌ error — second dimension unknown

5. Pointer arithmetic

Inside a function, array indexing is just pointer arithmetic:


void demo(int const* p)
{
    ::fast_io::println(p[2]);   // same as *(p + 2)
}

This is why C-style array parameters are powerful but also dangerous.

6. Const correctness

If the function should not modify the array, use int const*.


void print_all(int const* arr, std::size_t n);

7. Why C-style array parameters are unsafe

C-style array parameters have several problems:

For these reasons, modern C++ code avoids raw array parameters whenever possible.

8. Prefer ::fast_io::array and ::fast_io::vector

The fast_io library provides safer, less verbose containers that can be passed into or returned from functions easily.

Fixed-size arrays


void process(::fast_io::array<int, 4> arr)
{
    for(auto e : arr)
        ::fast_io::println(e);
}

Dynamic-size arrays


void process(::fast_io::vector<int> v)
{
    for(auto e : v)
        ::fast_io::println(e);
}

Returning a vector


::fast_io::vector<::std::size_t> make_sequence(std::size_t n)
{
    ::fast_io::vector<::std::size_t> v;

    for(std::size_t i{}; i != n; ++i)
        v.push_back(i);

    return v;  // efficient: NRVO + move semantics
}

Passing a vector by const reference


void process(::fast_io::vector<int> const& v)
{
    for(auto e : v)
        ::fast_io::println(e);
}

These containers:

9. Cost of passing by value vs passing by reference

Understanding the cost of parameter passing helps you write efficient code.

Type Pass by value Pass by reference
int const arr[] / int const* arr cheap (pointer copy) cheap (reference copy)
::fast_io::vector<T> cheap (3-pointer struct) cheapest
::fast_io::array<T, N> copies all N elements cheap

::fast_io::vector is designed to be cheap to pass around. It behaves like a small struct containing three pointers (begin, end, capacity).

::fast_io::array is fixed-size, so passing it by value copies all elements. Passing by reference avoids this cost.

Key takeaways