C++ Arrays
C++ Arrays and Containers
Section titled “C++ Arrays and Containers”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.
1. Built-in C-style Arrays
Section titled “1. Built-in C-style Arrays”This is the traditional array inherited from C. It stores a fixed-size sequential collection of elements.
Declaration:
Section titled “Declaration:”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];Initialization:
Section titled “Initialization:”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 sizeint 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};Accessing Elements:
Section titled “Accessing Elements:”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 elementdouble 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!Memory Layout:
Section titled “Memory Layout:”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]].
Limitations of C-style Arrays:
Section titled “Limitations of C-style Arrays:”- 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.
2. std::array (C++11 and later)
Section titled “2. std::array (C++11 and later)”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.
Declaration & Initialization:
Section titled “Declaration & Initialization:”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)Accessing Elements & Features:
Section titled “Accessing Elements & Features:”- Bracket Access
[]:myArray[0](no bounds check). at()Member Function:myArray.at(1)(provides bounds checking; throwsstd::out_of_rangeif 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.
3. std::vector
Section titled “3. std::vector”std::vector is a dynamic array. It manages its storage on the heap, allowing it to grow or shrink in size at runtime.
Declaration & Initialization:
Section titled “Declaration & Initialization:”Include the <vector> header.
#include <vector>
std::vector<int> scores; // Empty vectorstd::vector<std::string> names = {"Alice", "Bob", "Charlie"}; // Initializer liststd::vector<double> data(10); // Vector with 10 default-initialized doubles (0.0)std::vector<int> sequence(5, 100); // Vector with 5 integers, all value 100Accessing Elements & Features:
Section titled “Accessing Elements & Features:”- 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 tostd::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 40std::vector elements (modified): C++! is! fun!std::vector elements (const reference): C++! is! fun!Arrays in Detail (Modern Perspective):
Section titled “Arrays in Detail (Modern Perspective):”Key concepts related to using array-like structures effectively in C++:
| Concept | Description |
|---|---|
| Multi-dimensional arrays | Use 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 access | Both 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 functions | Prefer 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 functions | Functions can directly return std::array or std::vector by value. Modern compilers often optimize the copy away (Return Value Optimization - RVO). |