Appearance
std array Fixed-Size Array Container
std::array: Fixed-Size Array Container
std::array is a fixed-size array container that provides the safety and convenience of STL containers while maintaining the performance characteristics of C-style arrays. It's perfect for situations where you know the size at compile time and want maximum performance without dynamic allocation overhead.
It provides:
- Fixed size: Size is part of the type and cannot change
- Stack allocation: No dynamic memory allocation
- Bounds checking: Safe access through
at()method - STL compatibility: Works with all STL algorithms
- Value semantics: Can be copied, passed to functions, returned from functions
Creating and Initializing Arrays
cpp
#include <array>
#include <iostream>
// Empty array (elements default-constructed)
std::array<int, 5> empty_array;
// Array with initial values
std::array<int, 5> numbers = {1, 2, 3, 4, 5};
// Array with partial initialization (remaining elements default-constructed)
std::array<int, 5> partial = {1, 2, 3}; // {1, 2, 3, 0, 0}
// Array from another array
std::array<int, 5> copy_array = numbers;
// Array of different types
std::array<std::string, 3> names = {"Alice", "Bob", "Charlie"};
std::array<double, 4> prices = {10.99, 20.50, 15.75, 30.25};Basic Operations
Accessing Elements
cpp
std::array<int, 5> arr = {10, 20, 30, 40, 50};
// Random access (O(1))
int first = arr[0]; // 10
int last = arr[4]; // 50
int middle = arr[2]; // 30
// Bounds-checked access
int safe_first = arr.at(0); // 10, throws if out of bounds
int safe_last = arr.at(4); // 50, throws if out of bounds
// Front and back access
int front = arr.front(); // 10
int back = arr.back(); // 50
// Safe access with bounds checking
if (2 < arr.size()) {
int safe_middle = arr[2]; // Safe because we checked
}Iterating Over Arrays
cpp
std::array<int, 5> arr = {1, 2, 3, 4, 5};
// Range-based for loop (C++11)
for (const auto& item : arr) {
std::cout << item << " "; // 1 2 3 4 5
}
// Traditional index-based loop
for (size_t i = 0; i < arr.size(); ++i) {
std::cout << arr[i] << " ";
}
// Iterator-based loop
for (auto it = arr.begin(); it != arr.end(); ++it) {
std::cout << *it << " ";
}
// Reverse iteration
for (auto it = arr.rbegin(); it != arr.rend(); ++it) {
std::cout << *it << " "; // 5 4 3 2 1
}
// Using std::for_each
#include <algorithm>
std::for_each(arr.begin(), arr.end(), (int x) {
std::cout << x << " ";
});Performance Characteristics
Time Complexity
| Operation | Time Complexity | Notes |
|---|---|---|
| Random Access | O(1) | Direct memory offset calculation |
| Iteration | O(n) | Must visit all elements |
| Copy/Assignment | O(n) | Copy all elements |
| Size Query | O(1) | Compile-time constant |
Memory Layout
cpp
std::array<int, 5> arr = {1, 2, 3, 4, 5};
// Memory layout (conceptual):
// [1] [2] [3] [4] [5]
// ^ ^ ^ ^ ^
// | | | | |
// arr[0] arr[1] arr[2] arr[3] arr[4]
// Elements are stored contiguously in memory
// Perfect cache locality - accessing adjacent elements is optimal
// No memory overhead beyond the data itselfKey Insight: std::array provides perfect cache locality with zero memory overhead, making it ideal for performance-critical applications.
Why Choose std::array?
Safety and Convenience
cpp
// C-style array problems
void processArray(int arr, size_t size) {
// No way to know the actual size
// No bounds checking
// Can't be returned from functions
// Decays to pointer
}
// std::array solution
void processArray(const std::array<int, 5>& arr) {
// Size is part of the type
// Can use arr.size() safely
// Bounds checking with arr.at()
// No pointer decay
// Can be const-correct
}
// Return from functions
std::array<int, 5> createArray() {
return {1, 2, 3, 4, 5}; // Can return arrays!
}STL Algorithm Compatibility
cpp
#include <algorithm>
#include <numeric>
std::array<int, 5> numbers = {3, 1, 4, 1, 5};
// Sort the array
std::sort(numbers.begin(), numbers.end()); // {1, 1, 3, 4, 5}
// Find elements
auto it = std::find(numbers.begin(), numbers.end(), 4);
if (it != numbers.end()) {
std::cout << "Found 4 at position: " << (it - numbers.begin()) << std::endl;
}
// Count occurrences
int count = std::count(numbers.begin(), numbers.end(), 1); // 2
// Accumulate
int sum = std::accumulate(numbers.begin(), numbers.end(), 0); // 14
// Transform
std::array<int, 5> doubled;
std::transform(numbers.begin(), numbers.end(), doubled.begin(),
(int x) { return x * 2; });Bounds Checking
cpp
std::array<int, 5> arr = {1, 2, 3, 4, 5};
// Safe access with bounds checking
try {
int value = arr.at(10); // Throws std::out_of_range
} catch (const std::out_of_range& e) {
std::cout << "Index out of bounds: " << e.what() << std::endl;
}
// Unsafe access (undefined behavior)
// int value = arr[10]; // Undefined behavior!
// Safe bounds checking
if (10 < arr.size()) {
int value = arr[10]; // Safe because we checked
} else {
std::cout << "Index 10 is out of bounds" << std::endl;
}When to Use std::array
Use std::array when:
- Fixed size known at compile time: Size is part of the type
- Maximum performance needed: No dynamic allocation overhead
- Stack allocation preferred: Small arrays that fit on stack
- Perfect cache locality required: Contiguous memory layout
- STL algorithm compatibility needed: Want to use with algorithms
Consider alternatives when:
- Size not known at compile time: Use
std::vector - Dynamic sizing needed: Use
std::vector - Very large arrays: Use
std::vectorto avoid stack overflow - Frequent size changes: Use
std::vector
Summary
std::array is an excellent choice for fixed-size collections when you need: Advantages:
- Maximum performance: No dynamic allocation overhead
- Perfect cache locality: Contiguous memory layout
- Stack allocation: Small arrays fit on stack
- STL compatibility: Works with all algorithms
- Bounds checking: Safe access through
at()method - Value semantics: Can be copied, passed, returned
Trade-offs:
- Fixed size: Cannot change size after creation
- Stack allocation: Large arrays may cause stack overflow
- Compile-time size: Size must be known at compile time
Best Use Cases:
- Small, fixed collections: Configuration, constants, small buffers
- Performance-critical code: Maximum speed with no overhead
- Stack allocation preferred: Avoid heap allocation for small data
- STL algorithm compatibility: Need to use with algorithms
Choose std::array when you know the size at compile time and want maximum performance. It's the perfect bridge between C-style arrays and STL containers, providing safety and convenience without sacrificing performance.
Questions
Q: What is the main difference between std::array and C-style arrays?
A: std::array uses less memory
std::array knows its own size and provides bounds checking through the at() method. Unlike C-style arrays, std::array is a proper container that can be passed to functions, returned from functions, and used with STL algorithms.
Q: What is the time complexity of accessing elements in std::array?
std::array provides O(1) random access to any element because it's stored in contiguous memory, just like C-style arrays. The array index is used to calculate the memory offset directly.
Q: What happens when you try to access an element beyond the bounds of std::array using at()?
std::array::at() performs bounds checking and throws std::out_of_range exception if the index is out of bounds. This provides safety compared to operatorwhich causes undefined behavior on out-of-bounds access.
Q: Can you change the size of std::array after creation?
std::array has a fixed size that cannot be changed after creation. The size is part of the type (std::array<int, 5>), making it a compile-time constant. If you need dynamic sizing, use std::vector instead.
Q: When should you prefer std::array over std::vector?
Use std::array when you know the exact size at compile time and want maximum performance. std::array has no dynamic allocation overhead, perfect cache locality, and can be allocated on the stack, making it ideal for small, fixed-size collections.