|$ curl https://forge-ai.dev/api/markdown?path=docs/c/arrays
$cat docs/c-—-arrays.md
updated Recently·40 min read·published

C — Arrays

CArraysData StructuresIntermediateIntermediate🎯Free Tools
What Is an Array?

An array in C is a contiguous block of memory that holds a fixed number of elements of the same type. Arrays are one of the most fundamental data structures in C and serve as the building block for strings, matrices, buffers, and more. Unlike higher-level languages, C arrays carry no metadata — no length, no bounds checking, no type info at runtime. You get raw memory and pointer arithmetic, which is both powerful and dangerous.

Array Declaration and Initialization

Arrays are declared by specifying the element type followed by the array name and the number of elements in square brackets. You can initialize at declaration time using a brace-enclosed list of values.

array_basics.c
C
1// Explicit size with initialization
2int numbers[5] = {10, 20, 30, 40, 50};
3
4// Implicit size — compiler counts the elements (size = 4)
5int small[] = {1, 2, 3, 4};
6
7// Partial initialization — remaining elements are zeroed
8int partial[10] = {5, 10, 15}; // {5, 10, 15, 0, 0, 0, 0, 0, 0, 0}
9
10// All zeros
11int zeroed[100] = {0};
12
13// Designated initializers (C99) — sparse initialization
14int sparse[10] = {[0] = 1, [5] = 42, [9] = 99};
15// sparse = {1, 0, 0, 0, 0, 42, 0, 0, 0, 99}

info

When you partially initialize an array, all unmentioned elements are automatically set to zero. This only works at declaration time — assigning `={0}` to an existing array is NOT valid C.
Accessing Elements

Arrays in C are zero-indexed. The first element is at index 0, and the last element is at index `size - 1`. The array name itself decays to a pointer to the first element in most expressions, so `arr[0]` is equivalent to `*(arr + 0)`.

array_access.c
C
1int arr[5] = {10, 20, 30, 40, 50};
2
3// Access by index
4printf("First: %d\n", arr[0]); // 10
5printf("Third: %d\n", arr[2]); // 30
6printf("Last: %d\n", arr[4]); // 50
7
8// Pointer arithmetic equivalent
9printf("Second: %d\n", *(arr + 1)); // 20
10printf("Fourth: %d\n", *(arr + 3)); // 40
11
12// Modify elements
13arr[1] = 99;
14printf("Modified: %d\n", arr[1]); // 99
Array Traversal

The standard way to iterate over an array is with a for loop from index 0 to` length - 1`. Since C arrays don't know their own size, you must track the length separately.

array_traversal.c
C
1int arr[] = {5, 3, 8, 1, 9, 2, 7};
2int len = sizeof(arr) / sizeof(arr[0]); // 7
3
4// Classic for loop
5for (int i = 0; i < len; i++) {
6 printf("arr[%d] = %d\n", i, arr[i]);
7}
8
9// While loop
10int i = 0;
11while (i < len) {
12 printf("%d ", arr[i]);
13 i++;
14}
15
16// Using a pointer (more idiomatic C)
17for (int *p = arr; p < arr + len; p++) {
18 printf("%d ", *p);
19}
The sizeof Trick and Why Arrays Don't Know Their Size

The expression `sizeof(arr) / sizeof(arr[0])` gives you the number of elements in an array — but only when the array is declared in the same scope. When an array is passed to a function, it decays to a pointer, and `sizeof` returns the pointer size, not the array size. This is one of the most common source of bugs in C.

array_sizeof.c
C
1void print_size(int arr[]) {
2 // DANGER: arr is now a pointer!
3 // sizeof(arr) == sizeof(int*) == 8 on 64-bit
4 printf("Inside function: %zu bytes\n", sizeof(arr));
5}
6
7int main(void) {
8 int arr[10] = {0};
9 printf("In main: %zu bytes\n", sizeof(arr)); // 40
10 printf("Elements: %zu\n", sizeof(arr)/sizeof(arr[0])); // 10
11
12 print_size(arr); // prints 8, NOT 40
13
14 // Always pass the length alongside the array
15 return 0;
16}

warning

Never use `sizeof(arr)/sizeof(arr[0])` on an array passed to a function. Always pass the length as a separate parameter. This is a fundamental rule of C programming.
Multi-Dimensional Arrays

C supports multi-dimensional arrays as arrays of arrays. A 2D array is stored in row-major order — all elements of the first row come first in memory, then the second row, and so on.

