Chapter 15Beginner

Arrays in C

Store multiple values of the same type together! Learn 1D arrays (lists), 2D arrays (tables), and how arrays are stored in memory.

22 min readUpdated 2024-12-16
arrays1D array2D arrayindexinitializationsizeofmemory layout

What You Will Learn

  • โœ“Create and initialize arrays
  • โœ“Access elements using index (arr[0])
  • โœ“Loop through arrays
  • โœ“Work with 2D arrays (rows and columns)

01What is an Array?

An array is a collection of elements of the same data type stored in contiguous (adjacent) memory locations. Think of it like a row of boxes, where each box can hold one value and has a number (index) to identify it.

๐ŸŽฏ Why Use Arrays?

Without Arrays โŒ

main.c
C
int score1 = 85;
int score2 = 90;
int score3 = 78;
int score4 = 92;
int score5 = 88;
// Tedious with 100 students!

With Arrays โœ“

main.c
C
int scores[5] = {85, 90, 78, 92, 88};
// One variable for all 5 scores!
// Easy to loop through

Key Array Facts

  • โ€ข All elements must be the same type (all int, all float, etc.)
  • โ€ข Array size is fixed once declared (cannot grow or shrink)
  • โ€ข Index starts at 0, not 1 (first element is array[0])
  • โ€ข Elements are stored in contiguous memory

02One-Dimensional (1D) Arrays

A 1D array is the simplest form โ€” a single row of elements. Like a list of items.

Declaration Syntax

๐Ÿ“ This snippet shows the basic syntax for declaring arrays of different data types.

array_declaration.c
C
1// Syntax: dataType arrayName[size];
2
3int numbers[5]; // Array of 5 integers
4float prices[10]; // Array of 10 floats
5char letters[26]; // Array of 26 characters

Initialization Methods

๐Ÿ“ This program demonstrates 5 different ways to create and fill arrays with values. Run it to see how each method works.

1d_array_init.c
C
1#include <stdio.h>
2
3int main() {
4 // Method 1: Initialize at declaration
5 int scores[5] = {85, 90, 78, 92, 88};
6
7 // Method 2: Partial initialization (remaining = 0)
8 int values[5] = {10, 20}; // {10, 20, 0, 0, 0}
9
10 // Method 3: All zeros
11 int zeros[5] = {0}; // {0, 0, 0, 0, 0}
12
13 // Method 4: Let compiler count size
14 int auto_size[] = {1, 2, 3, 4}; // Size = 4
15
16 // Method 5: Initialize after declaration
17 int grades[3];
18 grades[0] = 95;
19 grades[1] = 87;
20 grades[2] = 91;
21
22 // Accessing elements
23 printf("First score: %d\n", scores[0]); // 85
24 printf("Last score: %d\n", scores[4]); // 88
25
26 // Calculate size
27 int size = sizeof(scores) / sizeof(scores[0]);
28 printf("Array size: %d\n", size); // 5
29
30 return 0;
31}

๐Ÿ” How This Program Works

1

int scores[5] = {85, 90, 78, 92, 88} โ€” Creates an array of 5 integers and fills them with values at once.

2

int values[5] = {10, 20} โ€” Only first 2 values given; remaining 3 are automatically set to 0.

3

int auto_size[] = {1, 2, 3, 4} โ€” Compiler counts elements and sets size = 4 automatically.

4

scores[0] accesses the first element (85), scores[4] accesses the last (88).

5

sizeof(scores) / sizeof(scores[0]) โ€” Calculates array size: total bytes รท bytes per element = number of elements.

๐Ÿง  Memory Layout of 1D Array

When you create int arr[5], the computer allocates 5 ร— 4 = 20 bytes of contiguous memory (assuming int is 4 bytes).

Memory Layout: int arr[5] = {10, 20, 30, 40, 50}

10

arr[0]

20

arr[1]

30

arr[2]

40

arr[3]

50

arr[4]

1000

1004

1008

1012

1016

Memory addresses (each int = 4 bytes)

Memory Size Calculation

Total memory = number_of_elements ร— size_of_each_element
For int arr[5]: 5 ร— 4 bytes = 20 bytes

๐Ÿ“ Address Calculation in 1D Arrays

