Arrays in C
A Complete 8-Chapter Reference
Declaration · Traversal · Pointers · Dynamic Allocation
C Programming Language Series
C Programming Language Arrays — A Complete Guide
Chapter 1
Introduction to Arrays
1.1 What is an Array?
An array in C is a contiguous collection of elements of the same data type, stored sequentially in memory
under a single variable name. Each element in the array can be accessed individually using an index or
subscript.
Instead of declaring multiple variables like score1, score2, …, score100, you declare one array: int
scores[100];. This single declaration reserves memory for 100 integers, all adjacent to each other in
memory.
Analogy: Think of an array as a row of mailboxes. The entire row has one name (e.g.,
"Mailboxes"). Each individual mailbox has a number (0, 1, 2, …) and can hold one item. All
mailboxes are the same size and are placed side by side.
1.2 Key Characteristics of Arrays in C
1. Homogeneous Elements: All elements must be of the same data type (int, char, float,
struct, etc.).
2. Contiguous Memory: Elements are stored in consecutive memory locations, allowing pointer
arithmetic to work seamlessly with arrays.
3. Fixed Size: The size must be known at compile time (unless using C99 VLAs). Once declared, the
size cannot change.
4. Zero-Based Indexing: The first element is at index 0, the last at index size - 1.
5. Random Access: Any element can be accessed directly in O(1) time using its index.
6. No Bounds Checking: C does not check if an index is within bounds. Accessing beyond the array
leads to undefined behavior.
1.3 Why Use Arrays?
• Organization: Group related data under one name.
• Efficiency: Process many elements with loops instead of repetitive code.
• Memory Locality: Contiguous storage improves cache performance.
• Foundation for Data Structures: Arrays form the basis for strings, matrices, dynamic arrays, and
more complex structures.
1.4 Visualizing Array Memory
Consider the declaration: int arr[5] = {10, 20, 30, 40, 50}; Assuming an int occupies 4
bytes and the array starts at address 1000:
Element Index Address Value
arr[0] 0 1000 10
—2—
C Programming Language Arrays — A Complete Guide
arr[1] 1 1004 20
arr[2] 2 1008 30
arr[3] 3 1012 40
arr[4] 4 1016 50
The address of arr[i] = base address + (i × size of each element).
—3—
C Programming Language Arrays — A Complete Guide
Chapter 2
Declaration, Initialization, and Access
2.1 Declaring an Array
Syntax:
data_type array_name[array_size];
• data_type: Any valid C type.
• array_name: A valid C identifier.
• array_size: A positive integer constant expression known at compile time.
Array declaration examples
int numbers[10]; // Array of 10 integers
char name[50]; // Array of 50 characters (49-char string + '\0')
float temperatures[31]; // Array of 31 floats
double matrix[100]; // Array of 100 doubles
2.2 Initializing Arrays
All initialization methods
// Method 1: Initialization at declaration
int arr[5] = {1, 2, 3, 4, 5}; // All 5 elements initialized
int arr2[5] = {1, 2}; // First two: 1,2; remaining: 0
int arr3[] = {1, 2, 3, 4, 5}; // Size deduced as 5
// Method 2: Partial initialization
int arr4[10] = {0}; // All 10 elements set to 0
int arr5[10] = {5}; // First element 5, rest 0
// Method 3: Designated initializers (C99)
int arr6[5] = {[2] = 10, [4] = 20}; // arr[2]=10, arr[4]=20, others 0
// Method 4: Character array (strings)
char str1[] = "Hello"; // Size 6 (includes '\0')
char str2[] = {'H','e','l','l','o','\0'}; // Same as above
char str3[10] = "Hello"; // First 5 chars, rest '\0'
2.3 Accessing Array Elements
Use the subscript operator [] with an index. Indices range from 0 to size - 1.
—4—
C Programming Language Arrays — A Complete Guide
int arr[5] = {10, 20, 30, 40, 50};
printf("%d\n", arr[0]); // Output: 10
printf("%d\n", arr[2]); // Output: 30
printf("%d\n", arr[4]); // Output: 50
arr[1] = 99; // Modify second element
printf("%d\n", arr[1]); // Output: 99
2.4 Common Pitfall: Out-of-Bounds Access
C does not prevent accessing indices beyond the array bounds. This leads to undefined behavior — the
program might crash, produce garbage output, or silently corrupt data.
int arr[5] = {1,2,3,4,5};
arr[5] = 100; // ERROR: index 5 is out of bounds (valid: 0-4)
arr[-1] = 10; // ERROR: negative index
2.5 The sizeof Operator with Arrays
int arr[20];
size_t total_bytes = sizeof(arr); // 80 (if int = 4 bytes)
size_t element_bytes = sizeof(arr[0]); // 4
size_t length = sizeof(arr) / sizeof(arr[0]); // 20
Important: sizeof only works on the actual array variable, not on a pointer to it. When an array is passed to a
function it 'decays' to a pointer — sizeof will then return the pointer size (usually 8 bytes), not the array size.
—5—
C Programming Language Arrays — A Complete Guide
Chapter 3
Traversing and Processing Arrays
3.1 Basic Traversal with a for Loop
Read, store, and print an array
#include <stdio.h>
#define SIZE 5
int main() {
int arr[SIZE];
// Input
printf("Enter %d integers: ", SIZE);
for (int i = 0; i < SIZE; i++) {
scanf("%d", &arr[i]);
}
// Output
printf("You entered: ");
for (int i = 0; i < SIZE; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
3.2 Common Array Operations
Common array operations
—6—
C Programming Language Arrays — A Complete Guide
// Sum of elements
int sum = 0;
for (int i = 0; i < SIZE; i++) sum += arr[i];
// Find maximum element
int max = arr[0];
for (int i = 1; i < SIZE; i++) {
if (arr[i] > max) max = arr[i];
}
// Reverse an array in-place
for (int i = 0; i < SIZE / 2; i++) {
int temp = arr[i];
arr[i] = arr[SIZE - 1 - i];
arr[SIZE - 1 - i] = temp;
}
// Linear search
int search(int arr[], int size, int target) {
for (int i = 0; i < size; i++) {
if (arr[i] == target) return i; // Found
}
return -1; // Not found
}
3.3 Using while and do-while Loops
int i = 0;
// while loop traversal
while (i < SIZE) {
printf("%d ", arr[i]);
i++;
}
// do-while (at least one iteration)
i = 0;
do {
printf("%d ", arr[i]);
i++;
} while (i < SIZE);
3.4 Multi-pass Processing — Bubble Sort
Bubble sort — multi-pass algorithm
—7—
C Programming Language Arrays — A Complete Guide
void bubbleSort(int arr[], int size) {
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
—8—
C Programming Language Arrays — A Complete Guide
Chapter 4
Multi-Dimensional Arrays
4.1 Declaring and Initializing 2D Arrays
A two-dimensional array is essentially an array of arrays, often visualised as a matrix with rows and
columns.
Declaration syntax: data_type array_name[rows][columns];
2D array initialization methods
// Method 1: Nested braces (recommended)
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// Method 2: Flat list (row-major order)
int matrix2[2][3] = {1, 2, 3, 4, 5, 6};
// Method 3: Partial initialization (remaining set to 0)
int matrix3[2][3] = {{1, 2}, {4}}; // Row0: 1,2,0; Row1: 4,0,0
// Method 4: Omit first dimension
int matrix4[][3] = {{1,2,3},{4,5,6}}; // Compiler infers rows = 2
4.2 Accessing 2D Array Elements
int matrix[2][3] = {{1,2,3},{4,5,6}};
printf("%d\n", matrix[0][1]); // Output: 2
printf("%d\n", matrix[1][2]); // Output: 6
matrix[1][1] = 99; // Change element at row1,col1
4.3 Memory Layout — Row-Major Order
C stores 2D arrays in row-major order: all elements of row 0 come first, then row 1, etc. For int
arr[2][3] = {{1,2,3},{4,5,6}};:
Address Value Element
1000 1 arr[0][0]
1004 2 arr[0][1]
1008 3 arr[0][2]
—9—
C Programming Language Arrays — A Complete Guide
1012 4 arr[1][0]
1016 5 arr[1][1]
1020 6 arr[1][2]
4.4 Traversing 2D Arrays
// Nested loops
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// Row-major traversal with a single pointer (advanced)
int *p = &matrix[0][0];
for (int i = 0; i < rows * cols; i++) {
printf("%d ", p[i]);
}
4.5 3D and Higher-Dimensional Arrays
int tensor[3][4][5]; // 3 layers, 4 rows, 5 cols (60 elements)
int tensor2[2][2][2] = {
{{1,2},{3,4}},
{{5,6},{7,8}}
};
printf("%d\n", tensor2[1][0][1]); // Output: 6
— 10 —
C Programming Language Arrays — A Complete Guide
Chapter 5
Arrays and Functions
5.1 Passing Arrays to Functions
When you pass an array to a function, what is actually passed is a pointer to the first element. The array
"decays" to a pointer. The function receives the address, not a copy of the entire array.
• The function can modify the original array (no copy is made).
• The size information is lost; you must pass the size separately.
All three prototypes below are equivalent: void process(int arr[], int size); / void process(int *arr, int size); / void
process(int arr[10], int size); — the 10 is ignored by the compiler.
Array passed to function — original is modified
void doubleElements(int arr[], int size) {
for (int i = 0; i < size; i++) {
arr[i] *= 2; // Modifies original array
}
}
int main() {
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
doubleElements(numbers, size);
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]); // Output: 2 4 6 8 10
}
return 0;
}
5.2 Passing Multi-Dimensional Arrays
For 2D arrays, you must specify the number of columns. The first dimension can be omitted.
— 11 —
C Programming Language Arrays — A Complete Guide
// C99: variable-length array in parameter
void printMatrix(int rows, int cols, int matrix[][cols]) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
}
// Fixed column count
void printMatrixFixed(int matrix[][4], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
}
5.3 Returning Arrays from Functions
You cannot return a local array from a function — it goes out of scope. Instead, use one of these
approaches:
1. Modify an array passed as a parameter (most common).
2. Return a pointer to dynamically allocated memory (using malloc).
3. Wrap the array in a struct (copyable, but inefficient for large arrays).
Return dynamically allocated array
int* createArray(int size) {
int *arr = (int*)malloc(size * sizeof(int));
if (arr != NULL) {
for (int i = 0; i < size; i++) arr[i] = 0;
}
return arr; // Caller must free()
}
5.4 Array of Function Pointers (Advanced)
Array of function pointers — dispatch table
— 12 —
C Programming Language Arrays — A Complete Guide
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int main() {
int (*operations[3])(int, int) = {add, sub, mul};
for (int i = 0; i < 3; i++) {
printf("%d\n", operations[i](10, 5)); // 15, 5, 50
}
return 0;
}
— 13 —
C Programming Language Arrays — A Complete Guide
Chapter 6
Arrays and Pointers Relationship
6.1 Array Name as a Constant Pointer
The name of an array is a constant pointer to its first element. arr is equivalent to &arr[0].
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr; // ptr points to arr[0]
printf("%d\n", *arr); // Output: 10
printf("%d\n", *(arr+2)); // Output: 30
6.2 Pointer Arithmetic with Arrays
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr;
printf("%d\n", *ptr); // 10
ptr++; // Move to next element
printf("%d\n", *ptr); // 20
ptr += 2; // Move two more elements
printf("%d\n", *ptr); // 40
6.3 Subscript vs. Pointer Notation
The expression arr[i] is exactly equivalent to *(arr + i). The compiler converts subscript notation
to pointer arithmetic internally.
int arr[5] = {1,2,3,4,5};
int i = 2;
printf("%d\n", arr[i]); // 3
printf("%d\n", *(arr + i)); // 3
printf("%d\n", i[arr]); // 3 — valid but never use this!
6.4 Differences Between Arrays and Pointers
Aspect Array Pointer
Storage Allocates memory for elements Stores an address only
sizeof Returns total bytes of all elements Returns address size (usually 8 bytes)
Assignment Cannot reassign the array name Can point to different addresses
Function decay Decays to pointer when passed Remains a pointer
— 14 —
C Programming Language Arrays — A Complete Guide
int arr[5] = {1,2,3,4,5};
int *ptr = arr;
printf("%zu\n", sizeof(arr)); // 20 (if int = 4 bytes)
printf("%zu\n", sizeof(ptr)); // 8 (on 64-bit system)
// arr = ptr; // ERROR: cannot assign to array name
ptr = arr + 2; // OK: pointer can be reassigned
6.5 Array of Pointers
String array — array of char pointers
const char *fruits[3] = {"Apple", "Banana", "Cherry"};
for (int i = 0; i < 3; i++) {
printf("%s\n", fruits[i]); // Each fruits[i] is a char*
}
— 15 —
C Programming Language Arrays — A Complete Guide
Chapter 7
VLAs, Dynamic Arrays & Flexible Members
7.1 Variable-Length Arrays (C99)
C99 introduced Variable-Length Arrays (VLAs), where the array size can be determined at runtime using
a variable.
int n;
printf("Enter size: ");
scanf("%d", &n);
int arr[n]; // VLA: size determined at runtime
for (int i = 0; i < n; i++) arr[i] = i;
Important VLA limitations:
• Not supported in C++ or pre-C99 standards.
• Cannot have global scope or static/extern storage class.
• Cannot be partially initialized (e.g., int arr[n] = {0} is not allowed).
• Large VLAs on the stack can cause stack overflow.
7.2 Dynamic Arrays (Heap Allocation)
For full runtime flexibility without VLA limitations, use dynamic memory allocation with pointers.
Dynamic array with realloc resizing
int n;
scanf("%d", &n);
int *arr = (int*)malloc(n * sizeof(int));
if (arr == NULL) { printf("Allocation failed\n"); return 1; }
for (int i = 0; i < n; i++) arr[i] = i;
// Resize using realloc
int new_n = n * 2;
int *temp = (int*)realloc(arr, new_n * sizeof(int));
if (temp != NULL) {
arr = temp;
for (int i = n; i < new_n; i++) arr[i] = 0;
}
free(arr);
arr = NULL;
— 16 —
C Programming Language Arrays — A Complete Guide
7.3 Dynamic 2D Arrays
Jagged 2D array
// Method 1: Array of pointers (jagged array)
int rows = 3, cols = 4;
int **matrix = (int**)malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++)
matrix[i] = (int*)malloc(cols * sizeof(int));
// Access: matrix[row][col]
// Freeing:
for (int i = 0; i < rows; i++) free(matrix[i]);
free(matrix);
Flat 2D array — better cache performance
// Method 2: Single contiguous block (better cache locality)
int *matrix = (int*)malloc(rows * cols * sizeof(int));
// Access: matrix[row * cols + col]
matrix[1 * cols + 2] = 42; // Row 1, Col 2
free(matrix);
7.4 Flexible Array Members (C99)
A structure can have an array of unspecified size as its last member, useful for creating variable-sized
objects allocated in one block.
Flexible array member in a struct
typedef struct {
int length;
int data[]; // Flexible array member — must be last
} FlexArray;
// Allocation
FlexArray *arr = (FlexArray*)malloc(sizeof(FlexArray) + 10 * sizeof(int));
arr->length = 10;
for (int i = 0; i < arr->length; i++) arr->data[i] = i;
free(arr);
— 17 —
C Programming Language Arrays — A Complete Guide
Chapter 8
Pitfalls, Best Practices & Advanced Examples
8.1 Common Pitfalls with Arrays
Pitfall Example Consequence
Off-by-one errors for(i=0; i<=SIZE; i++) Access beyond bounds
Missing null terminator char str[3] = "abc"; No space for '\0'
sizeof on pointer param void f(int a[]) { sizeof(a); } Returns pointer size, not array size
Returning local array int* f() { int a[5]; return a; } Dangling pointer — undefined behavior
Uninitialized elements int arr[5]; printf("%d", arr[0]); Garbage value printed
Unsafe strcpy strcpy(dest, src) where src is too long Buffer overflow
8.2 Best Practices Summary
1. Always know your array bounds and enforce them in every loop.
2. Use const for read-only parameters: void print(const int arr[], int size).
3. Pass size explicitly when passing arrays to functions.
4. Initialize arrays to avoid garbage values: int arr[100] = {0};
5. Use sizeof(arr)/sizeof(arr[0]) for compile-time size calculation.
6. For strings, leave room for '\0' and prefer strncpy, snprintf.
7. Free dynamically allocated memory and set pointers to NULL.
8. Use assert to validate indices in debug builds.
9. Prefer single contiguous blocks for 2D arrays when cache performance matters.
10. Consider flexible array members for variable-sized structures.
8.3 Advanced Example — Generic Linear Search
Generic linear search — void* and function pointers
— 18 —
C Programming Language Arrays — A Complete Guide
#include <stdio.h>
#include <stdlib.h>
// Generic linear search using void* and function pointer
void* linearSearch(void *base, size_t nmemb, size_t size,
const void *key,
int (*compare)(const void*, const void*)) {
char *ptr = (char*)base;
for (size_t i = 0; i < nmemb; i++) {
if (compare(ptr + i * size, key) == 0) {
return ptr + i * size;
}
}
return NULL;
}
int compareInt(const void *a, const void *b) {
return *(int*)a - *(int*)b;
}
int main() {
int arr[] = {10, 20, 30, 40, 50};
int key = 30;
int *result = (int*)linearSearch(arr, 5, sizeof(int), &key, compareInt);
if (result) {
printf("Found: %d at index %ld\n", *result, result - arr);
}
return 0;
}
8.4 Conclusion
Arrays are fundamental to C programming, providing a powerful way to manage collections of data.
Understanding the relationship between arrays and pointers, memory layout, and the limitations of static
arrays is essential for every C programmer.
While static arrays are simple and efficient for fixed-size data, dynamic allocation offers flexibility at the
cost of manual memory management. Mastery of arrays enables you to implement complex data
structures, process large datasets efficiently, and write systems-level code.
Always practice safe array handling: respect bounds, initialize properly, and pass sizes explicitly to
functions.
End of Document — Arrays in C: A Complete Guide
— 19 —