A declaration such as int marks[5] looks simple, but zero-based indexes, fixed bounds, initialization, traversal and function parameters cause most early mistakes. Almost every array bug a beginner writes reduces to one of four questions: which indexes are valid, what an initializer leaves in the cells it does not name, what a function actually receives, and how a two-dimensional array sits in memory. Each has an exact answer, and the answers are worth holding as numbers rather than as rules of thumb. If you are building a wider programming foundation, Coding & DSA Courses for Placements places C alongside the next problem-solving skills.
Arrays in C: what they store and why indexes start at zero
An array stores a contiguous sequence of elements of one type. Instead of declaring five unrelated variables, write:
int marks[5] = {72, 81, 69, 90, 78};The name marks identifies five elements: marks[0] = 72, marks[1] = 81, marks[2] = 69, marks[3] = 90 and marks[4] = 78. Indexing starts at zero, so length 5 gives valid indexes 0 through 4. Zero is the starting point because an index is an offset, not a position number: marks[i] names the element exactly i elements past the base address of the block, so marks[0] is the base address itself. Counting from one would put a subtraction into every single element access.
marks[5] is outside the array, and accessing it has undefined behaviour. A loop changes one index to process all five marks; separate variables need five statements.
Declaring and initializing one-dimensional C arrays
In int marks[5];, int is the element type, marks is the name and 5 is the count. An automatic local array starts with indeterminate elements, so initialize them before reading.
These common initialization forms produce different but predictable contents:
int marks[5] = {72, 81, 69, 90, 78}; // [72, 81, 69, 90, 78]
int zeros[5] = {0}; // [0, 0, 0, 0, 0]
int partial[5] = {4, 9}; // [4, 9, 0, 0, 0]
int primes[] = {2, 3, 5, 7, 11}; // inferred length: 5Where the real array object is visible, size_t count = sizeof marks / sizeof marks[0]; gives 5. Do not use this as a general solution in a function parameter, where an array parameter is adjusted to a pointer.
![Five contiguous memory cells for int marks[5] = {72, 81, 69, 90, 78}, with indexes 0 to 4 above the cells, the values 72, 81, 69, 90 and 78 inside them, and illustrative addresses 1000, 1004, 1008, 1012 and 1016 below, on the stated assumption that int occupies 4 bytes in this example.](https://cdn.knowledgegate.ai/blog-assets/blog_asset_1784255333435_pk7z5q.jpg)
These addresses are illustrative, not portable. The example assumes a four-byte int only to show contiguous placement.
Reading, updating and traversing an array with loops
With int marks[5] = {72, 81, 69, 90, 78};, marks[2] is 69. After marks[2] = 74;, the array is [72, 81, 74, 90, 78].
A forward traversal is:
for (size_t i = 0; i < count; i++)
printf("%d ", marks[i]);It prints 72 81 74 90 78. Use i < count, not i <= count, because count is not a valid index. Reverse safely with:
for (size_t i = count; i > 0; i--)
printf("%d ", marks[i - 1]);This prints 78 90 74 81 72. Here i - 1 moves from index 4 to 0, and the loop stops before unsigned size_t goes below zero. Repeated comparisons and swaps lead naturally to Sorting Algorithms: Complexity and Comparison.
Worked C array program: sum, average and maximum
This runnable program derives the length and updates the sum and maximum in one loop:
#include <stdio.h>
int main(void) {
int scores[] = {18, 24, 15, 27, 21, 30};
size_t n = sizeof scores / sizeof scores[0];
int sum = 0;
int max = scores[0];
size_t max_index = 0;
for (size_t i = 0; i < n; i++) {
sum += scores[i];
if (scores[i] > max) {
max = scores[i];
max_index = i;
}
}
double average = (double) sum / n;
printf("sum=%d, average=%.2f, max=%d, max_index=%zu\n",
sum, average, max, max_index);
return 0;
}The loop develops as follows:
Index |
| Running sum | Current max | Max index |
|---|---|---|---|---|
0 | 18 | 18 | 18 | 0 |
1 | 24 | 42 | 24 | 1 |
2 | 15 | 57 | 24 | 1 |
3 | 27 | 84 | 27 | 3 |
4 | 21 | 105 | 27 | 3 |
5 | 30 | 135 | 30 | 5 |
![A six-step trace table for scores = {18, 24, 15, 27, 21, 30} with columns index, scores[index], running sum, current max, and max index; rows show (0,18,18,18,0), (1,24,42,24,1), (2,15,57,24,1), (3,27,84,27,3), (4,21,105,27,3), and (5,30,135,30,5), followed by average = 135 / 6 = 22.50.](https://cdn.knowledgegate.ai/blog-assets/blog_asset_1784255334245_lqzw3q.jpg)
The arithmetic is 18 + 24 + 15 + 27 + 21 + 30 = 135, then 135 / 6 = 22.5. Output is sum=135, average=22.50, max=30, max_index=5. The strict > comparison retains the first occurrence of a tied maximum.
Passing arrays to C functions without losing the length
Pass the element count with the array:
int sum_array(const int values[], size_t n) {
int total = 0;
for (size_t i = 0; i < n; i++)
total += values[i];
return total;
}const promises that the function will not modify elements through values. At the caller:
int readings[] = {12, 15, 9, 14};
size_t n = sizeof readings / sizeof readings[0];
int total = sum_array(readings, n);The total moves 0 -> 12 -> 27 -> 36 -> 50, so sum_array(readings, 4) returns 50. In a parameter, int values[] becomes int *values, so the length is not received automatically. sizeof values / sizeof values[0] would use the pointer size, not the caller's element count. Pass n explicitly.
Two-dimensional arrays in C: rows, columns and nested loops
int sales[2][3] = {{5, 7, 9}, {6, 8, 10}}; creates two rows and three columns. Here sales[0][1] is 7, while sales[1][2] is 10.
Nested loops keep each bound clear:
int grand_total = 0;
for (size_t row = 0; row < 2; row++) {
int row_sum = 0;
for (size_t col = 0; col < 3; col++)
row_sum += sales[row][col];
grand_total += row_sum;
}Row 0 totals 5 + 7 + 9 = 21, row 1 totals 6 + 8 + 10 = 24, and the grand total is 45. Row-major order is 5, 7, 9, 6, 8, 10: row 0 precedes row 1 in memory.
Common C array errors and three exercises with answer checks
Most array bugs come from these causes:
Error | Consequence | Fix |
|---|---|---|
| Reaches invalid index | Use |
Read | Uses indeterminate values | Initialize before reading |
Use | Whole-array assignment is not permitted | Copy elements in a loop or use a suitable library routine |
Use | Omits the element's address | Validate the index, then use |
int a[3] = {1, 2, 3, 4}; requires a diagnostic because four values cannot fit three elements. In contrast, int a[3] = {1, 2}; is valid and zero-fills the remainder.
Three C array exercises with answer checks
Written papers and lab vivas keep returning to the same three abilities: naming the valid index range, predicting what an initializer leaves behind, and stating what a function receives. Work all three on paper first, then check the answer line under the list.
Reverse
{3, 8, 1, 6, 4}in place.For
{10, 20, 30, 40}, compute the average and count how many elements exceed it.For
{{2, 1, 0}, {4, 3, 5}, {7, 8, 6}}, sum the main diagonal.
Answers: {4, 6, 1, 8, 3}; average 25.0 with 2 elements above it (30 and 40); and 2 + 3 + 6 = 11.
For the same ground in question form, Array Basics MCQs in C: 11 Solved Questions with Explanations works through solved items one at a time. Arrays also support bounded push, pop, enqueue and dequeue operations; Stacks and Queues: Operations and Uses shows why index checks matter.
Arrays in C: the short version and next practice
Keep five rules in view:
Declare the correct element count.
Initialize elements before reading them.
Keep every index between
0andn - 1.Derive the length only where the real array object is visible.
Pass the length explicitly into functions.
The scores total 135, average 22.50, and have maximum 30 at index 5; the 2 x 3 sales array totals 45. For the complete language sequence and more practice, continue with C Language Course: Concepts, MCQs and Coding.
As a final check, change scores[2] from 15 to 33. Predict the new total 153, average 25.50, maximum 33, and maximum index 2, then rerun the program and verify each value.