The CPU uses a simple formula to find any element's memory address. This is why arrays allow O(1) constant-time access โ€” no matter which index you access!

๐Ÿ”ข The Address Formula

Address of arr[i] = Base Address + (i ร— size_of_element)

Base Address

Starting memory location of arr[0]

i (Index)

Position of element (0, 1, 2...)

size_of_element

Bytes per element (int=4, char=1)

Example: int arr[5] with Base Address = 1000

Index (i)CalculationAddressValue
01000 + (0 ร— 4)100010
11000 + (1 ร— 4)100420
21000 + (2 ร— 4)100830
31000 + (3 ร— 4)101240
41000 + (4 ร— 4)101650

๐Ÿ“ This program prints the actual memory address of each array element and shows how the formula calculates them.

address_calculation_1d.c
C
1#include <stdio.h>
2
3int main() {
4 int arr[5] = {10, 20, 30, 40, 50};
5
6 // Base address (address of first element)
7 printf("Base Address (arr or &arr[0]): %p\n", (void*)arr);
8
9 // Calculate and verify addresses
10 for (int i = 0; i < 5; i++) {
11 // Manual calculation
12 // Address = Base + (i ร— sizeof(int))
13 printf("arr[%d]: Address = %p, ", i, (void*)&arr[i]);
14 printf("Calculated = Base + (%d ร— %zu) = %p\n",
15 i, sizeof(int), (void*)(arr + i));
16 }
17
18 // Pointer arithmetic does this automatically!
19 // arr + i is same as &arr[i]
20 printf("\narr + 2 = %p (same as &arr[2])\n", (void*)(arr + 2));
21
22 return 0;
23}

๐Ÿ’ก Why This Matters

Because of this formula, accessing arr[0] or arr[999999]takes the same time! The CPU just does one multiplication and one addition โ€” instant O(1) access.

Looping Through Arrays

๐Ÿ“ This program uses a for loop to print all elements with their addresses, then calculates sum and average.

array_loop.c
C
1#include <stdio.h>
2
3int main() {
4 int numbers[5] = {10, 20, 30, 40, 50};
5 int size = sizeof(numbers) / sizeof(numbers[0]);
6
7 // Base address of array
8 printf("Base Address: %p\n\n", (void*)numbers);
9
10 // Print all elements with their addresses
11 printf("Array elements with addresses:\n");
12 printf("-----------------------------------------------\n");
13 printf("Index | Value | Address | Address Calculation\n");
14 printf("-----------------------------------------------\n");
15 for (int i = 0; i < size; i++) {
16 printf(" %d | %2d | %p | Base + (%d ร— %zu)\n",
17 i, numbers[i], (void*)&numbers[i], i, sizeof(int));
18 }
19 printf("-----------------------------------------------\n");
20
21 // Calculate sum
22 int sum = 0;
23 for (int i = 0; i < size; i++) {
24 sum += numbers[i];
25 }
26 printf("\nSum: %d\n", sum);
27 printf("Average: %.2f\n", (float)sum / size);
28
29 return 0;
30}
Output โ€” array_loop.c

$ ./array_loop


Base Address: 0x7ffd5c3e1a00


Array elements with addresses:

-----------------------------------------------

Index | Value | Address | Calculation

-----------------------------------------------

0 | 10 | 0x7ffd5c3e1a00 | Base + (0 ร— 4)

1 | 20 | 0x7ffd5c3e1a04 | Base + (1 ร— 4)

2 | 30 | 0x7ffd5c3e1a08 | Base + (2 ร— 4)

3 | 40 | 0x7ffd5c3e1a0c | Base + (3 ร— 4)

4 | 50 | 0x7ffd5c3e1a10 | Base + (4 ร— 4)

-----------------------------------------------


Sum: 150

Average: 30.00

๐Ÿ” How This Loop Works Step-by-Step

1

sizeof(numbers) / sizeof(numbers[0]) calculates size: 20 bytes รท 4 bytes = 5 elements.

2

for (int i = 0; i < size; i++) โ€” Loop runs 5 times: i = 0, 1, 2, 3, 4. Stops when i becomes 5.

3

