C in 100 Seconds: Arrays | Episode 10
Video: C in 100 Seconds: Store Five Numbers in One Variable — Arrays | Episode 10 by Taught by Celeste AI - AI Coding Coach
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.