Notifications

No notifications

/Phase 2

Arrays in C

Storing Many Values Together 📚

An array is a numbered collection of values, all of the same type, stored in contiguous memory.

int marks[5] = {78, 92, 65, 88, 70};
//   ↑     ↑   ↑
//  type  size  initial values

Accessing Elements

printf("%d\n", marks[0]);   // 78  — first element
printf("%d\n", marks[4]);   // 70  — LAST element (index 4, not 5!)
marks[2] = 100;              // change 3rd element

> Indexes start at 0, end at size − 1.

Declaring Arrays — 3 Ways

int a[5];                            // 5 ints, values are GARBAGE
int b[5] = {0};                      // 5 ints, all zero
int c[5] = {1, 2, 3};                // {1, 2, 3, 0, 0} — rest zeroed
int d[]  = {10, 20, 30, 40};         // size inferred → 4

Looping Over an Array

int n = 5;
int arr[5] = {10, 20, 30, 40, 50};

for (int i = 0; i < n; i++) { printf("arr[%d] = %d\n", i, arr[i]); }

sizeof Trick

int a[10];
int n = sizeof(a) / sizeof(a[0]);   // 10

> ⚠️ This trick only works on real arrays in scope, not on pointers (more on that in Pointers).

2-D Arrays

int matrix[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};
printf("%d\n", matrix[1][2]);   // 6

On this page

Detailed Theory

Arrays are the most fundamental data structure in computing. Every list, every string, every image is — at some level — an array. Understanding them in C means understanding memory itself.

Memory Layout — Why Contiguous Matters

When you write int arr[5];, the compiler reserves 5 × 4 = 20 bytes of contiguous memory:

Memory:  [ arr[0] ][ arr[1] ][ arr[2] ][ arr[3] ][ arr[4] ]
Address:   1000      1004      1008      1012      1016

Indexing arr[i] is just address-of-arr + i × sizeof(int). That's why array access is O(1) — one multiply and one add, regardless of size.

This contiguous layout also makes arrays incredibly cache-friendly: when the CPU loads one element, it usually pulls neighbours into the cache for free.

The Bounds-Check Problem

C does not check array bounds. Ever. This is fast — and dangerous.

int a[5];
a[10] = 42;     // ❌ writes to memory that isn't yours
                //    might silently corrupt another variable
                //    might crash with a segfault
                //    might do nothing visible — until later

This is *the* most common source of bugs and security holes in C code. You are responsible for staying inside 0 .. size-1.

> Modern compilers + sanitizers (-fsanitize=address) will catch many of these at runtime — turn them on while learning.

Initialization — Watch the Garbage

int a[5];               // values: GARBAGE (whatever was in RAM)
int b[5] = {0};         // values: 0 0 0 0 0
int c[5] = {1, 2};      // values: 1 2 0 0 0  (rest zeroed)
int d[5] = {[2] = 99};  // values: 0 0 99 0 0 (designated initialiser, C99+)

/* For local arrays, ALWAYS initialise. Reading garbage is undefined behaviour. */

Global arrays are automatically zero-initialised — local ones are not.

Array Size Must Be Known at Compile Time (mostly)

int n = 10;
int arr[n];        // ✅ C99 VLA (Variable-Length Array) — supported but discouraged
int arr2[CONST];   // ✅ if CONST is a #define or constant expression

For dynamic sizes, use malloc (covered in Pointers / Memory). VLAs are easy to overflow the stack.

sizeof on Arrays — A Subtle Point

int a[10];
printf("%zu\n", sizeof(a));      // 40  (10 × 4 bytes)
printf("%zu\n", sizeof(a)/sizeof(a[0]));   // 10  (count of elements)

This works only when a is a real array in the same scope. The moment you pass an array to a function, it decays to a pointer and sizeof returns the pointer's size (8 bytes on 64-bit), not the array's size:

void bad(int a[]) {
    printf("%zu\n", sizeof(a));   // 8 — NOT what you want!
}

Lesson: when passing arrays, always pass the size as a separate parameter:

void good(int a[], int n) {
    for (int i = 0; i < n; i++) ...
}

Multi-Dimensional Arrays

int grid[3][4];               // 3 rows × 4 columns
grid[1][2] = 7;               // row 1, column 2

Memory layout is row-major in C: row 0 is stored first, then row 1, then row 2:

[0][0] [0][1] [0][2] [0][3] [1][0] [1][1] ...

Loop in this order for cache efficiency:

for (int r = 0; r < 3; r++)        /* outer = row */
    for (int c = 0; c < 4; c++)    /* inner = column */
        grid[r][c] = 0;

Common Operations

/* Sum */
int sum = 0;
for (int i = 0; i < n; i++) sum += a[i];

/* Find max */ int max = a[0]; for (int i = 1; i < n; i++) if (a[i] > max) max = a[i];

/* Linear search */ int idx = -1; for (int i = 0; i < n; i++) if (a[i] == target) { idx = i; break; }

/* Reverse in place */ for (int i = 0, j = n - 1; i < j; i++, j--) { int tmp = a[i]; a[i] = a[j]; a[j] = tmp; }

Common Mistakes

BugSymptom
Reading uninit local arrayRandom garbage values
a[size] instead of a[size-1] for lastOut-of-bounds — undefined
Forgetting array size in functionsizeof returns pointer size
Comparing arrays with ==Compares addresses, not contents
Returning a local array from a functionMemory invalid after return

Master arrays and the next steps — strings, pointers, dynamic memory — fall into place.