numbers[i] โ€” Each iteration accesses a different element: numbers[0]=10, numbers[1]=20, etc.

4

sum += numbers[i] โ€” Adds each element to sum: 0+10+20+30+40+50 = 150.

5

(float)sum / size โ€” Casts sum to float for decimal division: 150.0 รท 5 = 30.00.

03Two-Dimensional (2D) Arrays

A 2D array is like a table with rows and columns. Think of it as a grid, spreadsheet, or a matrix.

Real-World Examples of 2D Arrays

๐Ÿ“Š

Spreadsheet

Rows ร— Columns of data

๐Ÿ–ผ๏ธ

Image Pixels

Width ร— Height grid

โ™Ÿ๏ธ

Chess Board

8 ร— 8 squares

Declaration & Initialization

๐Ÿ“ This program shows 3 ways to create 2D arrays and how to access elements using [row][column] notation.

2d_array_init.c
C
1#include <stdio.h>
2
3int main() {
4 // Syntax: dataType name[rows][columns];
5
6 // Method 1: Full initialization
7 int matrix[3][4] = {
8 {1, 2, 3, 4}, // Row 0
9 {5, 6, 7, 8}, // Row 1
10 {9, 10, 11, 12} // Row 2
11 };
12
13 // Method 2: Linear initialization (fills row by row)
14 int grid[2][3] = {1, 2, 3, 4, 5, 6};
15 // Same as: {{1,2,3}, {4,5,6}}
16
17 // Method 3: Partial initialization
18 int partial[2][3] = {{1, 2}, {4}};
19 // Result: {{1,2,0}, {4,0,0}}
20
21 // Accessing elements: array[row][column]
22 printf("matrix[0][0] = %d\n", matrix[0][0]); // 1
23 printf("matrix[1][2] = %d\n", matrix[1][2]); // 7
24 printf("matrix[2][3] = %d\n", matrix[2][3]); // 12
25
26 return 0;
27}

๐Ÿง  Memory Layout of 2D Array

Even though we think of 2D arrays as tables, they're stored in memory as one continuous block (row-major order). Row 0 comes first, then Row 1, etc.

int matrix[2][3] โ€” Logical View vs Memory View

Logical View (How We Think)

Col 0Col 1Col 2
Row 0123
Row 1456