array_2d.c
C
1// 2D array: 3 rows, 4 columns
2int matrix[3][4] = {
3 {1, 2, 3, 4},
4 {5, 6, 7, 8},
5 {9, 10, 11, 12}
6};
7
8// Partial initialization — rest is zero
9int sparse[3][4] = {
10 {1, 5},
11 {0, 0, 9},
12 {}
13};
14
15// Access elements
16printf("%d\n", matrix[1][2]); // 7
17
18// Row-major layout in memory: 1,2,3,4,5,6,7,8,9,10,11,12
19// Traverse 2D array
20for (int i = 0; i < 3; i++) {
21 for (int j = 0; j < 4; j++) {
22 printf("%3d ", matrix[i][j]);
23 }
24 printf("\n");
25}

3D arrays work the same way — they're arrays of 2D arrays. They're less common but useful for things like volumetric data or color images with width × height × channels.

array_3d.c
C
1// 3D array: 2 layers, 3 rows, 4 columns
2int cube[2][3][4] = {
3 {
4 {1, 2, 3, 4},
5 {5, 6, 7, 8},
6 {9, 10, 11, 12}
7 },
8 {
9 {13, 14, 15, 16},
10 {17, 18, 19, 20},
11 {21, 22, 23, 24}
12 }
13};
14
15printf("%d\n", cube[1][2][3]); // 24
Arrays as Function Parameters

When you pass an array to a function, it decays to a pointer to its first element. The function receives a pointer, not a copy. This means modifications inside the function affect the original array. For 1D arrays, you must pass the size separately. For 2D arrays, you must specify all dimensions except the first.

array_params.c
C
1// 1D array parameter — size must be passed separately
2void fill(int arr[], int size, int value) {
3 for (int i = 0; i < size; i++) {
4 arr[i] = value;
5 }
6}
7
8// 2D array parameter — second dimension must be known at compile time
9void print_matrix(int rows, int cols, int matrix[rows][cols]) {
10 for (int i = 0; i < rows; i++) {
11 for (int j = 0; j < cols; j++) {
12 printf("%3d ", matrix[i][j]);
13 }
14 printf("\n");
15 }
16}
17
18// Alternative: pass as a flat pointer with explicit indexing
19void print_flat(int *matrix, int rows, int cols) {
20 for (int i = 0; i < rows; i++) {
21 for (int j = 0; j < cols; j++) {
22 printf("%3d ", matrix[i * cols + j]);
23 }
24 printf("\n");
25 }
26}
📝

note

In C99 and later, you can use variable-length array (VLA) parameters like `int matrix[rows][cols]`. The size parameters must appear before the array parameter in the function signature.
Variable-Length Arrays (C99)

C99 introduced variable-length arrays (VLAs), which allow array sizes to be determined at runtime. VLAs are allocated on the stack and their lifetime is limited to the block in which they are declared. They are optional in C11 and later.

array_vla.c
C
1#include <stdio.h>
2#include <stdlib.h>
3
4void dynamic_matrix(int n) {
5 // VLA — size determined at runtime
6 int matrix[n][n];
7
8 // Initialize as identity matrix
9 for (int i = 0; i < n; i++) {
10 for (int j = 0; j < n; j++) {
11 matrix[i][j] = (i == j) ? 1 : 0;
12 }
13 }
14
15 // Print
16 for (int i = 0; i < n; i++) {
17 for (int j = 0; j < n; j++) {
18 printf("%d ", matrix[i][j]);
19 }
20 printf("\n");
21 }
22}
23
24int main(void) {
25 int size;
26 printf("Enter matrix size: ");
27 scanf("%d", &size);
28
29 if (size > 0 && size <= 1000) {
30 dynamic_matrix(size);
31 }
32
33 return 0;
34}

warning

VLAs are allocated on the stack. Large VLAs can cause stack overflow. Use malloc() for large dynamic arrays. VLAs are also not supported in all C compilers (MSVC does not support them).
Array Bounds and Undefined Behavior

C performs no bounds checking on array access. Accessing an element outside the array's allocated memory is undefined behavior — it may read garbage data, corrupt other variables, crash your program, or appear to work correctly (which is the worst outcome because the bug hides).

