Understanding Arrays in C Language
Array Fundamentals
An array is a collection of elements of the same type stored in contiguous memory locations.
One-Dimensional Array Declaration
// General syntax for array declaration
element_type array_name[array_size];
The declaration requires three components:
element_type: the data type of each elementarray_name: the identifier for the arrayarray_size: a constant expression specifying the number of elements
int values[10];
int data[3 + 2]; // constant expression allowed
char buffer[3];
float measurements[10];
double precision[4];
Varible-Length Arrays (C99 Feature)
Prior to C99, array dimensions had to be constants. C99 introduced variable-length arrays (VLAs), allowing runtime-specified sizes:
int n = 0;
scanf("%d", &n);
int dynamicArr[n]; // valid in C99, but cannot be initialized
Note: Visual Studio does not support VLAs regardless of C standard version.
One-Dimensional Array Initialization
Initialization occurs at declaration time:
int nums[10] = {1, 2, 3}; // partial initialization, rest are zero
int autoSize[] = {10, 20, 30, 40}; // size inferred from initializer
int zeroInit[5] = {0}; // all elements set to zero
int complete[5] = {1, 2, 3, 4, 5}; // full initialization
char letters[3] = {'x', 97, 'z'}; // ASCII values work in char arrays
char word[] = {'a', 'b', 'c'}; // no null terminator
char text[] = "hello"; // includes '\0' automatically
Accessing and Modifying Array Elements
Elements are accessed via zero-based indices:
#include <stdio.h>
int main() {
int data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int i = 0;
for (i = 0; i < 10; i++) {
printf("%d ", data[i]);
}
return 0;
}
Modifying elements through index assignment:
#include <stdio.h>
int main() {
int data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int i = 0;
// Reverse the array
for (i = 0; i < 10; i++) {
data[i] = 10 - i;
}
for (i = 0; i < 10; i++) {
printf("%d ", data[i]);
}
return 0;
}
Calculating Array Size
#include <stdio.h>
int main() {
int numbers[] = {5, 10, 15, 20, 25, 30, 35, 40, 45, 50};
int count = sizeof(numbers) / sizeof(numbers[0]);
printf("Total bytes: %zu\n", sizeof(numbers));
printf("Element size: %zu\n", sizeof(numbers[0]));
printf("Element count: %d\n", count);
return 0;
}
For character arrays, sizeof and strlen behave differently:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "x,y,z";
printf("sizeof counts null terminator: %zu\n", sizeof(str));
printf("strlen excludes null terminator: %zu\n", strlen(str));
printf("Character count: %zu\n", sizeof(str) / sizeof(str[0]));
return 0;
}
Key distinction: sizeof includes the null terminator, while strlen stops at it.
Memory Layout of One-Dimensional Arrays
Arrays occupy contiguous memory locations with addresses increasing sequentially:
#include <stdio.h>
int main() {
int values[5] = {10, 20, 30, 40, 50};
int length = sizeof(values) / sizeof(values[0]);
int k = 0;
for (k = 0; k < length; k++) {
printf("values[%d] address: %p\n", k, (void*)&values[k]);
}
return 0;
}
Observation: addresses increase by 4 bytes (size of int) per element, confirming contiguous storage from low to high memory addresses.
Two-Dimensional Array Declaration
element_type array_name[row_size][column_size];
int matrix[3][4];
char grid[3][5];
double table[2][4];
Two-Dimensional Array Initialization
#include <stdio.h>
int main() {
int a[3][4] = {1, 2, 3, 4}; // fills row by row
int b[3][4] = {{5, 6}, {7, 8}}; // explicit row grouping
int c[3][4] = {{1}, {2, 3}, {4, 5, 6}}; // partial row initialization
int d[][4] = {{9, 8}, {7, 6}}; // row dimension inferred
return 0;
}
Important rule: when initializing 2D arrays, the row dimension may be omitted, but the column dimension is mandatory.
Accessing Two-Dimensional Array Elements
#include <stdio.h>
int main() {
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
int row = 0;
int col = 0;
for (row = 0; row < 2; row++) {
for (col = 0; col < 3; col++) {
printf("matrix[%d][%d] = %d\n", row, col, matrix[row][col]);
}
}
return 0;
}
Modifying elements via nested loops:
#include <stdio.h>
int main() {
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
int row = 0;
int col = 0;
for (row = 0; row < 2; row++) {
for (col = 0; col < 3; col++) {
matrix[row][col] = row * 3 + col + 1;
}
}
return 0;
}
Memory Layout of Two-Dimensional Arrays
2D arrays are stored as contiguous memory, effectively as an array of arrays:
#include <stdio.h>
int main() {
int grid[2][3] = {{1, 2, 3}, {4, 5, 6}};
int r = 0;
int c = 0;
for (r = 0; r < 2; r++) {
for (c = 0; c < 3; c++) {
printf("&grid[%d][%d] = %p\n", r, c, (void*)&grid[r][c]);
}
}
return 0;
}
The notation grid[r] represents the r-th row as a 1D array, and grid[r][c] accesses the c-th element within that row.
Array Bounds
Array indices must remain within valid bounds. C does not perform automatic bounds checking:
int arr[5] = {1, 2, 3, 4, 5};
// Valid indices: 0, 1, 2, 3, 4
// arr[5] and arr[-1] are out of bounds but may not trigger immediate errors
Accessing indices below 0 or above size-1 constitutes undefined behavior.
Passing Arrays to Functions
When an array is passed to a function, it decays to a pointer. The array size information is lost:
#include <stdio.h>
void printArray(int arr[], int size) {
int i = 0;
for (i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
}
int main() {
int data[5] = {1, 2, 3, 4, 5};
printArray(data, 5); // size must be passed separately
return 0;
}
Bubble Sort Implementation
Bubble sort compares and swaps adjacent elements in multiple passes:
#include <stdio.h>
void bubbleSort(int arr[], int size) {
int pass = 0;
int j = 0;
int temp = 0;
for (pass = 0; pass < size - 1; pass++) {
for (j = 0; j < size - 1 - pass; j++) {
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main() {
int numbers[10] = {64, 34, 25, 12, 22, 11, 90, 5, 72, 40};
int idx = 0;
int length = sizeof(numbers) / sizeof(numbers[0]);
bubbleSort(numbers, length);
for (idx = 0; idx < length; idx++) {
printf("%d ", numbers[idx]);
}
return 0;
}
Understanding Array Names as Pointers
The array name evaluates to the adress of its first element in most contexts:
#include <stdio.h>
int main() {
int data[5] = {10, 20, 30, 40, 50};
printf("data points to: %p\n", (void*)data);
printf("data + 1 points to: %p\n", (void*)(data + 1));
printf("&data points to: %p\n", (void*)&data);
printf("&data + 1 points to: %p\n", (void*)(&data + 1));
printf("sizeof(data) = %zu bytes\n", sizeof(data));
return 0;
}
Exceptions where array name represents the entire array:
sizeof(array_name)returns the total byte size&array_namereturns a pointer to the entire array
In all other contexts, the array name behaves as a pointer to its first element.