1. Introduction
An array in C is a fixed-size, contiguous block of memory that holds multiple elements of the same data type, accessed using a single variable name and an index. Instead of declaring separate variables like score1, score2, score3, an array lets you store all related values under one identifier, e.g. score[0], score[1], score[2]. Arrays are foundational to C programming because strings, matrices, buffers, and many data structures (stacks, queues, hash tables) are built directly on top of them.
Cricket analogy: Instead of naming eleven separate variables for each batsman's score, a scorecard uses one array battingScores[0..10], just like Virat Kohli's team sheet lists all eleven players under one lineup instead of eleven separate documents.
C arrays are zero-indexed, meaning the first element is at index 0 and the last valid element of an array of size n is at index n-1. Unlike higher-level languages, C performs no automatic bounds checking, so understanding array size and indexing precisely is critical to writing correct and secure code.
Cricket analogy: A batting order of 11 players is numbered 0 to 10 in the array, so the '12th man' index doesn't exist — reaching for battingOrder[11] is like sending a substitute fielder in as if he were an official batsman, undefined and dangerous.
2. Syntax
// 1D array declaration
data_type array_name[array_size];
// 1D array with initialization
data_type array_name[array_size] = {value0, value1, ..., valueN};
// 2D array declaration
data_type array_name[rows][columns];
// 2D array with initialization
data_type array_name[rows][columns] = {
{value00, value01},
{value10, value11}
};3. Explanation
The compiler allocates array_size * sizeof(data_type) contiguous bytes when an array is declared. The array name itself represents the address of its first element in most expression contexts, which is why arrays interact closely with pointers in C.
Cricket analogy: A stand of 10,000 numbered seats reserved contiguously for a Test match ticket block is exactly array_size*sizeof(seat) — the gate number ('Section A entrance') is like the array name pointing at seat[0], the first seat of the block.
1D Arrays
A one-dimensional array stores a linear sequence of elements. You can declare it with a fixed size and optionally initialize it at the point of declaration. If you provide an initializer list without specifying a size, the compiler infers the size from the number of initializers: int nums[] = {10, 20, 30}; creates an array of size 3. If you specify a size larger than the initializer list, remaining elements are zero-initialized: int arr[5] = {1, 2}; sets arr[0]=1, arr[1]=2, and arr[2], arr[3], arr[4] to 0.
Cricket analogy: Declaring int scores[] = {45, 102, 33} lets the scoreboard infer exactly 3 innings recorded, but int scores[5] = {45, 102} leaves the remaining two 'not yet batted' slots zero, like an unfinished Test innings shown as 0 not out.
int marks[5] = {90, 85, 78, 92, 88};
for (int i = 0; i < 5; i++) {
printf("marks[%d] = %d\n", i, marks[i]);
}Multidimensional Arrays
C supports multidimensional arrays, most commonly two-dimensional arrays used to represent matrices or grids. A 2D array int matrix[3][4] is stored in row-major order in memory, meaning all elements of row 0 are stored contiguously, followed by all elements of row 1, and so on. Access uses two indices: matrix[row][col]. C also supports three-dimensional and higher arrays using the same nested-bracket syntax, though 2D arrays are by far the most common in practice.
Cricket analogy: A run-rate matrix over[6][4] tracking 6 overs * 4 bowlers is stored row-major — all of over 0's data for every bowler first, then over 1 — just like a scorecard fills one full over across all bowlers before starting the next.
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}Tip: To find the number of elements in a locally declared array (within the same scope, before it decays), use sizeof(array) / sizeof(array[0]). This trick fails once the array has decayed to a pointer, such as after being passed into a function.
Passing Arrays to Functions
When an array is passed as a function argument, C does not copy the entire array. Instead, the array 'decays' into a pointer to its first element. This is why a function parameter declared as int arr[] is treated identically to int *arr by the compiler. As a direct consequence, sizeof(arr) inside the function returns the size of a pointer (typically 4 or 8 bytes) rather than the size of the original array — a very common beginner bug. Because only a pointer is passed, the function operates on the caller's original array data; any modifications made through the pointer are visible to the caller (arrays are effectively passed 'by reference' in this sense, even though C is strictly pass-by-value for the pointer itself). For this reason, functions that process arrays almost always take an explicit size parameter alongside the array pointer.
Cricket analogy: Handing a scoring app just the 'current over' pointer instead of the entire innings record is like passing an array to a function — the function only sees where over[0] starts, not how many overs remain, so it must be told the count explicitly.
// arr decays to int*; size must be passed explicitly
void printArray(int arr[], int size) {
printf("sizeof(arr) inside function = %zu\n", sizeof(arr)); // size of a pointer, NOT the array
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main(void) {
int nums[5] = {1, 2, 3, 4, 5};
printf("sizeof(nums) in main = %zu\n", sizeof(nums)); // full array size
printArray(nums, 5);
return 0;
}Array Out-of-Bounds Access (Undefined Behavior)
C performs no runtime bounds checking on array indexing. Reading or writing outside the valid index range [0, size-1] is undefined behavior (UB): the compiler is not required to detect it, and the program may appear to 'work', crash with a segmentation fault, silently corrupt unrelated memory (including the return address on the stack, leading to security vulnerabilities like stack-buffer-overflow exploits), or produce garbage results that differ across compilers, optimization levels, and runs. This is one of the most common sources of bugs and security vulnerabilities in C programs.
Cricket analogy: Writing to score[15] on an 11-player array is like a scorer scribbling a '12th batsman's' score onto the sheet margin — it might overwrite the umpire's signature (adjacent memory) and nobody stops you until the scorecard is unusable.
Warning: int arr[5]; arr[5] = 10; compiles without error but writes past the end of the array — undefined behavior. There is no automatic bounds check in C. Always ensure index expressions satisfy 0 <= index < array_size, and prefer passing array length explicitly to functions rather than relying on guesswork or sentinel values.
int arr[5] = {1, 2, 3, 4, 5};
// arr[5] does NOT exist (valid indices are 0..4)
arr[5] = 100; // undefined behavior: out-of-bounds write
printf("%d\n", arr[10]); // undefined behavior: out-of-bounds read, garbage value4. Example
#include <stdio.h>
void printMatrix(int m[][3], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", m[i][j]);
}
printf("\n");
}
}
int sumArray(int arr[], int size) {
int total = 0;
for (int i = 0; i < size; i++) {
total += arr[i];
}
return total;
}
int main(void) {
int scores[5] = {70, 85, 90, 60, 95};
printf("Sum of scores = %d\n", sumArray(scores, 5));
int grid[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
printMatrix(grid, 2);
return 0;
}5. Output
Sum of scores = 400
1 2 3
4 5 6 6. Key Takeaways
- Arrays store fixed-size, contiguous, same-type elements and are zero-indexed (valid indices: 0 to size-1).
- Uninitialized elements in a partially initialized array are set to zero; omitting size with a full initializer list lets the compiler infer it.
- 2D arrays are stored in row-major order in memory and accessed as arr[row][col].
- When passed to a function, an array decays to a pointer to its first element — sizeof inside the function gives the pointer size, not the array size, so the length must be passed explicitly.
- Modifications made to array elements inside a function affect the caller's original array because the function receives a pointer, not a copy.
- C performs no bounds checking; accessing indices outside [0, size-1] is undefined behavior and a common source of bugs and security vulnerabilities.
- For 2D array parameters, all dimensions except the first (e.g. m[][3]) must be specified so the compiler can compute row offsets correctly.
Practice what you learned
1. What is the valid index range for an array declared as int arr[10];?
2. What does sizeof(arr) return inside a function when arr is declared as a parameter void f(int arr[])?
3. How is a 2D array int m[3][4] stored in memory in C?
4. What happens when you write to arr[10] on an array declared int arr[5];?
5. Given int arr[5] = {1, 2}; what are the values of arr[2], arr[3], and arr[4]?
Was this page helpful?
You May Also Like
Strings in C
Master C strings: null-terminated char arrays, string literals vs char arrays, key string.h functions, and buffer-overflow pitfalls.
Pointers in C
Learn C pointers with syntax, address-of and dereference operators, pointer arithmetic, and common pitfalls, with examples and output.
Functions in C
Learn C functions: declaration vs definition, parameters, pass-by-value, return statements, and prototypes with examples.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics