C

Arrays

An array is a fixed number of values of the same type, stored back to back in one contiguous block of memory, accessed by a numeric position. It's C's most basic collection type — no dynamic resizing, no built-in bounds checking, just a block of memory and an index into it.

Quick reference
What it is
A fixed-size, contiguous block of same-typed values, accessed by a zero-based numeric index.
Mental model
A row of same-size mailboxes, numbered starting at 0 — the array's name is really just the address of the first mailbox.
Key numbers
Indexing starts at 0, not 1 — the last valid index of an array of size N is N - 1.
Reach for it when
You need a fixed number of same-typed values you'll access by position — sensor readings, a lookup table, a fixed-size buffer.

Declaring and indexing an array

int readings[5];              // an array of 5 ints, uninitialized
int scores[5] = {90, 85, 77, 92, 60};   // declared and initialized at once

printf("%d\n", scores[0]);    // 90 — the FIRST element
printf("%d\n", scores[4]);    // 60 — the LAST element (size 5, so indices 0-4)

scores[2] = 100;               // overwrite the element at index 2

The number inside the square brackets when declaring ([5]) is the array's fixed size — decided once, at declaration, and never changeable afterward. The number inside square brackets when using the array (scores[2]) is an index — which element you're reading or writing.

Memory layout

The five elements of scores sit in one contiguous block of memory, each 4 bytes, with scores itself referring to the address of index 0 int scores[5] = {90, 85, 77, 92, 60}; 90 85 77 92 60 [0] [1] [2] [3] [4] One block, no gaps — element [i] always sits at (start address) + i × (element size)
This contiguity is exactly why indexing is fast (one address calculation, no searching) and exactly why there's no bounds checking — the compiler trusts the index you give it.

Iterating over an array

int scores[5] = {90, 85, 77, 92, 60};
int total = 0;

for (int i = 0; i < 5; i++) {
    total += scores[i];
}
printf("Average: %d\n", total / 5);   // Average: 80

A for loop counting up to the array's size is the standard, idiomatic way to touch every element — see Loops if this shape isn't familiar yet.

2D arrays

An array of arrays — useful for grid-like data (a small image, a game board, a matrix):

int grid[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};

printf("%d\n", grid[1][2]);   // 6 — row 1, column 2

In memory, a 2D array is still one contiguous block — grid[1][2] is really shorthand for "skip past row 0 entirely, then take column 2 of row 1." Rows are stored one after another, left to right, top to bottom (row-major order).

Arrays and pointers

An array's name, used almost anywhere in an expression, automatically becomes a pointer to its first element — this is called array decay. It's why scores and &scores[0] print the exact same address:

int scores[5] = {90, 85, 77, 92, 60};
printf("%p\n", (void*)scores);       // e.g. 0x7ffee2a1
printf("%p\n", (void*)&scores[0]);   // exact same address

This relationship is genuinely important and genuinely confusing at first — it's covered in full depth, including exactly where it breaks down, in Embedded C's Pointers chapter, worth reading once the next chapter (Pointers, in this domain) has introduced the basics.

Real-world applications

Gotchas

Field notes