Memory View (How It's Actually Stored)

1

[0][0]

2

[0][1]

3

[0][2]

4

[1][0]

5

[1][1]

6

[1][2]

Contiguous memory: Row 0 โ†’ Row 1 (row-major order)

2D Array Memory Calculation

Total memory = rows ร— columns ร— size_of_element
For int matrix[3][4]: 3 ร— 4 ร— 4 bytes = 48 bytes

๐Ÿ“ Address Calculation in 2D Arrays

For 2D arrays, the formula must account for row-major order โ€” complete rows are stored one after another. To find any element, we first "skip" complete rows, then move within that row.

๐Ÿ”ข Row-Major Order Formula

Address of arr[i][j] = Base + ((i ร— COLS) + j) ร— size

Base

Address of arr[0][0]

i ร— COLS

Skip i complete rows

+ j

Move j columns in row

ร— size

Bytes per element

Example: int matrix[3][4] with Base = 2000

Each row has 4 columns (COLS = 4)

ElementFormulaCalculationAddress
arr[0][0]2000 + ((0ร—4)+0)ร—42000 + 02000
arr[0][2]2000 + ((0ร—4)+2)ร—42000 + 82008
arr[1][0]2000 + ((1ร—4)+0)ร—42000 + 162016
arr[1][3]2000 + ((1ร—4)+3)ร—42000 + 282028
arr[2][1]2000 + ((2ร—4)+1)ร—42000 + 362036

๐Ÿ“ This program demonstrates how to manually calculate the address of matrix[1][2] using the row-major formula, then verifies it matches the actual address.

address_calculation_2d.c
C
1#include <stdio.h>
2
3int main() {
4 int matrix[3][4] = {
5 {1, 2, 3, 4},
6 {5, 6, 7, 8},
7 {9, 10, 11, 12}
8 };
9
10 int rows = 3, cols = 4;
11 int *base = &matrix[0][0]; // Base address
12
13 printf("Base Address: %p\n\n", (void*)base);
14
15 // Calculate address for matrix[1][2]
16 int i = 1, j = 2;
17
18 // Formula: Base + ((i ร— COLS) + j) ร— sizeof(element)
19 int offset = (i * cols) + j; // Linear position: 1ร—4 + 2 = 6
20 int *calculated_addr = base + offset;
21
22 printf("Finding matrix[%d][%d]:\n", i, j);
23 printf(" Step 1: Skip %d complete rows = %d ร— %d = %d elements\n",
24 i, i, cols, i * cols);
25 printf(" Step 2: Move %d columns in current row\n", j);
26 printf(" Step 3: Total offset = %d elements = %d bytes\n",
27 offset, offset * (int)sizeof(int));
28 printf(" Calculated Address: %p\n", (void*)calculated_addr);
29 printf(" Actual Address (&matrix[%d][%d]): %p\n", i, j, (void*)&matrix[i][j]);
30 printf(" Value: %d\n", *calculated_addr);
31
32 return 0;
33}

๐Ÿง  Think of it Like This

To reach arr[2][1] in a 3ร—4 matrix:
Step 1: Skip 2 complete rows = 2 ร— 4 = 8 elements
Step 2: Move 1 column into row 2 = 1 element
Total: 8 + 1 = 9 elements from base = 9 ร— 4 = 36 bytes

Looping Through 2D Arrays

๐Ÿ“ This program uses nested loops (outer for rows, inner for columns) to print the matrix as a table, show address calculations for each element, and calculate the total sum.

2d_array_loop.c
C
1#include <stdio.h>
2
3int main() {
4 int matrix[3][4] = {
5 {1, 2, 3, 4},
6 {5, 6, 7, 8},
7 {9, 10, 11, 12}
8 };
9
10 int rows = 3;
11 int cols = 4;
12 int *base = &matrix[0][0];
13
14 printf("Base Address: %p\n\n", (void*)base);
15
16 // Print as table
17 printf("Matrix:\n");
18 for (int i = 0; i < rows; i++) {
19 for (int j = 0; j < cols; j++) {
20 printf("%3d ", matrix[i][j]);
21 }
22 printf("\n");
23 }
24
25 // Show address calculation for each element
26 printf("\nAddress Calculation (Row-Major Order):\n");
27 printf("----------------------------------------------------------\n");
28 printf("Element | Value | Offset Calculation | Address\n");
29 printf("----------------------------------------------------------\n");
30 for (int i = 0; i < rows; i++) {
31 for (int j = 0; j < cols; j++) {
32 int offset = (i * cols) + j;
33 printf("matrix[%d][%d] | %2d | (%dร—%d)+%d = %2d elements | %p\n",
34 i, j, matrix[i][j], i, cols, j, offset, (void*)&matrix[i][j]);
35 }
36 }
37 printf("----------------------------------------------------------\n");
38
39 // Calculate sum
40 int sum = 0;
41 for (int i = 0; i < rows; i++) {
42 for (int j = 0; j < cols; j++) {
43 sum += matrix[i][j];
44 }
45 }
46 printf("\nSum of all elements: %d\n", sum);
47
48 return 0;
49}
Output โ€” 2d_array_loop.c

$ ./2d_array_loop


Base Address: 0x7ffd5c3e1a20


Matrix:

1 2 3 4

5 6 7 8

9 10 11 12


Address Calculation (Row-Major Order):

----------------------------------------------------------

Element | Value | Offset Calculation | Address

----------------------------------------------------------

matrix[0][0] | 1 | (0ร—4)+0 = 0 elements | 0x7ffd...1a20

matrix[0][1] | 2 | (0ร—4)+1 = 1 elements | 0x7ffd...1a24

matrix[0][2] | 3 | (0ร—4)+2 = 2 elements | 0x7ffd...1a28

matrix[1][0] | 5 | (1ร—4)+0 = 4 elements | 0x7ffd...1a30

matrix[1][2] | 7 | (1ร—4)+2 = 6 elements | 0x7ffd...1a38

...

----------------------------------------------------------


Sum of all elements: 78

04Three-Dimensional (3D) Arrays

A 3D array adds another dimension โ€” think of it as multiple 2D tables stacked together. It has layers, rows, and columns.

Real-World Examples of 3D Arrays

๐Ÿ“š

Multiple Spreadsheets

Sheets ร— Rows ร— Columns

๐ŸŽฌ

Video Frames

Frames ร— Height ร— Width

๐Ÿข

Building Floors

Floors ร— Rows ร— Rooms

Declaration & Initialization

๐Ÿ“ This program creates a 3D array (2 layers ร— 3 rows ร— 4 columns) and shows how to access elements using [layer][row][column] notation.

3d_array_init.c
C
1#include <stdio.h>
2
3int main() {
4 // Syntax: dataType name[layers][rows][columns];
5
6 // 3D Array: 2 layers, each with 3 rows and 4 columns
7 int cube[2][3][4] = {
8 // Layer 0 (first 2D table)
9 {
10 {1, 2, 3, 4},
11 {5, 6, 7, 8},
12 {9, 10, 11, 12}
13 },
14 // Layer 1 (second 2D table)
15 {
16 {13, 14, 15, 16},
17 {17, 18, 19, 20},
18 {21, 22, 23, 24}
19 }
20 };
21
22 // Accessing elements: array[layer][row][column]
23 printf("cube[0][0][0] = %d\n", cube[0][0][0]); // 1
24 printf("cube[0][2][3] = %d\n", cube[0][2][3]); // 12
25 printf("cube[1][1][2] = %d\n", cube[1][1][2]); // 19
26
27 return 0;
28}

๐Ÿง  Memory Layout of 3D Array

3D arrays are also stored in contiguous memory. Layer 0 comes first (all its rows), then Layer 1, and so on.

int cube[2][2][3] โ€” Visualization

Layer 0 (cube[0])

123
456

Layer 1 (cube[1])

789
101112

In memory: [1,2,3,4,5,6] โ†’ [7,8,9,10,11,12] (Layer 0 โ†’ Layer 1)

3D Array Memory Calculation

Total memory = layers ร— rows ร— columns ร— size_of_element
For int cube[2][3][4]: 2 ร— 3 ร— 4 ร— 4 bytes = 96 bytes

๐Ÿ“ Address Calculation in 3D Arrays

For 3D arrays, we extend the formula: first skip complete layers, then skip rows within that layer, then move within the row.

๐Ÿ”ข 3D Array Address Formula

Address of arr[i][j][k] = Base + [(i ร— ROWS ร— COLS) + (j ร— COLS) + k] ร— size

i ร— ROWS ร— COLS

Skip i complete layers

j ร— COLS

Skip j rows in layer

k

Column offset in row

ร— size

Bytes per element

Example: int cube[2][3][4] with Base = 3000

2 layers ร— 3 rows ร— 4 columns

ElementCalculationOffsetAddress
cube[0][0][0](0ร—3ร—4)+(0ร—4)+0 = 00 ร— 4 = 03000
cube[0][1][2](0ร—3ร—4)+(1ร—4)+2 = 66 ร— 4 = 243024
cube[1][0][0](1ร—3ร—4)+(0ร—4)+0 = 1212 ร— 4 = 483048
cube[1][2][3](1ร—3ร—4)+(2ร—4)+3 = 2323 ร— 4 = 923092

๐Ÿ“ This program finds the address of cube[1][2][3] by calculating: skip 1 layer + skip 2 rows + move 3 columns, then verifies the result.

address_calculation_3d.c
C
1#include <stdio.h>
2
3int main() {
4 int cube[2][3][4] = {
5 {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}},
6 {{13,14,15,16}, {17,18,19,20}, {21,22,23,24}}
7 };
8
9 int layers = 2, rows = 3, cols = 4;
10 int *base = &cube[0][0][0];
11
12 printf("Base Address: %p\n\n", (void*)base);
13
14 // Calculate address for cube[1][2][3]
15 int i = 1, j = 2, k = 3;
16
17 // Formula: Base + (iร—ROWSร—COLS + jร—COLS + k) ร— size
18 int offset = (i * rows * cols) + (j * cols) + k;
19 int *calculated = base + offset;
20
21 printf("Finding cube[%d][%d][%d]:\n", i, j, k);
22 printf(" Skip %d layers: %d ร— %d ร— %d = %d elements\n",
23 i, i, rows, cols, i * rows * cols);
24 printf(" Skip %d rows: %d ร— %d = %d elements\n",
25 j, j, cols, j * cols);
26 printf(" Column offset: %d elements\n", k);
27 printf(" Total: %d + %d + %d = %d elements = %d bytes\n",
28 i * rows * cols, j * cols, k, offset, offset * 4);
29 printf(" Address: %p, Value: %d\n", (void*)calculated, *calculated);
30
31 return 0;
32}

