Arrays
An array in C is a collection of elements of the same type, stored in contiguous memory locations. Arrays allow you to group related data together and access each element using an index.
Basic array declaration and usageβ
To declare an array, specify the data type of its elements followed by the array name and its size in square brackets:
int numbers[5]; // Declares an array of 5 integers
This creates space for 5 integers in memory, indexed from 0
to 4
. You can access and modify individual elements using their index:
numbers[0] = 10; // Assign 10 to the first element
numbers[1] = 20; // Assign 20 to the second element
int x = numbers[2]; // Retrieve value of third element
Array initializationβ
There are several ways to initialize arrays in C:
// Method 1: Initialize with values at declaration
int values[5] = {10, 20, 30, 40, 50};
// Method 2: Partial initialization (remaining elements are set to 0)
int partial[5] = {10, 20}; // Equivalent to {10, 20, 0, 0, 0}
// Method 3: Initialize all elements to zero
int zeros[100] = {0};
// Method 4: Let the compiler determine the size based on initializers
int auto_sized[] = {1, 2, 3, 4}; // Creates an array of size 4
// β Error - more initializers than specified size
double wrong[2] = {5.4, 3.87, 3.55}; // Compilation error
Using {0}
initializes all elements to zero. This is a common technique for clearing arrays:
int arr[10] = {0}; // All 10 elements set to zero
int matrix[50][50] = {0}; // All 2500 elements set to zero
This works because C automatically sets any unspecified initializers to zero when you provide at least one initializer value.
Important rules for array initializationβ
When initializing arrays with the syntax type name[size] = { values... }
, keep these rules in mind:
- If
size
is specified and there are fewer initializers than the size, remaining elements are automatically initialized to zero - If
size
is specified, you cannot provide more initializers than the declared size - If
size
is omitted ([]
), the compiler will determine the size based on the number of initializers provided - Without initialization, local arrays (automatic storage duration) will contain garbage values
Array input and outputβ
Reading array elements from inputβ
You can read array elements using scanf
:
int arr[5];
printf("Enter 5 integers: ");
for (int i = 0; i < 5; i++) {
scanf("%d", &arr[i]); // Note the & operator
}
Note that when you call scanf
, it requires the address of the variable where the input will be stored, not the value itself.
By using &arr[i]
, you pass the pointer to the iβth element of arr
. This ensures that each entered integer is written directly into the correct position in the array.
If you frequently need to print arrays, you can define a macro to simplify the process and avoid writing repetitive loops each time as follows:
#define PRINT_ARRAY(arr, size) \
for (int i = 0; i < (size); i++) \
printf("%d ", (arr)[i]);
// Usage
PRINT_ARRAY(a, SIZE);
Also, wrapping the macro body in a do { β¦ } while (0)
makes it behave like a single statement, preventing issues when used inside conditionals:
#define PRINT_ARRAY(arr, size) \
do { \
for (int i = 0; i < (size); i++) \
printf("%d ", (arr)[i]); \
} while (0)
// Safe in an if/else without braces
if (condition)
PRINT_ARRAY(a, SIZE);
else
printf("No array to print\n");
Reading array input with basic error handlingβ
When reading input into an array, it's good practice to check if the input is valid. Here is a way to do that:
#define SIZE 5
// and in main()...
int a[SIZE];
for (int i = 0; i < SIZE; i++) {
printf("Enter value %d: ", i);
// scanf needs the address of the variable to store the input
// The condition checks if scanf successfully read an integer
if (scanf("%d", &a[i]) != 1) {
printf("Invalid input\n");
break;
}
}
If the user enters something that is not an integer, the program prints an error message and stops reading further input. This helps prevent undefined behavior from invalid or unexpected input.
Notice that the scanf
function is called directly inside the if
condition:
if (scanf("%d", &a[i]) != 1) {
printf("Invalid input\n");
break;
}
This means scanf
is executed once, and its return value is immediately checked. There is no need to call scanf
again in an else
block, because the input has already been read (or attempted to be read) at this point.
Multidimensional arraysβ
C supports multidimensional arrays, which are essentially arrays of arrays:
// 2D array (3 rows, 4 columns)
int matrix[3][4] = {
{11, 12, 13, 14}, // Row 0
{21, 22, 23, 24}, // Row 1
{31, 32, 33, 34} // Row 2
};
// Access element at row 1, column 2
int value = matrix[1][2]; // Gets 23
Col 0 | Col 1 | Col 2 | Col 3 | |
---|---|---|---|---|
Row 0 | 11 | 12 | 13 | 14 |
Row 1 | 21 | 22 | 23 | 24 |
Row 2 | 31 | 32 | 33 | 34 |
You can create arrays with any number of dimensions:
// 3D array (2 matrices of 3Γ3)
int cube[2][3][3];
// Accessing elements
cube[0][1][2] = 99; // First matrix, second row, third column
To print them, use nested loops. For example, printing a 2D array is done like this:
#include <stdio.h>
int main() {
int matrix[3][4] = {
{11, 12, 13, 14},
{21, 22, 23, 24},
{31, 32, 33, 34}
};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
return 0;
}
11 12 13 14
21 22 23 24
31 32 33 34
You can extend this approach for higher-dimensional arrays by adding more nested loops.
Arrays and memoryβ
Arrays in C have a fixed size that must be known at compile time (for static arrays). The array name itself represents the memory address of the first element.
int nums[5] = {10, 20, 30, 40, 50};
printf("Address of first element: %p\n", nums); // Same as &nums[0]
The relationship between arrays and pointers is why array names can often be used interchangeably with pointers to their first element:
int *ptr = nums; // ptr points to nums[0]
printf("First element: %d\n", *ptr); // Prints 10
printf("Second element: %d\n", *(ptr + 1)); // Prints 20
This memory diagram might be helpful:
a
) acts as a pointer to the first element (p
is the same as a
here, they both point to the same location). Pointer arithmetic (such as p + 1
) moves to subsequent elements, just like incrementing the array index.Remember that each data type has a specific size; for example, an int
typically occupies 32 bits (4 bytes) in most C implementations.
The relationship between arrays and pointersβ
In C, arrays have a special relationship with pointers. When you use an array name in most expressions, it's automatically converted to a pointer to its first element.
This is why you can assign an array to a pointer:
int numbers[5] = {10, 20, 30, 40, 50};
int *p = numbers; // p now points to numbers[0]
However, there are two important cases where arrays are not converted to pointers:
When using the
sizeof
operator:int arr[10];
size_t arr_size = sizeof(arr); // Returns 10 * sizeof(int), typically 40 bytes
size_t ptr_size = sizeof(int*); // Returns pointer size, typically 4 or 8 bytesWhen using the address-of operator
&
:int arr[5];
int *p1 = arr; // OK: arr converted to &arr[0]
int (*p2)[5] = &arr; // Different type: pointer to an array of 5 ints
Memory layout and accessβ
Arrays in C are stored in contiguous memory locations. For example, if each integer takes 4 bytes, a 5-element integer array would look like this in memory:
Element: [0] [1] [2] [3] [4]
Address: 1000 1004 1008 1012 1016
ββ 4 bytes ββ
This contiguous layout enables efficient access using pointer arithmetic. The compiler uses the formula:
Passing arrays to functionsβ
When you pass an array to a function, you're actually passing a pointer to its first element. The array size information is not passed automatically:
// These function declarations are equivalent:
void process_array(int arr[], size_t size);
void process_array(int *arr, size_t size);
This means:
- The function receives only the address of the first element, not a copy of the entire array.
- Changes made to array elements within the function affect the original array.
- The function cannot determine the array size by itself - you must pass it as a separate parameter.
void modify_array(int arr[], size_t size) {
for (size_t i = 0; i < size; i++) {
arr[i] *= 2; // Doubles each element
}
}
int main() {
int numbers[5] = {1, 2, 3, 4, 5};
modify_array(numbers, 5);
// numbers is now {2, 4, 6, 8, 10}
}
When passing arrays to functions, a common mistake is trying to determine the array size inside the function like this:
void func(int a[]) {
size_t n = sizeof a / sizeof *a;
// ... use n as the array size ...
}
This won't work! Inside the function:
a
is of typeint*
(pointer to int), sosizeof a
equals 4 (on 32-bit systems)*a
(a[0]
) is the first element of typeint
, sosizeof *a
equals 4- The result
n
will always be 1, regardless of the actual array size!
To correctly work with arrays in functions, you must pass the size as a separate parameter:
void func(int a[], size_t n) {
// ...
}
Or equivalently:
void func(int *a, size_t n) {
// ...
}
Using const with array parametersβ
If your function shouldn't modify the array, use the const
keyword:
void print_array(const int arr[], size_t size) {
for (size_t i = 0; i < size; i++) {
printf("%d ", arr[i]);
// arr[i] = 0; // Error: cannot modify a const array
}
}
This prevents accidental modification of the array elements and makes your code's intent clearer.
Dynamic arraysβ
Static arrays have a fixed size determined at compile time. For variable-sized arrays allocated at runtime, C provides dynamic memory allocation:
#include <stdio.h>
#include <stdlib.h>
int main() {
// Allocate an array of n integers
int size;
printf("Enter array size: ");
scanf("%d", &size);
int *dynamic_array = (int *)malloc(size * sizeof(int));
// Check if allocation was successful
if (dynamic_array == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// Use the array
for (int i = 0; i < size; i++) {
dynamic_array[i] = i * 10;
}
// Print the array
printf("Array contents: ");
for (int i = 0; i < size; i++) {
printf("%d ", dynamic_array[i]);
}
printf("\n");
// Important: Free the memory when done
free(dynamic_array);
return 0;
}
Enter array size: 5
Array contents: 0 10 20 30 40
C99 introduced Variable-Length Arrays, which allow array sizes to be determined at runtime:
void process_data(int size) {
int variable_array[size]; // Size determined at runtime
// Use the array...
}
However, VLAs have limitations:
- They cannot be initialized at declaration
- They exist only until the end of their scope
- They can cause stack overflow with large sizes
- They're optional in C11 and later standards
For larger or longer-lived dynamic arrays, malloc()
is generally preferred.
Common pitfalls with arraysβ
- Bounds checking: C does not automatically check if an array index is valid. Accessing an element beyond the array bounds leads to undefined behavior.
int arr[3] = {1, 2, 3};
printf("%d\n", arr[5]); // Out-of-bounds access: undefined behavior
- Array size: Unlike some languages, C arrays do not track their own size. You must manage this information separately.
int arr[10];
// sizeof(arr) gives the total size in bytes
int size = sizeof(arr) / sizeof(arr[0]); // Calculates number of elements
printf("Array size: %d\n", size);
The strlen()
function is commonly used to determine the length of a string (a character array terminated by a null character '\0'
).
It cannot be used to find the size of a general array of numbers or other types.
For example:
char str[] = "hello";
printf("%zu\n", strlen(str)); // Prints 5 (number of characters before '\0')
But for non-string arrays (like int arr[10]
), strlen()
does not apply. Always use sizeof(arr) / sizeof(arr[0])
for such cases.
- Uninitialized arrays: elements of an array declared without initialization contain garbage values unless explicitly initialized.
int arr[5]; // Uninitialized array
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]); // Unpredictable output (garbage values)
}
- Array assignment: you cannot assign one array to another with the
=
operator. Instead, you must copy elements individually or use functions likememcpy()
.
int a[5] = {1, 2, 3, 4, 5};
int b[5];
b = a; // β Wrong! This does not work in C
// Correct way to copy:
for (int i = 0; i < 5; i++) {
b[i] = a[i];
}
π Exercise: Analyze weekly temperaturesβ
Let's try coding a C program that:
- Stores the temperatures for 7 days in an array.
- Calculates the average temperature.
- Prints the days (1-indexed) where the temperature was above average.
- Displays the average temperature and the number of days above average.
Solve this on your own before checking the solution!
Show solution
#include <stdio.h>
int main() {
// Declare and initialize an array
int temperatures[7] = {24, 22, 25, 23, 26, 25, 24};
// Calculate average temperature
int sum = 0;
for (int i = 0; i < 7; i++) {
sum += temperatures[i];
}
float average = (float)sum / 7;
// Find days above average
int above_average_count = 0;
printf("Days with above average temperature: ");
for (int i = 0; i < 7; i++) {
if (temperatures[i] > average) {
printf("%d ", i + 1); // Day number (1-indexed)
above_average_count++;
}
}
printf("\n");
printf("Average temperature: %.2fΒ°C\n", average);
printf("Number of days above average: %d\n", above_average_count);
return 0;
}
Days with above average temperature: 3 5 6
Average temperature: 24.14Β°C
Number of days above average: 3