Ch5.5: C‑style Arrays

Overview

A C‑style array is a fixed‑size sequence of elements stored in one contiguous block of memory. Arrays are the foundation for pointer arithmetic, array decay, and C‑style strings, so it is essential to understand how they behave in memory.

In this chapter, you will learn:

1. Declaring a C‑style array

A C‑style array is declared by specifying the element type and the number of elements:


int a[3];     // array of 3 ints
char b[10];   // array of 10 chars
double c[4];  // array of 4 doubles

The size must be known at compile time, and it must be greater than 0.


// int a[0];   // error: C-style arrays cannot have size 0

2. Initializing arrays

You can initialize arrays using brace‑initialization:


int a[3]{10, 20, 30};

If you provide fewer elements, the rest are zero‑initialized:


int a[5]{1, 2};   // becomes {1, 2, 0, 0, 0}

If you omit the size, the compiler deduces it:


int a[]{10, 20, 30};   // size is 3

3. Arrays are contiguous in memory

All elements of an array are stored next to each other in memory.


int a[3]{10, 20, 30};

Example memory layout (addresses vary):

Address:   1000   1004   1008
Memory:   [ 10 ] [ 20 ] [ 30 ]
             ↑      ↑      ↑
            a[0]  a[1]   a[2]

This contiguity is what makes pointer arithmetic possible (covered in Ch5.6).

4. Accessing array elements

You access elements using the [] operator:


int a[3]{10, 20, 30};

int x = a[0];   // 10
int y = a[2];   // 30

a[1] = 99;      // a becomes {10, 99, 30}

5. Out‑of‑bounds access is undefined behavior

C‑style arrays do not perform bounds checking. Accessing outside the array is undefined behavior.


int a[3]{10, 20, 30};

// a[3];    // undefined behavior — out of bounds
// a[-1];   // undefined behavior

Undefined behavior means:

You cannot rely on any specific outcome.

WebAssembly note

On WebAssembly without memory tagging, out‑of‑bounds array access often does not trap, making bugs harder to detect.

6. fast_io::array vs C‑style arrays

::fast_io::array is a safer and more flexible alternative to C‑style arrays.


#include <fast_io_dsal/array.h>

::fast_io::array<int, 0> empty;   // valid: zero-sized array

Normal element access is checked:


::fast_io::array<int, 3> arr{10, 20, 30};

int x = arr[1];   // safe: bounds checked

If you intentionally want unchecked access (for performance‑critical code), ::fast_io::array provides:


int x = arr.index_unchecked(1);   // no bounds checking

For most code, prefer arr[i] with bounds checking.

7. fast_io::vector as the default container

For most dynamic sequences of elements, prefer ::fast_io::vector. It manages its own size and capacity and also supports bounds‑checked access.


#include <fast_io_dsal/vector.h>

::fast_io::vector<int> vec;
vec.push_back(10);
vec.push_back(20);

int x = vec[0];   // bounds checked

When you need unchecked access for performance‑critical code, ::fast_io::vector also provides:


int x = vec.index_unchecked(0);   // no bounds checking

For most code, prefer vec[i] with bounds checking and reserve index_unchecked() for carefully reviewed hot paths.

8. Arrays and pointers are related but not the same

Arrays and pointers are closely related, but they are not the same.

An array:

A pointer:

We will explore the relationship between arrays and pointers in detail in Ch5.7: Array Decay.

9. Getting the address of array elements

You can obtain the address of any element using ::std::addressof:


#include <memory>

int a[3]{10, 20, 30};

int *p0 = ::std::addressof(a[0]);
int *p1 = ::std::addressof(a[1]);
int *p2 = ::std::addressof(a[2]);

Addresses (example):

p0 = 1000
p1 = 1004
p2 = 1008

This layout is what makes pointer arithmetic meaningful (next chapter).

10. Arrays cannot be copied or assigned

C‑style arrays cannot be copied or assigned using =.


int a[3]{1, 2, 3};
int b[3];

// b = a;   // error: arrays cannot be assigned

Copying arrays requires manual element‑by‑element copying or memcpy (covered in Ch5.14).

Key takeaways