C Arrays — One <= Corrupted Memory for 6 Weeks
A loop using <= instead of < wrote past int[10], corrupting emergency flags non-deterministically for weeks.
- Arrays in C store elements contiguously in memory — access by index uses base address + (index * element_size) — O(1) access
- Key components: element type (int, char, float), name, size (fixed at compile time), index (zero-based)
- Performance: Contiguous memory enables CPU cache prefetching — sequential iteration is ~10x faster than pointer-chasing structures
- Production trap: No bounds checking — accessing scores[10] in int scores[5] compiles and silently corrupts memory or crashes later
- Biggest mistake: Using
sizeof(arr)/sizeof(arr[0])inside a function (array decays to pointer, returns pointer size 8, not array size)
Every real program deals with collections of data. A weather app tracks 30 days of temperatures. A game tracks the scores of 8 players. A bank tracks thousands of account balances. If C didn't give us a way to store multiple values under a single name, you'd have to write temperature_day1, temperature_day2, temperature_day3... all the way to temperature_day30. That is not programming — that's madness.
Arrays solve this exact problem. They let you group multiple values of the same type under one variable name and access any individual value instantly using its position number. Instead of 30 separate variables, you get one array with 30 slots. Instead of writing 30 lines to print each temperature, you write one loop. That is the power arrays hand you — and it's the foundation of almost every data structure you'll ever learn.
By the end you'll know how to declare and initialize an array in C, read from it and write to it using indexes, loop through every element with a for loop, understand how arrays sit in memory, and avoid the two bugs that trip up almost every beginner on their first week.
Declaring and Initializing Your First C Array
Declaring an array in C follows a simple pattern: you give the data type, a name, and the number of slots you need in square brackets. That's it.
The syntax looks like this: int scores[5]; — this tells C to reserve 5 consecutive slots in memory, each big enough to hold one int, and label the whole row 'scores'. Nothing is stored in them yet — they hold garbage values until you assign something.
You can also declare and fill the array at the same time using an initializer list — curly braces with values separated by commas. When you do this, C lets you leave out the size entirely and figures it out for you by counting the values you provided.
One thing to burn into memory right now: C arrays are zero-indexed. The first element lives at index 0, the second at index 1, and so on. An array of size 5 has valid indexes 0, 1, 2, 3, and 4. Index 5 does not exist. Accessing it is the single most common bug in C programming, and we'll come back to it in the gotchas section.
Let's declare an array of 5 student test scores, initialize it with real values, and print each one.
Looping Through an Array — The Real Power Unlocked
Accessing elements one by one with hardcoded indexes only works when your array is tiny and you already know every position you need. The real strength of arrays shows up the moment you combine them with a loop.
A for loop and an array are a natural pair. The loop counter acts as the index — it starts at 0, goes up by 1 each iteration, and stops before it reaches the array's length. This pattern is so standard in C that you'll write it thousands of times in your career.
The critical ingredient here is knowing the array's length. In C, arrays don't carry their own size around — unlike some other languages. The standard trick is to calculate the size at the point where the array is declared using the sizeof trick: sizeof(array) / sizeof(array[0]). This divides the total bytes the array occupies by the bytes one element occupies, giving you the element count. This only works in the same scope where the array was declared — we'll cover why in the gotchas.
Let's build a real example: store seven daily temperatures for a week, print them all, and calculate the average — something a weather app genuinely does.
How Arrays Live in Memory — Why This Changes Everything
This section is where things get genuinely interesting — and where C separates itself from beginner-friendly languages. Understanding this will make you a better C programmer immediately.
When you declare int scores[5], C goes to RAM and finds 5 consecutive (side-by-side) memory addresses and reserves them all for you. On most systems, an int is 4 bytes, so your array occupies 20 bytes in a row. Think of it like booking 5 consecutive seats on a train — not 5 seats scattered randomly, but 5 in a row.
This matters because of how fast array access is. When you write scores[3], C doesn't search for element 3. It calculates the exact memory address mathematically: start_address + (3 × size_of_one_element). That's a single calculation — instant access regardless of whether you're accessing element 0 or element 499. This is called O(1) access time and it's why arrays are so fundamental.
The array name itself — scores, without brackets — is actually the memory address of the very first element. This is the bridge between arrays and pointers, which you'll explore later. For now, just know that when you pass an array to a function, you're passing this starting address, not a copy of all the data. That's why sizeof won't give you the element count inside a function that received the array as a parameter.
| Feature / Aspect | C Array (int[]) | Pointer + malloc | Linked List (struct node*) |
|---|---|---|---|
| Size flexibility | Fixed at compile time | Dynamic (realloc) | Dynamic (add/remove nodes) |
| Memory allocation | Stack (auto) or global | Heap (manual malloc/free) | Heap (per node) |
| Access by index | O(1) — address calculation | O(1) | O(n) — traverse list |
| Insert at beginning | O(n) — shift all elements | O(n) — shift or realloc | O(1) — just rewire head |
| Insert at end | Only if preallocated space | O(1) amortised (realloc) | O(1) with tail pointer |
| Memory overhead per element | 0 bytes (values inline) | 0 bytes (values inline) | ~16 bytes (two pointers) |
| Cache locality | Excellent — contiguous | Excellent — contiguous | Poor — scattered nodes |
| Requires length tracking? | Yes — no built-in length | Yes — caller tracks size | Yes — traverse to count |
| Best for | Fixed-size, high-performance, embedded | Variable size, large datasets | Frequent insert/delete at arbitrary positions |
Key Takeaways
- C arrays are zero-indexed — the first element is at index 0 and the last valid index is always size minus one. Accessing index equal to the size is undefined behavior and one of C's most common bugs.
- There is no bounds checking — int arr[5]; arr[5] = 42; compiles and runs, corrupting adjacent memory silently. Use static analysis and runtime assertions.
- Array names decay to a pointer to the first element when passed to functions — which means sizeof inside a function gives you pointer size (8 bytes), not the array size. Always pass length as a separate parameter.
- sizeof(array) / sizeof(array[0]) is the correct way to calculate element count — but only in the same scope where the array was declared. Store the result in a named variable before your loop.
- Arrays guarantee contiguous memory — all elements sit side-by-side in RAM. This makes index access O(1) via simple arithmetic and makes arrays the fastest data structure for positional access in the entire C language.
Common Mistakes to Avoid
- Off-by-one error — accessing index equal to the array size
Symptom: For an array int data[5], writing data[5] goes one past the end. C won't stop you and won't throw an error — it'll read whatever bytes happen to be in memory after the array. Your program might crash, produce wrong output, or appear to work fine until it doesn't.
Fix: Always use index < length (strictly less than) as your loop condition, never index <= length. For loops:for (int i = 0; i < size; i++). For direct access, assert:assert(index >= 0 && index < size); - Using sizeof inside a function to get array length
Symptom: Writing `int count = sizeof(numbers) / sizeof(numbers[0])` inside a function that received the array as a parameter gives you the pointer size (8 bytes on 64-bit systems), not the array size. The result will be 2 or 1 instead of the correct count.
Fix: Always pass the array length as a separate int parameter from the caller, where sizeof does give you the full array size. Declaration:void process(int arr[], size_t len). Call:process(arr, sizeof(arr)/sizeof(arr[0])); - Forgetting to initialize array elements
Symptom: Declaring `int scores[5];` without an initializer list leaves all 5 slots holding garbage values (whatever bits happened to be in that memory). If you then loop through and print or use them, you'll get random large numbers.
Fix: Either use an initializer listint scores[5] = {0};(which zeroes all elements), or explicitly assign every element before you read from it. For large arrays, use a loop:for (int i = 0; i < N; i++) arr[i] = 0; - Assuming array is copyable by assignment
Symptom: Writing `int b[5] = a;` where `a` is another array of the same size. C does not copy arrays by assignment — this is a syntax error or decays to pointer assignment.
Fix: Usememcpy(b, a, sizeof(a));(include<string.h>). Or loop copying each element:for (int i = 0; i < 5; i++) b[i] = a[i]; - Using `arr++` on array name
Symptom: Writing `arr++` where `arr` is array name (not pointer). Compiler error: 'lvalue required as increment operand'. Array name cannot be modified.
Fix: Create a pointer:int *p = arr; then p++;. The array name is constant address; pointer variable can be incremented.
Interview Questions on This Topic
- QWhat is the difference between an array's name and a pointer in C, and in what situations does the array name not decay to a pointer?SeniorReveal
- QIf I pass an array to a function and use sizeof inside that function to calculate the number of elements, what value will I get and why? How would you solve this problem correctly?Mid-levelReveal
- QAn array int buffer[10] is declared but not initialized. A colleague says 'its elements are all zero by default.' Are they right? Under what specific condition would they be right, and why does this matter in production code?Mid-levelReveal
- QHow would you design a bounds-checked array in C for a safety-critical embedded system?SeniorReveal
Frequently Asked Questions
What is the size limit of an array in C?
There's no hard limit defined by the C language itself — the practical limit depends on where the array lives. Stack-allocated arrays (declared inside a function) are typically limited to a few megabytes before you risk a stack overflow. Heap-allocated arrays (using malloc) are limited by available RAM. For large data sets, always allocate on the heap. On embedded systems with limited RAM (e.g., 32KB), stack arrays over 1KB may cause overflow.
Can you change the size of an array in C after declaring it?
No — standard C arrays have a fixed size set at declaration time and that size cannot change. If you need a resizable collection, you use dynamic memory allocation with malloc and realloc to manually manage a heap block that can grow. For beginners, just know that once you write int scores[5], that array will always have exactly 5 slots for its entire lifetime.
Why does C array indexing start at 0 instead of 1?
Because an index in C is actually an offset from the start of the array in memory. The first element is 0 steps away from the beginning — hence index 0. This makes the address calculation straightforward: element address = base_address + (index × element_size). Starting at 1 would mean the formula needed a constant subtraction every single time, adding cost to every array access. Starting at 0 keeps the math clean and fast — a single multiply and add (base + offset).
How do I copy one array to another in C?
You cannot assign arrays directly: int b[5] = a; is invalid. Use memcpy(b, a, sizeof(a)); from <string.h>. Or loop element by element: for (int i = 0; i < 5; i++) b[i] = a[i];. For string arrays (char arrays), use strcpy(dest, src) but ensure destination has enough space.
That's C Basics. Mark it forged?
4 min read · try the examples if you haven't