Day 2 - Arrays and StringsArraysDeclaration & Initialization

Declaration and Initialization

Before we can use an array, we need to declare and initialize it. The syntax varies slightly between programming languages, but the underlying concept remains the same: reserving a block of memory for our data.

Remember: In languages like C++ and Java, arrays have a fixed size once declared. In Python (Lists) and JavaScript, they are dynamic and can grow or shrink.

How to Declare an Array

Declaring an array tells the compiler or interpreter that we want to create an array variable.

In C++, we specify the type of elements and the name of the array. The size is often required for static arrays.

// Declaration with size
int numbers[5];
 
// Declaration with initialization
int scores[] = {85, 90, 78, 92, 88};
 
// Using a vector (dynamic array)
#include <vector>
vector<int> dynamicArr = {1, 2, 3, 4, 5};

Accessing Elements

Once declared, you can access elements using their index. Remember that array indexing typically starts at 0.

⚠️

Attempting to access an index outside the bounds of the array (e.g., index 5 in an array of size 5) will result in an error or undefined behavior depending on the language.

int scores[] = {85, 90, 78, 92, 88};
 
// Accessing the first element
int firstScore = scores[0]; // 85
 
// Accessing the last element
int lastScore = scores[4]; // 88
 
// Modifying an element
scores[2] = 95;  // Changes 78 to 95
85
[0]
90
[1]
modified
95
[2]
92
[3]
88
[4]

2D Arrays (Matrices)

A 2D array is essentially an array of arrays — think of it as a table with rows and columns.

// Declaration: 3 rows, 4 columns
int matrix[3][4];
 
// Declaration with initialization
int grid[3][3] = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
 
// Accessing: grid[row][col]
int center = grid[1][1];  // 5
 
// Iterating through a 2D array
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        cout << grid[i][j] << " ";
    }
    cout << endl;
}

2D Array Memory Layout

In memory, a 2D array is actually stored as a 1D array. There are two ways to lay it out:

Row-Major Order (C, C++, Java, Python): Elements of each row are stored together.

grid[0][0], grid[0][1], grid[0][2], grid[1][0], grid[1][1], ...

Column-Major Order (Fortran, MATLAB): Elements of each column are stored together.

grid[0][0], grid[1][0], grid[2][0], grid[0][1], grid[1][1], ...

Performance tip: When iterating through a 2D array in a row-major language (C++, Java, Python), iterate row-by-row (for i... for j...) for better cache performance. Going column-by-column causes cache misses.

Common Pitfalls

1. Off-by-One Errors

int arr[5] = {10, 20, 30, 40, 50};
// Valid indices: 0, 1, 2, 3, 4
// arr[5] is OUT OF BOUNDS -- undefined behavior in C++!

2. Uninitialized Arrays

int arr[5];  // In C++, contains garbage values!
// Always initialize: int arr[5] = {0};  // All zeros

3. Python’s Shallow Copy Trap

# This creates 3 references to the SAME inner list
bad = [[0] * 3] * 3
bad[0][0] = 1
print(bad)  # [[1, 0, 0], [1, 0, 0], [1, 0, 0]]  -- Surprise!
 
# Correct way: each row is a separate list
good = [[0] * 3 for _ in range(3)]
good[0][0] = 1
print(good)  # [[1, 0, 0], [0, 0, 0], [0, 0, 0]]  -- As expected

4. Array Size Confusion

int arr[] = {1, 2, 3, 4, 5};
int size = sizeof(arr) / sizeof(arr[0]);  // 5 -- works here
 
void broken(int arr[]) {
    // sizeof(arr) gives pointer size (8 bytes), NOT array size!
    // Always pass size as a separate parameter
}

Quick Check

What is the output of this Python code?\n\na = [[0]*3]*2\na[0][1] = 5\nprint(a[1][1])
In a 3x3 grid stored in row-major order starting at address 100 (4 bytes per int), what is the address of grid[2][1]?