Looping Through 3D Arrays

๐Ÿ“ This program uses 3 nested loops (layers โ†’ rows โ†’ columns) to print each layer as a separate table and calculate the total sum of all 24 elements.

3d_array_loop.c
C
1#include <stdio.h>
2
3int main() {
4 int cube[2][3][4] = {
5 {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}},
6 {{13,14,15,16}, {17,18,19,20}, {21,22,23,24}}
7 };
8
9 int layers = 2, rows = 3, cols = 4;
10
11 // Print all layers
12 for (int l = 0; l < layers; l++) {
13 printf("=== Layer %d ===\n", l);
14 for (int r = 0; r < rows; r++) {
15 for (int c = 0; c < cols; c++) {
16 printf("%3d ", cube[l][r][c]);
17 }
18 printf("\n");
19 }
20 printf("\n");
21 }
22
23 // Calculate total sum
24 int sum = 0;
25 for (int l = 0; l < layers; l++) {
26 for (int r = 0; r < rows; r++) {
27 for (int c = 0; c < cols; c++) {
28 sum += cube[l][r][c];
29 }
30 }
31 }
32 printf("Total sum: %d\n", sum); // 300
33
34 return 0;
35}

05Array Memory Size Summary

Array TypeDeclarationElementsMemory (int=4 bytes)
1Dint arr[5]55 ร— 4 = 20 bytes
2Dint arr[3][4]3 ร— 4 = 1212 ร— 4 = 48 bytes
3Dint arr[2][3][4]2 ร— 3 ร— 4 = 2424 ร— 4 = 96 bytes