array_bounds.c
C
1int arr[5] = {10, 20, 30, 40, 50};
2
3// Undefined behavior — reading past the end
4int bad = arr[5]; // index 5 is OUT OF BOUNDS (valid: 0-4)
5int worse = arr[-1]; // negative index is also UB
6
7// This might corrupt other stack variables
8int x = 42;
9arr[10] = 99; // writes to arbitrary memory
10printf("%d\n", x); // x might be corrupted
11
12// The compiler will NOT warn you about this (usually)
13// Always ensure your indices are in range: 0 <= i < size
Common Array Algorithms

Arrays are the foundation for many classic algorithms. Here are the most essential search and sort algorithms every C programmer should know.

Linear Search — O(n)

linear_search.c
C
1int linear_search(int arr[], int size, int target) {
2 for (int i = 0; i < size; i++) {
3 if (arr[i] == target) {
4 return i; // found at index i
5 }
6 }
7 return -1; // not found
8}
9
10// Usage
11int arr[] = {4, 2, 7, 1, 9, 3};
12int idx = linear_search(arr, 6, 7);
13printf("Found at index: %d\n", idx); // 2

Binary Search — O(log n) — requires sorted array

binary_search.c
C
1int binary_search(int arr[], int size, int target) {
2 int low = 0, high = size - 1;
3
4 while (low <= high) {
5 int mid = low + (high - low) / 2; // avoid overflow
6
7 if (arr[mid] == target) {
8 return mid;
9 } else if (arr[mid] < target) {
10 low = mid + 1;
11 } else {
12 high = mid - 1;
13 }
14 }
15 return -1; // not found
16}

Bubble Sort — O(n²) — simple but slow

bubble_sort.c
C
1void bubble_sort(int arr[], int size) {
2 for (int i = 0; i < size - 1; i++) {
3 int swapped = 0;
4 for (int j = 0; j < size - 1 - i; j++) {
5 if (arr[j] > arr[j + 1]) {
6 int tmp = arr[j];
7 arr[j] = arr[j + 1];
8 arr[j + 1] = tmp;
9 swapped = 1;
10 }
11 }
12 if (!swapped) break; // already sorted
13 }
14}

Selection Sort — O(n²) — minimal swaps

selection_sort.c
C
1void selection_sort(int arr[], int size) {
2 for (int i = 0; i < size - 1; i++) {
3 int min_idx = i;
4 for (int j = i + 1; j < size; j++) {
5 if (arr[j] < arr[min_idx]) {
6 min_idx = j;
7 }
8 }
9 if (min_idx != i) {
10 int tmp = arr[i];
11 arr[i] = arr[min_idx];
12 arr[min_idx] = tmp;
13 }
14 }
15}

Insertion Sort — O(n²) — efficient for small or nearly sorted arrays

insertion_sort.c
C
1void insertion_sort(int arr[], int size) {
2 for (int i = 1; i < size; i++) {
3 int key = arr[i];
4 int j = i - 1;
5 while (j >= 0 && arr[j] > key) {
6 arr[j + 1] = arr[j];
7 j--;
8 }
9 arr[j + 1] = key;
10 }
11}
Passing 2D Arrays to Functions

Passing 2D arrays to functions in C is tricky because the compiler needs to know the number of columns to compute memory offsets. There are several approaches, each with trade-offs.

array_2d_params.c
C
1// Method 1: Fixed column size
2void sum_rows_fixed(int matrix[][4], int rows) {
3 for (int i = 0; i < rows; i++) {
4 int sum = 0;
5 for (int j = 0; j < 4; j++) {
6 sum += matrix[i][j];
7 }
8 printf("Row %d sum: %d\n", i, sum);
9 }
10}
11
12// Method 2: VLA parameters (C99)
13void sum_rows_vla(int rows, int cols, int matrix[rows][cols]) {
14 for (int i = 0; i < rows; i++) {
15 int sum = 0;
16 for (int j = 0; j < cols; j++) {
17 sum += matrix[i][j];
18 }
19 printf("Row %d sum: %d\n", i, sum);
20 }
21}
22
23// Method 3: Flat pointer with manual indexing
24void sum_rows_flat(int *matrix, int rows, int cols) {
25 for (int i = 0; i < rows; i++) {
26 int sum = 0;
27 for (int j = 0; j < cols; j++) {
28 sum += matrix[i * cols + j];
29 }
30 printf("Row %d sum: %d\n", i, sum);
31 }
32}
Array of Strings

An array of strings is a 2D char array where each row is a null-terminated string. The second dimension sets the maximum length of each string.

