Docs
README
Arrays in C++
Table of Contents
- •Introduction
- •What is an Array?
- •Array Declaration and Initialization
- •Accessing Array Elements
- •Array Size and Bounds
- •Multidimensional Arrays
- •Arrays and Functions
- •C-Style Strings (Character Arrays)
- •std::array (C++11)
- •Common Array Operations
- •Arrays vs Vectors
- •Best Practices
- •Common Mistakes
- •Summary
Introduction
Arrays are one of the most fundamental data structures in C++. They provide a way to store multiple elements of the same type in contiguous memory locations, allowing efficient access by index.
Why Use Arrays?
┌─────────────────────────────────────────────────────────────┐
│ ARRAY USE CASES │
├─────────────────────────────────────────────────────────────┤
│ ✓ Store fixed collections of same-type data │
│ ✓ Fast random access by index - O(1) │
│ ✓ Memory-efficient storage │
│ ✓ Cache-friendly iteration │
│ ✓ Foundation for other data structures │
└─────────────────────────────────────────────────────────────┘
What is an Array?
An array is a collection of elements of the same data type stored in contiguous memory locations.
Memory Layout
Array: int arr[5] = {10, 20, 30, 40, 50};
Memory:
┌──────┬──────┬──────┬──────┬──────┐
│ 10 │ 20 │ 30 │ 40 │ 50 │
├──────┼──────┼──────┼──────┼──────┤
│arr[0]│arr[1]│arr[2]│arr[3]│arr[4]│
└──────┴──────┴──────┴──────┴──────┘
1000 1004 1008 1012 1016 ← Memory addresses (4 bytes each for int)
↑
arr (base address)
Key Characteristics
| Property | Description |
|---|---|
| Fixed Size | Size determined at compile time (for C-style arrays) |
| Homogeneous | All elements must be same type |
| Contiguous | Elements stored in consecutive memory |
| Zero-indexed | First element is at index 0 |
| Direct Access | O(1) access time by index |
Array Declaration and Initialization
Declaration Syntax
type arrayName[size];
Various Initialization Methods
#include <iostream>
using namespace std;
int main() {
// Method 1: Declaration without initialization (contains garbage values)
int arr1[5]; // Contains unpredictable values!
// Method 2: Declaration with initialization
int arr2[5] = {10, 20, 30, 40, 50};
// Method 3: Partial initialization (remaining elements = 0)
int arr3[5] = {10, 20}; // {10, 20, 0, 0, 0}
// Method 4: All zeros
int arr4[5] = {}; // {0, 0, 0, 0, 0}
int arr5[5] = {0}; // {0, 0, 0, 0, 0}
// Method 5: Size inferred from initializer
int arr6[] = {1, 2, 3, 4, 5}; // Size = 5
// Method 6: Uniform initialization (C++11)
int arr7[5]{1, 2, 3, 4, 5};
// Method 7: Character arrays (C-strings)
char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
char str2[] = "Hello"; // Automatically adds '\0'
// Method 8: Constant arrays
const int DAYS[7] = {1, 2, 3, 4, 5, 6, 7};
return 0;
}
constexpr Arrays (C++11)
constexpr int PRIMES[] = {2, 3, 5, 7, 11, 13};
constexpr int SIZE = sizeof(PRIMES) / sizeof(PRIMES[0]);
Accessing Array Elements
Using Index Operator
int arr[5] = {10, 20, 30, 40, 50};
// Read elements
int first = arr[0]; // 10
int third = arr[2]; // 30
int last = arr[4]; // 50
// Write elements
arr[1] = 25; // arr is now {10, 25, 30, 40, 50}
arr[3] = arr[2] + 5; // arr is now {10, 25, 30, 35, 50}
Using Pointers
int arr[5] = {10, 20, 30, 40, 50};
// Pointer arithmetic
int* ptr = arr; // Points to first element
cout << *ptr << endl; // 10
cout << *(ptr + 1) << endl; // 20
cout << *(ptr + 4) << endl; // 50
// Equivalent ways to access arr[i]
cout << arr[i] << endl; // Array subscript
cout << *(arr + i) << endl; // Pointer arithmetic
cout << i[arr] << endl; // Also valid! (rarely used)
Iterating Over Arrays
int arr[5] = {10, 20, 30, 40, 50};
int size = sizeof(arr) / sizeof(arr[0]);
// Method 1: Index-based for loop
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
// Method 2: Range-based for loop (C++11)
for (int x : arr) {
cout << x << " ";
}
// Method 3: Pointer iteration
for (int* ptr = arr; ptr < arr + size; ptr++) {
cout << *ptr << " ";
}
// Method 4: Using std::begin/std::end (C++11)
for (auto it = begin(arr); it != end(arr); it++) {
cout << *it << " ";
}
Array Size and Bounds
Calculating Array Size
int arr[10];
// Get total bytes
size_t totalBytes = sizeof(arr); // 40 bytes (10 * 4)
// Get number of elements
size_t numElements = sizeof(arr) / sizeof(arr[0]); // 10
// C++17: std::size()
#include <iterator>
size_t len = std::size(arr); // 10
Bounds Checking (Important!)
int arr[5] = {1, 2, 3, 4, 5};
// DANGER: No bounds checking!
arr[5] = 100; // Undefined behavior - writing past array bounds
int x = arr[10]; // Undefined behavior - reading past array bounds
arr[-1] = 0; // Undefined behavior - negative index
// Safe alternative: use std::array with .at()
#include <array>
std::array<int, 5> safe_arr = {1, 2, 3, 4, 5};
safe_arr.at(5) = 100; // Throws std::out_of_range exception
Size Decay Problem
void printSize(int arr[]) {
// WARNING: arr is now a pointer, not an array!
cout << sizeof(arr) << endl; // Prints pointer size (8 on 64-bit)
}
int main() {
int arr[10];
cout << sizeof(arr) << endl; // Prints 40 (10 * 4)
printSize(arr); // Prints 8 (pointer size!)
}
Multidimensional Arrays
2D Arrays (Matrix)
// Declaration
int matrix[3][4]; // 3 rows, 4 columns
// Initialization
int matrix[3][4] = {
{1, 2, 3, 4}, // Row 0
{5, 6, 7, 8}, // Row 1
{9, 10, 11, 12} // Row 2
};
// Can omit first dimension
int matrix2[][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8}
}; // Compiler infers 2 rows
// Flat initialization (row-major order)
int matrix3[2][3] = {1, 2, 3, 4, 5, 6};
// Same as: {{1, 2, 3}, {4, 5, 6}}
Memory Layout (Row-Major Order)
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
Logical View: Memory Layout:
┌───┬───┬───┐ ┌───┬───┬───┬───┬───┬───┐
│ 1 │ 2 │ 3 │ Row 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │
├───┼───┼───┤ └───┴───┴───┴───┴───┴───┘
│ 4 │ 5 │ 6 │ Row 1 [0,0][0,1][0,2][1,0][1,1][1,2]
└───┴───┴───┘
Accessing 2D Array Elements
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// Access element at row 1, column 2
int element = matrix[1][2]; // 7
// Iterate with nested loops
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
cout << matrix[i][j] << " ";
}
cout << endl;
}
3D Arrays
int cube[2][3][4]; // 2 layers, 3 rows, 4 columns
// Initialization
int cube[2][3][4] = {
{ // Layer 0
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
},
{ // Layer 1
{13, 14, 15, 16},
{17, 18, 19, 20},
{21, 22, 23, 24}
}
};
// Access: cube[layer][row][col]
cout << cube[1][2][3]; // 24
Arrays and Functions
Passing Arrays to Functions
Arrays decay to pointers when passed to functions:
// These three declarations are equivalent:
void func(int arr[]);
void func(int arr[10]); // Size is ignored!
void func(int* arr);
// Must pass size separately
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
printArray(arr, 5);
}
Passing 2D Arrays
// Must specify all dimensions except first
void print2D(int arr[][4], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 4; j++) {
cout << arr[i][j] << " ";
}
cout << endl;
}
}
// Using templates for flexibility
template<size_t Rows, size_t Cols>
void print2DTemplate(int (&arr)[Rows][Cols]) {
for (size_t i = 0; i < Rows; i++) {
for (size_t j = 0; j < Cols; j++) {
cout << arr[i][j] << " ";
}
cout << endl;
}
}
Returning Arrays from Functions
// Cannot return C-style arrays directly
// Use pointers (with caution) or std::array
// Option 1: Use static array (dangerous - not thread-safe)
int* getArray() {
static int arr[5] = {1, 2, 3, 4, 5};
return arr;
}
// Option 2: Modify array passed by pointer
void fillArray(int arr[], int size, int value) {
for (int i = 0; i < size; i++) {
arr[i] = value;
}
}
// Option 3: Use std::array (recommended)
#include <array>
std::array<int, 5> getStdArray() {
return {1, 2, 3, 4, 5};
}
C-Style Strings
Character arrays used to represent strings:
#include <cstring>
// Declaration
char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
char str2[] = "Hello"; // Adds '\0' automatically
char str3[20] = "Hello"; // Remaining chars are '\0'
// Common operations
cout << strlen(str2) << endl; // 5 (length without '\0')
cout << sizeof(str2) << endl; // 6 (includes '\0')
// String copy
char dest[20];
strcpy(dest, str2); // Copy str2 to dest
// String concatenation
strcat(dest, " World"); // dest = "Hello World"
// String comparison
if (strcmp(str1, str2) == 0) {
cout << "Strings are equal" << endl;
}
// Input
char input[100];
cin >> input; // Reads until whitespace
cin.getline(input, 100); // Reads entire line
Warning: C-strings are error-prone. Prefer std::string for new code.
std::array (C++11)
A safer, more feature-rich alternative to C-style arrays:
#include <array>
// Declaration
std::array<int, 5> arr = {1, 2, 3, 4, 5};
// Size is part of the type
cout << arr.size() << endl; // 5
// Bounds-checked access
arr.at(2) = 10; // OK
arr.at(10) = 100; // Throws std::out_of_range
// Unchecked access (like C array)
arr[2] = 10; // No bounds checking
// Useful methods
arr.front(); // First element
arr.back(); // Last element
arr.fill(0); // Fill with value
arr.empty(); // Check if size is 0
// Can be passed/returned by value
std::array<int, 5> copy = arr;
// Works with STL algorithms
std::sort(arr.begin(), arr.end());
auto it = std::find(arr.begin(), arr.end(), 3);
// Comparison operators work
std::array<int, 3> a = {1, 2, 3};
std::array<int, 3> b = {1, 2, 3};
if (a == b) cout << "Equal" << endl;
Comparison: C-Array vs std::array
| Feature | C-Style Array | std::array |
|---|---|---|
| Size known at runtime | No (decays to pointer) | Yes (.size()) |
| Bounds checking | No | Yes (.at()) |
| Copyable | No | Yes |
| Works with STL | Limited | Full support |
| Can return from function | No | Yes |
| Memory overhead | None | None |
Common Array Operations
Finding Elements
int arr[] = {5, 2, 8, 1, 9, 3};
int size = 6;
// Linear search
int target = 8;
int index = -1;
for (int i = 0; i < size; i++) {
if (arr[i] == target) {
index = i;
break;
}
}
// Using std::find
#include <algorithm>
int* ptr = std::find(arr, arr + size, target);
if (ptr != arr + size) {
cout << "Found at index: " << (ptr - arr) << endl;
}
Sorting
#include <algorithm>
int arr[] = {5, 2, 8, 1, 9, 3};
int size = 6;
// Sort ascending
std::sort(arr, arr + size);
// Sort descending
std::sort(arr, arr + size, std::greater<int>());
Reversing
#include <algorithm>
int arr[] = {1, 2, 3, 4, 5};
std::reverse(arr, arr + 5); // {5, 4, 3, 2, 1}
Copying
#include <algorithm>
int src[] = {1, 2, 3, 4, 5};
int dest[5];
std::copy(src, src + 5, dest);
// Or: std::copy(std::begin(src), std::end(src), dest);
Filling
#include <algorithm>
int arr[10];
std::fill(arr, arr + 10, 42); // All elements = 42
Arrays vs Vectors
| Feature | Array | Vector |
|---|---|---|
| Size | Fixed at compile time | Dynamic (can grow/shrink) |
| Memory | Stack (usually) | Heap |
| Performance | Slightly faster | Nearly as fast |
| Safety | No bounds checking | .at() with bounds checking |
| Flexibility | Limited | Very flexible |
| Use case | Fixed-size, performance-critical | General purpose |
Recommendation: Use std::vector for most cases. Use std::array when you need fixed size. Use C-arrays only for legacy code or special requirements.
Best Practices
1. Always Initialize Arrays
// Bad: Contains garbage values
int arr[100];
// Good: Initialize to zero
int arr[100] = {};
2. Use Constants for Size
// Bad: Magic number
int arr[100];
// Good: Named constant
const int MAX_SIZE = 100;
int arr[MAX_SIZE];
// Better: constexpr
constexpr int MAX_SIZE = 100;
int arr[MAX_SIZE];
3. Pass Size with Array
// Bad: No way to know size
void process(int arr[]);
// Good: Pass size explicitly
void process(int arr[], int size);
4. Prefer std::array Over C-arrays
// C-style (avoid)
int arr[5] = {1, 2, 3, 4, 5};
// Modern C++ (prefer)
std::array<int, 5> arr = {1, 2, 3, 4, 5};
5. Check Bounds in Debug Mode
#ifdef DEBUG
if (index >= size) {
throw std::out_of_range("Index out of bounds");
}
#endif
arr[index] = value;
Common Mistakes
1. Array Out of Bounds
int arr[5] = {1, 2, 3, 4, 5};
arr[5] = 6; // ERROR: Index 5 is out of bounds!
2. Forgetting Size Decay
void func(int arr[]) {
int size = sizeof(arr) / sizeof(arr[0]); // WRONG!
// arr is a pointer, not an array
}
3. Returning Local Array
int* createArray() {
int arr[5] = {1, 2, 3, 4, 5};
return arr; // DANGER: Returns dangling pointer!
}
4. Missing Null Terminator in C-String
char str[5] = {'H', 'e', 'l', 'l', 'o'}; // No '\0'!
cout << str; // Undefined behavior - may print garbage
5. Comparing Arrays with ==
int a[3] = {1, 2, 3};
int b[3] = {1, 2, 3};
if (a == b) { } // WRONG: Compares pointers, not contents!
// Correct way:
bool equal = std::equal(a, a + 3, b);
Summary
| Concept | Key Points |
|---|---|
| Declaration | type name[size]; |
| Indexing | Zero-based, arr[0] to arr[size-1] |
| Size | sizeof(arr)/sizeof(arr[0]) or std::size(arr) |
| Iteration | Index loop, range-for, pointers |
| Multidimensional | arr[rows][cols], row-major order |
| Functions | Arrays decay to pointers, pass size separately |
| std::array | Safer alternative with size info and bounds checking |
| Best Practice | Prefer std::array or std::vector |
Compilation
g++ -std=c++17 -Wall -Wextra examples.cpp -o examples
./examples
Navigation
| Previous | Up | Next |
|---|---|---|
| Control Flow | Working with Data | Vectors |
Arrays are the foundation of data structures - master them to build more complex solutions!