๐Ÿ“ This simple program uses sizeof() to print the actual memory size of 1D, 2D, and 3D arrays. Run it to verify the calculations!

array_sizes.c
C
1#include <stdio.h>
2
3int main() {
4 int arr1D[5];
5 int arr2D[3][4];
6 int arr3D[2][3][4];
7
8 printf("Size of int: %zu bytes\n", sizeof(int));
9 printf("1D array [5]: %zu bytes\n", sizeof(arr1D));
10 printf("2D array [3][4]: %zu bytes\n", sizeof(arr2D));
11 printf("3D array [2][3][4]: %zu bytes\n", sizeof(arr3D));
12
13 return 0;
14}

06Common Mistakes

โŒ Array Index Out of Bounds

main.c
C
int arr[5] = {1, 2, 3, 4, 5};
printf("%d", arr[5]); // ERROR! Valid indices are 0-4
printf("%d", arr[-1]); // ERROR! No negative indices

C doesn't check array bounds! Accessing invalid indices causes undefined behavior.

โŒ Forgetting Array Indices Start at 0

main.c
C
int scores[5] = {85, 90, 78, 92, 88};
// First element is scores[0], NOT scores[1]
// Last element is scores[4], NOT scores[5]

โŒ Using sizeof() Wrong in Functions

main.c
C
void printArray(int arr[]) {
// sizeof(arr) returns pointer size, NOT array size!
int size = sizeof(arr) / sizeof(arr[0]); // WRONG!
}
// Solution: Pass size as a parameter

07Summary

What You Learned:

  • โœ“1D Arrays: Single row of elements, accessed with arr[i]
  • โœ“2D Arrays: Table with rows/columns, accessed with arr[row][col]
  • โœ“3D Arrays: Stacked tables, accessed with arr[layer][row][col]
  • โœ“Memory: Arrays are stored in contiguous memory (row-major order)
  • โœ“Index: Always starts at 0, last element is at size-1

08Next Steps

Continue learning about other derived data types: