Part of C in 100s

C in 100 Seconds: Arrays | Episode 10

Celest KimCelest Kim

Video: C in 100 Seconds: Store Five Numbers in One Variable — Arrays | Episode 10 by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

C Arrays: Store Many Values in One Variable

int nums[5] = {10, 20, 30, 40, 50};. Fixed-size, contiguous, zero-indexed. The starting point for every higher-level data structure in C.

C arrays are bare-metal — a chunk of contiguous memory, indexed from 0. No bounds checking. No length tracking. They're fast because they're simple, but you have to do the bookkeeping yourself.

The basic shape

#include <stdio.h>

int main() {
  int nums[] = {10, 20, 30, 40, 50};
  int len = sizeof(nums) / sizeof(nums[0]);

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

  nums[2] = 99;
  printf("\nAfter change: nums[2] = %d\n", nums[2]);

  return 0;
}

Output:

nums[0] = 10
nums[1] = 20
nums[2] = 30
nums[3] = 40
nums[4] = 50

After change: nums[2] = 99

Declaration syntax

int a[5];                          // 5 ints, uninitialized
int b[5] = {10, 20, 30, 40, 50};   // 5 ints, initialized
int c[] = {10, 20, 30};            // 3 ints, size inferred
int d[5] = {1, 2};                 // {1, 2, 0, 0, 0} — rest zero-filled
int e[5] = {0};                    // all zeros (idiom)

{0} zero-initializes the whole array — old C trick. C99+ has designated initializers:

int sparse[10] = {[3] = 99, [7] = 42};   // zeros except index 3 (99) and 7 (42)

Length: sizeof / sizeof[0]

int nums[] = {10, 20, 30, 40, 50};
int len = sizeof(nums) / sizeof(nums[0]);   // 5

sizeof(nums) is the byte size of the whole array (5 × 4 = 20 bytes for int[5]). sizeof(nums[0]) is the byte size of one element (4 for int). Divide: 5.

This only works in the same scope where the array was declared. When you pass an array to a function, it decays to a pointer — sizeof then returns the pointer size, not the array size.

void process(int arr[]) {
  printf("%lu\n", sizeof(arr));   // 8 (pointer size on 64-bit), NOT array size
}

For functions, always pass the size as a separate parameter:

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

Zero-indexed

nums[0] is the first element. nums[len-1] is the last. There's no nums[-1] for "last" — that's a different memory address, undefined behavior.

int nums[5] = {10, 20, 30, 40, 50};
nums[0]    // 10
nums[4]    // 50
nums[5]    // OUT OF BOUNDS — undefined behavior
nums[-1]   // OUT OF BOUNDS — undefined behavior

C doesn't bounds-check. Reading past the end might return garbage, crash, or print your password. Tools like AddressSanitizer (gcc -fsanitize=address) catch most violations at runtime — use them during development.

Reading and writing

nums[2] = 99;             // assign
int x = nums[3];          // read
nums[i] = nums[i] * 2;    // doubles in place

Same [] syntax for reading and writing. The element type is whatever the array's element type is — here int.

Iteration

for (int i = 0; i < len; i++) {
  printf("%d ", nums[i]);
}

Standard for-loop with index. C has no for-each (that's C++). The i < len is the bounds check — your responsibility.

Initialization gotchas

int x[5];                  // garbage values — not zeroed
int y[5] = {0};            // {0, 0, 0, 0, 0} — initialized

Local arrays are not zero-initialized. Reading x[0] before assigning gives garbage. Always initialize.

Global and static arrays are zero-initialized:

int global_arr[100];   // all zeros at program start

2D arrays

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

for (int row = 0; row < 3; row++) {
  for (int col = 0; col < 3; col++) {
    printf("%d ", grid[row][col]);
  }
  printf("\n");
}

int grid[3][3] is "3 arrays of 3 ints." Stored contiguously in row-major order — grid[0][0], grid[0][1], grid[0][2], grid[1][0], ....

For passing to a function, the first dimension can be omitted but the second can't:

void print_grid(int grid[][3], int rows) { ... }   // OK
void print_grid(int grid[3][], int rows) { ... }   // ERROR

Array of strings (preview)

char *names[] = {"Alice", "Bob", "Charlie"};
for (int i = 0; i < 3; i++) {
  printf("%s\n", names[i]);
}

char *names[] is "array of pointers to char." Each element points to a string literal. Episode 28 covers this in detail.

Common mistakes

Off-by-one. for (int i = 0; i <= len; i++) reads nums[len] — past the end. Use i < len.

Pass to function and sizeof. Returns pointer size, not array size. Pass the length explicitly.

Modifying past the end. nums[10] = 5 on a 5-element array stomps unrelated memory. Stack arrays might corrupt local variables; heap arrays might corrupt the malloc bookkeeping (catastrophic).

Forgetting null-terminator on strings. char s[5] = "hello" doesn't fit ('h', 'e', 'l', 'l', 'o', '\0' is 6 bytes). Compiler warns but might silently truncate. Use char s[] = "hello" to size automatically.

int *p = nums;not a copy. Both point to the same array. To copy: memcpy or manual loop.

nums = other_array; — doesn't compile. Arrays aren't assignable. Copy via memcpy(nums, other, sizeof(nums)).

What's next

Episode 11: strings. Arrays of char, the \0 null terminator, and strlen / strcpy / strcmp.

Recap

int nums[5] = {10, 20, 30, 40, 50}. Zero-indexed, contiguous in memory. Length: sizeof(arr) / sizeof(arr[0])only valid where the array was declared. Passing to functions decays to a pointer — pass length separately. Local arrays aren't zero-initialized; use = {0}. C doesn't bounds-check — reads/writes past the end are undefined. 2D: int grid[3][3], row-major.

Next episode: strings.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.