All Courses
Working With Data

Arrays in C++


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.


Recommended Conventions

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

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