array_strings.c
C
1// Array of strings (2D char array)
2char fruits[][20] = {
3 "Apple",
4 "Banana",
5 "Cherry",
6 "Date"
7};
8
9int count = sizeof(fruits) / sizeof(fruits[0]); // 4
10
11for (int i = 0; i < count; i++) {
12 printf("Fruit %d: %s\n", i, fruits[i]);
13}
14
15// Compare strings
16if (strcmp(fruits[0], "Apple") == 0) {
17 printf("First fruit is Apple\n");
18}
19
20// You can modify individual characters
21fruits[0][0] = 'a'; // "apple" — valid because it's a char array
Composite Literal Arrays (C99)

C99 introduced composite literals, which create unnamed arrays (or structs) that exist as expressions. Unlike array literals in other languages, these are actual objects in memory. You can pass them to functions without naming them.

array_composite.c
C
1#include <stdio.h>
2
3void print_array(int *arr, int size) {
4 for (int i = 0; i < size; i++) {
5 printf("%d ", arr[i]);
6 }
7 printf("\n");
8}
9
10int main(void) {
11 // Pass a temporary array directly to a function
12 print_array((int[]){10, 20, 30, 40, 50}, 5);
13
14 // Assign to a pointer (lifetime = enclosing block)
15 int *dynamic = (int[]){1, 2, 3, 4, 5};
16 printf("First: %d\n", dynamic[0]);
17
18 // 2D composite literal
19 int (*grid)[3] = (int[][3]){
20 {1, 2, 3},
21 {4, 5, 6}
22 };
23
24 printf("%d %d %d\n", grid[0][0], grid[0][1], grid[0][2]);
25
26 return 0;
27}
Flexible Array Members (C99)

A flexible array member is an array of unknown size declared as the last member of a struct. It must be the only array member and must come after all fixed members. You allocate the struct with extra space using malloc.

array_flexible.c
C
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5typedef struct {
6 int length;
7 char data[]; // flexible array member (no size specified)
8} String;
9
10String *string_create(const char *src) {
11 int len = strlen(src);
12 String *s = malloc(sizeof(String) + len + 1); // +1 for null terminator
13 if (!s) return NULL;
14
15 s->length = len;
16 memcpy(s->data, src, len + 1);
17 return s;
18}
19
20void string_destroy(String *s) {
21 free(s);
22}
23
24int main(void) {
25 String *hello = string_create("Hello, World!");
26 printf("Length: %d\n", hello->length); // 13
27 printf("Content: %s\n", hello->data); // Hello, World!
28 string_destroy(hello);
29 return 0;
30}
Common Mistakes

Arrays are a frequent source of bugs in C. Here are the most common mistakes and how to avoid them.

MistakeExampleFix
Off-by-one error`for (i = 0; i <= len)``for (i = 0; i < len)`
Buffer overflow`arr[5] = 1` on size-5 array`arr[4] = 1` (last valid index)
Uninitialized values`int arr[100];` — garbage values`int arr[100] = {0};`
Array assigned to pointer`int *p = arr;` — loses size info`int *p = arr; int n = len;`
sizeof on decayed array`sizeof(arr)` in function = pointer size`Pass size as separate parameter`
array_mistakes.c
C
1// Off-by-one: accessing arr[5] on a size-5 array
2int arr[5] = {1, 2, 3, 4, 5};
3for (int i = 0; i <= 5; i++) { // BUG: should be i < 5
4 printf("%d\n", arr[i]); // arr[5] is out of bounds
5}
6
7// Uninitialized array
8int values[100];
9// values[0] could be anything — random garbage from the stack
10printf("%d\n", values[0]); // undefined behavior
11
12// Always initialize arrays you plan to read from
13int safe[100] = {0}; // all zeros

best practice

Always initialize arrays before reading from them. Use `int arr[N] = {0};` at declaration. When passing arrays to functions, always pass the size as a separate parameter. Use const pointers for read-only access: `void func(const int arr[], int n)`.
Summary
ConceptSyntax
Declare + initialize`int arr[5] = {1,2,3,4,5};`
Implicit size`int arr[] = {1,2,3};`
Get array length`sizeof(arr)/sizeof(arr[0])`
2D array`int m[3][4];`
VLA (C99)`int arr[n];`
Composite literal`(int[]){1,2,3}`
Flexible array member`struct { int n; int d[]; };`
$Blueprint — Engineering Documentation·Section ID: C-ARRAYS·Revision: 1.0