C Arrays: Store Multiple Values Like a Pro in 2026
Table of Contents
What Are Arrays in C?
An array is a collection of elements of the same data type stored in contiguous memory locations. Instead of declaring ten separate integer variables, you declare one array that holds ten integers. This makes your code cleaner, more manageable, and far more powerful when working with collections of data.
If you have been following our C Language Roadmap, you have already learned about C Variables & Data Types and C for Loop. Arrays combine both concepts — you store data in a structured way and process it with loops. Every serious C program uses arrays, from operating system kernels to embedded firmware.
Think of an array like a row of numbered mailboxes. Each mailbox holds one item (a value), and you access it by its number (the index). The first mailbox is always number 0, not 1 — this is zero-based indexing, and it catches every beginner at least once.
Declaring and Initializing Arrays
There are several ways to create arrays in C. The syntax always follows the pattern: type name[size];
Declaration Without Initialization
int scores[5]; // 5 integers, values are garbage (uninitialized)
double prices[100]; // 100 doubles
char name[50]; // 50 characters (for strings later)
When you declare an array without initializing it, the values inside are whatever happened to be in that memory — random garbage. Never assume uninitialized arrays contain zeros.
Declaration With Initialization
int scores[5] = {90, 85, 78, 92, 88}; // all 5 values set
int partial[5] = {10, 20}; // first 2 set, rest are 0
int zeros[5] = {0}; // all 5 are 0
int auto_sized[] = {1, 2, 3, 4}; // compiler counts: size = 4
The {0} trick is the most common way to zero-initialize an array in C. The compiler sets the first element to 0 and automatically zero-fills the rest. This works because partially initialized arrays have their remaining elements set to zero by the C standard.
Designated Initializers (C99+)
int arr[10] = {[0] = 5, [5] = 100, [9] = 42};
// arr = {5, 0, 0, 0, 0, 100, 0, 0, 0, 42}
Designated initializers let you set specific indices without filling everything sequentially. Unspecified elements default to zero. This feature requires C99 or later — which every modern compiler supports.
Accessing Array Elements
You access elements using the subscript operator [] with a zero-based index:
int scores[5] = {90, 85, 78, 92, 88};
printf("First score: %d\n", scores[0]); // 90
printf("Third score: %d\n", scores[2]); // 78
printf("Last score: %d\n", scores[4]); // 88
scores[1] = 95; // modify the second element
printf("Updated second: %d\n", scores[1]); // 95
The index must be between 0 and size-1. For an array of size 5, valid indices are 0, 1, 2, 3, and 4. Accessing scores[5] is out of bounds — C will not stop you, but the behavior is undefined. Your program might crash, produce wrong results, or appear to work fine until the worst possible moment.
Looping Through Arrays
The most common operation with arrays is iterating through every element using a C for Loop:
#include <stdio.h>
int main(void) {
int temps[7] = {72, 68, 75, 80, 77, 73, 69};
int sum = 0;
// Print all temperatures
for (int i = 0; i < 7; i++) {
printf("Day %d: %d°F\n", i + 1, temps[i]);
sum += temps[i];
}
printf("Average: %.1f°F\n", (double)sum / 7);
return 0;
}
Notice the loop condition is i < 7, not i <= 7. This is the standard pattern — loop from 0 to less than the array size. Using <= would access one element past the end of the array.
Reverse Traversal
for (int i = 6; i >= 0; i--) {
printf("%d ", temps[i]);
}
Using while Loops
You can also traverse arrays with C while Loops, though for loops are the conventional choice:
int i = 0;
while (i < 7) {
printf("%d ", temps[i]);
i++;
}
Finding Array Size
C does not store the length of an array anywhere. You calculate it using the sizeof operator:
int arr[] = {10, 20, 30, 40, 50};
int length = sizeof(arr) / sizeof(arr[0]);
printf("Array has %d elements\n", length); // 5
sizeof(arr) gives the total bytes occupied by the array. sizeof(arr[0]) gives the size of one element. Dividing them produces the element count. This only works in the same scope where the array was declared — once you pass an array to a function, sizeof returns the pointer size instead.
The SIZE Macro Pattern
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
int data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for (int i = 0; i < ARRAY_SIZE(data); i++) {
printf("%d ", data[i]);
}
This macro is so common that many production codebases define it. It saves you from manually tracking array sizes and makes code less error-prone when you add or remove elements. As covered in our C Header Files lesson, you would typically place this macro in a utility header file.
Common Array Patterns
Finding the Maximum and Minimum
int find_max(int arr[], int size) {
int max = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
int find_min(int arr[], int size) {
int min = arr[0];
for (int i = 1; i < size; i++) {
if (arr[i] < min) {
min = arr[i];
}
}
return min;
}
Linear Search
int linear_search(int arr[], int size, int target) {
for (int i = 0; i < size; i++) {
if (arr[i] == target) {
return i; // found at index i
}
}
return -1; // not found
}
Reversing an Array In-Place
void reverse(int arr[], int size) {
for (int i = 0; i < size / 2; i++) {
int temp = arr[i];
arr[i] = arr[size - 1 - i];
arr[size - 1 - i] = temp;
}
}
Counting Occurrences
int count(int arr[], int size, int target) {
int c = 0;
for (int i = 0; i < size; i++) {
if (arr[i] == target) c++;
}
return c;
}
Bubble Sort
void bubble_sort(int arr[], int size) {
for (int i = 0; i < size - 1; i++) {
int swapped = 0;
for (int j = 0; j < size - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = 1;
}
}
if (!swapped) break; // already sorted
}
}
Bubble sort is O(n²) and not used in production, but it is the easiest sorting algorithm to understand. The early-exit optimization (swapped flag) makes it O(n) for already-sorted data.
Bounds Checking and Safety
C performs zero bounds checking on arrays. This is by design — checking every access would slow down the language. But it means you are responsible for staying within bounds:
int arr[5] = {1, 2, 3, 4, 5};
// DANGEROUS — undefined behavior:
arr[5] = 99; // one past the end
arr[-1] = 0; // negative index
arr[1000] = 7; // wildly out of bounds
Out-of-bounds access is the source of countless bugs and security vulnerabilities. Buffer overflow attacks — one of the most common attack vectors in cybersecurity — exploit exactly this weakness. Always validate indices before using them, especially when they come from user input.
// Safe pattern
void set_element(int arr[], int size, int index, int value) {
if (index >= 0 && index < size) {
arr[index] = value;
} else {
fprintf(stderr, "Index %d out of bounds (size %d)\n", index, size);
}
}
Passing Arrays to Functions
When you pass an array to a C Functions, C does not copy the entire array. Instead, it passes a pointer to the first element. This means the function can modify the original array:
#include <stdio.h>
void double_elements(int arr[], int size) {
for (int i = 0; i < size; i++) {
arr[i] *= 2; // modifies original!
}
}
int main(void) {
int nums[] = {1, 2, 3, 4, 5};
int len = sizeof(nums) / sizeof(nums[0]);
double_elements(nums, len);
for (int i = 0; i < len; i++) {
printf("%d ", nums[i]); // 2 4 6 8 10
}
printf("\n");
return 0;
}
This is why you must always pass the array size as a separate parameter — inside the function, sizeof(arr) gives you the pointer size (typically 8 bytes on 64-bit systems), not the array size. As we discussed in C Parameters & Return, C uses pass by value, but for arrays, the “value” being passed is a pointer.
Using const for Read-Only Access
void print_array(const int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// arr[0] = 99; // ERROR — arr is const
}
Marking the parameter const tells the compiler and other programmers that this function will not modify the array. The compiler enforces it — attempting to write through a const pointer produces a compilation error.
Variable-Length Arrays (VLAs)
C99 introduced variable-length arrays, where the size can be determined at runtime:
#include <stdio.h>
int main(void) {
int n;
printf("How many scores? ");
scanf("%d", &n);
int scores[n]; // VLA — size determined at runtime
for (int i = 0; i < n; i++) {
printf("Score %d: ", i + 1);
scanf("%d", &scores[i]);
}
int sum = 0;
for (int i = 0; i < n; i++) {
sum += scores[i];
}
printf("Average: %.1f\n", (double)sum / n);
return 0;
}
VLAs are allocated on the stack. If n is too large, you will get a stack overflow with no error message — the program simply crashes. For this reason, many production codebases avoid VLAs entirely and use malloc() instead (covered in a later lesson). Note that VLAs were made optional in C11, meaning not all compilers support them.
Common Mistakes with C Arrays
Mistake 1: Forgetting Zero-Based Indexing
int arr[5] = {10, 20, 30, 40, 50};
// arr[5] does NOT exist — valid indices are 0 to 4
printf("%d\n", arr[5]); // undefined behavior!
Mistake 2: Using = to Copy Arrays
int a[5] = {1, 2, 3, 4, 5};
int b[5];
// b = a; // ERROR — cannot assign arrays
// Correct way:
for (int i = 0; i < 5; i++) {
b[i] = a[i];
}
// Or use memcpy:
#include <string.h>
memcpy(b, a, sizeof(a));
Mistake 3: Comparing Arrays with ==
int a[3] = {1, 2, 3};
int b[3] = {1, 2, 3};
// if (a == b) // compares addresses, NOT contents!
// Correct way:
int equal = 1;
for (int i = 0; i < 3; i++) {
if (a[i] != b[i]) { equal = 0; break; }
}
// Or: memcmp(a, b, sizeof(a)) == 0
Mistake 4: sizeof After Passing to a Function
void wrong(int arr[]) {
// sizeof(arr) is 8 (pointer size), NOT the array size!
int len = sizeof(arr) / sizeof(arr[0]); // WRONG
}
Practice Exercises
Try these exercises to solidify your understanding:
- Sum and Average: Write a program that reads 10 integers into an array, then prints their sum and average.
- Second Largest: Find the second largest element in an array without sorting it.
- Remove Duplicates: Given a sorted array, remove duplicate elements in-place and return the new length.
- Rotate Array: Rotate an array of n elements to the right by k positions.
- Merge Sorted: Given two sorted arrays, merge them into a single sorted array.
Summary
C arrays are the foundation of nearly every data structure you will encounter. They give you direct, contiguous memory access with zero overhead — but that power comes with the responsibility of managing bounds and sizes yourself. You now know how to declare, initialize, traverse, and manipulate arrays. You understand the sizeof trick for calculating length, the const pattern for safety, and the common pitfalls that trip up even experienced developers.
In the next lesson, we will extend arrays into two dimensions with C Language Roadmap multidimensional arrays — essential for matrices, grids, game boards, and image processing.