Docs

README

Arrays in C++

Table of Contents

  1. Introduction
  2. What is an Array?
  3. Array Declaration and Initialization
  4. Accessing Array Elements
  5. Array Size and Bounds
  6. Multidimensional Arrays
  7. Arrays and Functions
  8. C-Style Strings (Character Arrays)
  9. std::array (C++11)
  10. Common Array Operations
  11. Arrays vs Vectors
  12. Best Practices
  13. Common Mistakes
  14. 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

PropertyDescription
Fixed SizeSize determined at compile time (for C-style arrays)
HomogeneousAll elements must be same type
ContiguousElements stored in consecutive memory
Zero-indexedFirst element is at index 0
Direct AccessO(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

FeatureC-Style Arraystd::array
Size known at runtimeNo (decays to pointer)Yes (.size())
Bounds checkingNoYes (.at())
CopyableNoYes
Works with STLLimitedFull support
Can return from functionNoYes
Memory overheadNoneNone

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

FeatureArrayVector
SizeFixed at compile timeDynamic (can grow/shrink)
MemoryStack (usually)Heap
PerformanceSlightly fasterNearly as fast
SafetyNo bounds checking.at() with bounds checking
FlexibilityLimitedVery flexible
Use caseFixed-size, performance-criticalGeneral 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

ConceptKey Points
Declarationtype name[size];
IndexingZero-based, arr[0] to arr[size-1]
Sizesizeof(arr)/sizeof(arr[0]) or std::size(arr)
IterationIndex loop, range-for, pointers
Multidimensionalarr[rows][cols], row-major order
FunctionsArrays decay to pointers, pass size separately
std::arraySafer alternative with size info and bounds checking
Best PracticePrefer std::array or std::vector

Compilation

g++ -std=c++17 -Wall -Wextra examples.cpp -o examples
./examples

Navigation


Arrays are the foundation of data structures - master them to build more complex solutions!

README - C++ Tutorial | DeepML