Skip to content

C++ Arrays

Arrays are fundamental data structures used to store collections of elements of the same type in contiguous memory locations. C++ offers several ways to work with array-like structures, each with its own characteristics.

This is the traditional array inherited from C. It stores a fixed-size sequential collection of elements.

Specify the element type, array name, and size (which must be a compile-time constant):

type arrayName[arraySize];

Example: Declare an array scores to hold 10 integers.

int scores[10];

You can initialize C-style arrays during declaration using brace initialization {}:

double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0};
// Size can be omitted if initialized; compiler deduces size
int values[] = {1, 2, 3, 4};
// Initialize first few elements (rest are zero-initialized for static/global, indeterminate for local)
float readings[100] = {1.1f, 2.2f};

Access elements using the array name followed by the index in square brackets []. Array indexing is zero-based, meaning the first element is at index 0 and the last element is at index arraySize - 1.

balance[0] = 1200.0; // Change the first element
double thirdBalance = balance[2]; // Access the third element (index 2)
// IMPORTANT: Accessing beyond bounds (e.g., balance[5] or balance[-1])
// leads to Undefined Behavior - a common and dangerous bug!

C-style array elements are stored contiguously in memory. For double balance[5], the memory looks like: [balance[0]][balance[1]][balance[2]][balance[3]][balance[4]].

  • Fixed Size: Size must be known at compile time and cannot be changed.
  • No Bounds Checking: Accessing elements outside the valid index range compiles but causes undefined behavior at runtime.
  • Pointer Decay: When passed to functions, they often ‘decay’ into a pointer to the first element, losing size information.
  • No Member Functions: They don’t have helpful methods like getting the size or checking if empty.

Because of these limitations, modern C++ strongly prefers using standard library containers like std::array and std::vector.

std::array is a container encapsulating fixed-size arrays. It provides the efficiency and memory layout of C-style arrays but with the benefits of a standard container.

Include the <array> header. Specify the type and size as template arguments.

#include <array>
std::array<int, 5> myArray = {1, 2, 3, 4, 5};
std::array<double, 3> temps; // Elements are uninitialized (like C-style local array)
std::array<char, 10> name{}; // Value-initializes elements (to 0 for char)
  • Bracket Access []: myArray[0] (no bounds check).
  • at() Member Function: myArray.at(1) (provides bounds checking; throws std::out_of_range if index is invalid).
  • size() Member Function: myArray.size() returns the size (compile-time constant).
  • Iterators: Supports iterators (begin(), end()) for use with standard algorithms and range-based for loops.
  • front() / back(): Access first/last element.
  • empty(): Checks if the size is zero.
  • data(): Returns a pointer to the underlying C-style array.

Use std::array when you need a fixed-size array allocated typically on the stack.

std::vector is a dynamic array. It manages its storage on the heap, allowing it to grow or shrink in size at runtime.

Include the <vector> header.

#include <vector>
std::vector<int> scores; // Empty vector
std::vector<std::string> names = {"Alice", "Bob", "Charlie"}; // Initializer list
std::vector<double> data(10); // Vector with 10 default-initialized doubles (0.0)
std::vector<int> sequence(5, 100); // Vector with 5 integers, all value 100
  • Bracket Access []: names[0] (no bounds check for direct access).
  • at() Member Function: names.at(1) (provides bounds checking).
  • size() Member Function: names.size() returns the current number of elements.
  • push_back(): Adds an element to the end (may reallocate memory).
  • pop_back(): Removes the last element.
  • resize(): Changes the number of elements.
  • clear(): Removes all elements.
  • empty(): Checks if the vector contains elements.
  • Iterators, front(), back(), data(): Similar to std::array.

Use std::vector when you need an array whose size is not known at compile time or needs to change during execution.

Modern Iteration: Range-Based For Loop (C++11)

Section titled “Modern Iteration: Range-Based For Loop (C++11)”

The most convenient way to iterate over elements in standard containers (like std::array, std::vector) and C-style arrays (when size is known) is the range-based for loop:

#include <iostream>
#include <vector>
#include <array>
int main() {
std::array<int, 4> fixedNums = {10, 20, 30, 40};
std::cout << "std::array elements: ";
for (int num : fixedNums) { // Read-only access (makes a copy)
std::cout << num << " ";
}
std::cout << std::endl;
std::vector<std::string> words = {"C++", "is", "fun"};
std::cout << "std::vector elements (modified): ";
for (std::string& word : words) { // Access by reference (allows modification)
word += "!";
std::cout << word << " ";
}
std::cout << std::endl;
std::cout << "std::vector elements (const reference): ";
for (const auto& word : words) { // Read-only access (no copy, preferred for objects)
std::cout << word << " ";
}
std::cout << std::endl;
return 0;
}

Output:

std::array elements: 10 20 30 40
std::vector elements (modified): C++! is! fun!
std::vector elements (const reference): C++! is! fun!

Key concepts related to using array-like structures effectively in C++:

ConceptDescription
Multi-dimensional arraysUse std::array<std::array<T, M>, N> for fixed multi-dimensional arrays or std::vector<std::vector<T>> for dynamic ones. Avoid C-style T[N][M] due to potential pointer decay issues.
Pointer accessBoth std::array and std::vector provide .data() member function to get a raw pointer to the contiguous underlying data, useful for interoperability with C APIs.
Passing arrays to functionsPrefer passing std::vector and std::array by reference (&) or const reference (const&) to avoid copying. For non-owning views of contiguous data (from C-arrays, std::array, std::vector), use std::span (C++20).
Return array from functionsFunctions can directly return std::array or std::vector by value. Modern compilers often optimize the copy away (Return Value Optimization - RVO).