A structured lesson workspace with readable content, hands-on examples, and a clean path to completion.
An array is a contiguous block of same-typed elements. That is it. No bounds checking. No length metadata. No iterator protocol. You get a starting address and a count that you must track yourself. The language trusts you to stay within bounds. Break that trust and you read or write arbitrary memory. This is not a design flaw. It is the design. Everything else in C's memory model builds on this foundation.
#include <stdio.h>int main(void) {int scores[5] = {92, 85, 78, 96, 88};int zeros[4] = {0};int inferred[] = {1, 2, 3, 4, 5, 6};printf("scores[0] = %d\n", scores[0]);printf("scores[4] = %d\n", scores[4]);printf("zeros[3] = %d\n", zeros[3]);printf("inferred has %zu elements\n", sizeof(inferred) / sizeof(inferred[0]));return 0;}
The {0} initializer zero-fills the entire array, not just the first element. Omitting the size and letting the compiler infer it from the initializer list is convenient but means you cannot add elements later without updating every sizeof calculation. The sizeof trick only works in the same scope where the array was declared. Pass it to a function and sizeof gives you the pointer size, not the array size. We will beat this point repeatedly because it causes real bugs.
#include <stdio.h>int main(void) {int arr[3] = {10, 20, 30};printf("arr[2] = %d\n", arr[2]);printf("arr[3] = %d\n", arr[3]);printf("arr[10] = %d\n", arr[10]);printf("arr[-1] = %d\n", arr[-1]);return 0;}
Without ASan, those out-of-bounds reads would print whatever garbage sits adjacent on the stack. No crash. No error. Just wrong data flowing silently through your program. With ASan, you get an immediate report with the exact line number. Develop with ASan. Always.
In most expressions, an array name evaluates to the address of its first element. arr is equivalent to &arr[0]. This is called decay. It happens when you pass an array to a function, assign it to a pointer, or use it in arithmetic. The sizeof exception is one of the few contexts where the array retains its full type. This decay behavior is the root of the array-pointer confusion that plagues C learners, and we dedicate multiple lessons to it in the pointers module.
#include <stdio.h>int main(void) {int arr[4] = {100, 200, 300, 400};int *ptr = arr;printf("arr = %p\n", (void *)arr);printf("&arr[0] = %p\n", (void *)&arr[0]);printf("ptr = %p\n", (void *)ptr);printf("*ptr = %d\n", *ptr);printf("*(ptr+2) = %d\n", *(ptr + 2));return 0;}
Arrays are the simplest data structure in C and the foundation for strings, buffers, matrices, and every contiguous memory pattern you will encounter. Their lack of safety is the price of their speed. Next lesson: strings, which are just char arrays with a null terminator and a whole ecosystem of functions that assume that terminator exists.
Finish the lesson once you have worked through the material. This awards ★ 20 XP.