Scientyfic World

Arrays in C: Declaration, Initialization, Size, 2D Arrays and Common Mistakes

An array in C is a fixed-size collection of elements of the same type, stored next to each other in memory and accessed by an index that starts at 0....

Share:

Get an AI summary of this article

Array banner image

An array in C is a fixed-size collection of elements of the same type, stored next to each other in memory and accessed by an index that starts at 0. Instead of declaring fifty separate variables for fifty scores, you declare one array, such as int scores[50], and reach each value as scores[0] to scores[49].

This guide covers how to declare and initialise arrays, loop over them, find their length with sizeof, pass them to functions, use character arrays and strings, work with 2D arrays, and grow an array at runtime with malloc and realloc. Every program here was compiled with clang -std=c17 -Wall -Wextra and run; the outputs shown are the real ones. At the end is a list of the mistakes that crash C programs most often, including one from the earlier version of this post.

How Arrays Work in C

When you write int a[5];, the compiler reserves one block of memory big enough for five int values (20 bytes on most current systems, where an int is 4 bytes). The elements sit next to each other, so the program can find any element instantly: element i is i × 4 bytes after the start.

Diagram of how C stores arrays: int a[5] as five consecutive 4-byte slots at base plus 0 to 16 bytes, and int m[2][3] stored row by row
A C array is one block of memory. Element i sits i × sizeof(element) bytes after the start, and 2D arrays are stored row by row.

Three rules follow from this layout:

  • Indexes start at 0 and the last valid index is length − 1. The reasons are explained in why array indexes start at 0.
  • The size is fixed once the array is created. To grow, you need dynamic memory (covered below).
  • C does not check bounds. Reading a[5] of a five-element array compiles and runs, and returns whatever happens to be in memory.

Declaring and Initialising an Array

The syntax is type name[size];. You can give values at the same time with a brace list:

#include <stdio.h>

int main(void) {
    int scores[5] = {90, 72, 85, 64, 99};   /* size and values            */
    int partial[5] = {1, 2};                 /* rest are set to 0          */
    int zeros[5] = {0};                      /* all five are 0             */
    int counted[] = {3, 1, 4, 1, 5, 9};      /* size worked out: 6         */
    int picked[6] = {[0] = 7, [4] = 9};      /* C99 designated initializers */

    printf("scores[0] = %d, scores[4] = %d\n", scores[0], scores[4]);
    printf("partial: ");
    for (int i = 0; i < 5; i++) printf("%d ", partial[i]);
    printf("\nzeros[3] = %d\n", zeros[3]);
    printf("counted has %zu elements\n", sizeof counted / sizeof counted[0]);
    printf("picked: ");
    for (int i = 0; i < 6; i++) printf("%d ", picked[i]);
    printf("\n");

    scores[1] = 80;                          /* change one element */
    printf("scores[1] is now %d\n", scores[1]);
    return 0;
}
scores[0] = 90, scores[4] = 99
partial: 1 2 0 0 0 
zeros[3] = 0
counted has 6 elements
picked: 7 0 0 0 9 0 
scores[1] is now 80
FormWhat it does
int a[5];Five ints. Inside a function the values are garbage until you set them; global and static arrays start at 0
int a[5] = {90, 72, 85, 64, 99};Size and every value given
int a[5] = {1, 2};Missing values are set to 0: 1 2 0 0 0
int a[5] = {0};The usual way to zero an array
int a[] = {3, 1, 4, 1, 5, 9};The compiler counts the values (6)
int a[6] = {[0] = 7, [4] = 9};Designated initializers (C99): set chosen indexes, the rest are 0

You can’t assign a whole array after it is declared (a = b; is a compile error); copy element by element in a loop, or with memcpy.

Reading Input Into an Array and Looping Over It

A classic first program: read some numbers and add them up. The array has a fixed maximum size, and the program checks the count before using it:

#include <stdio.h>

#define MAX 100

int main(void) {
    int a[MAX];
    int n;

    printf("How many numbers (1-%d)? ", MAX);
    if (scanf("%d", &n) != 1 || n < 1 || n > MAX) {
        printf("Please enter a number from 1 to %d.\n", MAX);
        return 1;
    }

    for (int i = 0; i < n; i++) {
        printf("Number %d: ", i + 1);
        scanf("%d", &a[i]);
    }

    long sum = 0;
    for (int i = 0; i < n; i++) {
        sum += a[i];
    }
    printf("\nSum = %ld, average = %.2f\n", sum, (double)sum / n);
    return 0;
}

Running it with the inputs 4, then 12, 7, 30 and 5 printed:

How many numbers (1-100)? Number 1: Number 2: Number 3: Number 4:
Sum = 54, average = 13.50

Entering 0 as the count prints “Please enter a number from 1 to 100.” instead of running the loops. Two details matter here. The loop condition is i < n, never i <= n, so the last index used is n − 1. And the count is checked against MAX, because writing past the end of the array would corrupt memory. Loops themselves are covered in loops in C.

How to Find the Length of an Array in C

C arrays don’t store their length. You calculate it with sizeof: the size of the whole array in bytes divided by the size of one element.

#include <stdio.h>

#define LEN(arr) (sizeof(arr) / sizeof((arr)[0]))

void print_size(int arr[]) {
    /* arr is really a pointer here, so this is NOT the array length */
    printf("inside function: sizeof(arr) = %zu\n", sizeof(arr));
}

int main(void) {
    int a[5] = {10, 20, 30, 40, 50};
    printf("sizeof(a) = %zu bytes\n", sizeof(a));
    printf("sizeof(a[0]) = %zu bytes\n", sizeof(a[0]));
    printf("length = %zu\n", LEN(a));
    print_size(a);
    return 0;
}
sizeof(a) = 20 bytes
sizeof(a[0]) = 4 bytes
length = 5
inside function: sizeof(arr) = 8

Inside main, the formula gives the correct length, 5. Inside the function it doesn’t: an array passed to a function turns into a pointer to its first element (this is called array decay), so sizeof(arr) returns the size of a pointer, 8 bytes on a 64-bit system. Clang warned about exactly this at compile time:

warning: sizeof on array function parameter will return size of 'int *' instead of 'int[]' [-Wsizeof-array-argument]

So use the sizeof trick only where the array was declared, and pass the length along to any function that needs it.

Passing Arrays to Functions

#include <stdio.h>

/* Arrays are passed as a pointer to the first element, so pass the length too */
long sum(const int arr[], size_t n) {
    long total = 0;
    for (size_t i = 0; i < n; i++) total += arr[i];
    return total;
}

void double_all(int arr[], size_t n) {
    for (size_t i = 0; i < n; i++) arr[i] *= 2;   /* changes the caller's array */
}

int main(void) {
    int a[] = {1, 2, 3, 4};
    size_t n = sizeof a / sizeof a[0];

    printf("sum = %ld\n", sum(a, n));
    double_all(a, n);
    printf("after double_all: %d %d %d %d\n", a[0], a[1], a[2], a[3]);
    return 0;
}
sum = 10
after double_all: 2 4 6 8

Because the function receives a pointer to the original array, not a copy, double_all changes the caller’s values. Mark the parameter const (as in sum) when the function should only read the array; the compiler will then reject accidental writes. The parameter can be written as int arr[] or int *arr; they mean the same thing.

Character Arrays and Strings

C has no separate string type. A string is a char array that ends with the null character '\0':

#include <stdio.h>
#include <string.h>

int main(void) {
    char word[] = "array";                 /* 5 letters + '\0' */
    printf("sizeof = %zu, strlen = %zu\n", sizeof word, strlen(word));

    char grid[3][8] = {"C", "Python", "Go"};          /* 2D char array   */
    const char *names[] = {"C", "Python", "Go"};      /* array of pointers */
    for (int i = 0; i < 3; i++) printf("%s / %s\n", grid[i], names[i]);
    printf("sizeof grid = %zu, sizeof names = %zu\n", sizeof grid, sizeof names);
    return 0;
}
sizeof = 6, strlen = 5
C / C
Python / Python
Go / Go
sizeof grid = 24, sizeof names = 24
  • char word[] = "array"; takes 6 bytes: five letters plus the terminating '\0'. strlen counts only the letters.
  • An array of strings can be a 2D char array (grid: 3 rows of 8 bytes, each string copied into its row) or an array of pointers to string literals (names: three 8-byte pointers). Both happen to be 24 bytes here, but the pointer version doesn’t waste space on short strings, and its strings are read-only.
  • Always leave room for the '\0': a 5-letter word needs char w[6].

Two-Dimensional (2D) Arrays

A 2D array is an array of arrays, used for matrices, grids and tables. This program adds two 2×3 matrices and then prints where each element of a sits in memory:

#include <stdio.h>

#define ROWS 2
#define COLS 3

int main(void) {
    int a[ROWS][COLS] = {{1, 2, 3}, {4, 5, 6}};
    int b[ROWS][COLS] = {{10, 20, 30}, {40, 50, 60}};
    int sum[ROWS][COLS];

    for (int r = 0; r < ROWS; r++)
        for (int c = 0; c < COLS; c++)
            sum[r][c] = a[r][c] + b[r][c];

    for (int r = 0; r < ROWS; r++) {
        for (int c = 0; c < COLS; c++) printf("%4d", sum[r][c]);
        printf("\n");
    }

    /* Row-major layout: each row follows the previous one in memory */
    for (int r = 0; r < ROWS; r++)
        for (int c = 0; c < COLS; c++)
            printf("a[%d][%d] at offset %td bytes\n", r, c,
                   (char *)&a[r][c] - (char *)&a[0][0]);
    return 0;
}
  11  22  33
  44  55  66
a[0][0] at offset 0 bytes
a[0][1] at offset 4 bytes
a[0][2] at offset 8 bytes
a[1][0] at offset 12 bytes
a[1][1] at offset 16 bytes
a[1][2] at offset 20 bytes

The offsets show that C stores 2D arrays in row-major order: all of row 0, then all of row 1. Element [r][c] is at (r × COLS + c) × sizeof(int) bytes from the start. That is why looping over rows in the outer loop and columns in the inner loop is the fastest order, since it reads memory sequentially. Arrays with more dimensions (int cube[2][3][4]) work the same way. When you pass a 2D array to a function, every dimension except the first must be known, for example void print(int m[][3], int rows).

Dynamic Arrays With malloc and realloc

When you don’t know the size until runtime, allocate the array on the heap and grow it as needed:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    size_t capacity = 2, count = 0;
    int *items = malloc(capacity * sizeof *items);
    if (items == NULL) return 1;

    for (int value = 1; value <= 5; value++) {
        if (count == capacity) {                      /* full: grow it */
            capacity *= 2;
            int *bigger = realloc(items, capacity * sizeof *items);
            if (bigger == NULL) { free(items); return 1; }
            items = bigger;
            printf("grew to capacity %zu\n", capacity);
        }
        items[count++] = value * 10;
    }

    for (size_t i = 0; i < count; i++) printf("%d ", items[i]);
    printf("\n");
    free(items);
    return 0;
}
grew to capacity 4
grew to capacity 8
10 20 30 40 50

Doubling the capacity each time it fills keeps the number of copies low. Assign the result of realloc to a temporary pointer first: if it fails it returns NULL, and writing that straight into items would lose the original memory. Every malloc needs a matching free.

C99 also added variable-length arrays (int a[n]; where n is a variable). C11 made them optional, and they live on the stack, so a large n can crash the program. For anything that comes from user input, prefer a fixed maximum or malloc.

Common Mistakes With Arrays in C

Using a variable size before it has a value

The earlier version of this post had this bug, and it is common in beginner code:

#include <stdio.h>
int main(void) {
    int n, a[n], sum = 0;   /* n has no value yet */
    printf("Enter the size: ");
    scanf("%d", &n);
    for (int i = 0; i < n; i++) { scanf("%d", &a[i]); sum += a[i]; }
    printf("Sum = %d\n", sum);
    return 0;
}

a[n] is created when it is declared, while n still holds garbage, so the array gets a random size. Clang warned variable 'n' is uninitialized when used here. When I ran it with the inputs 3, 1, 2, 3, it crashed with a segmentation fault (exit code 139). With -fsanitize=address,undefined, the sanitizer reported variable length array bound evaluates to non-positive value 0, followed by a dynamic-stack-buffer-overflow. The fix is the program in the “Reading input” section: a fixed maximum size, and checking n before using it.

Other mistakes to watch for

  • Off-by-one loops: for (i = 0; i <= n; i++) reads one element past the end. Use i < n.
  • No bounds checking: writing past the end silently overwrites other variables. Compile with -fsanitize=address while developing to catch it.
  • Uninitialised local arrays: int a[5]; inside a function holds garbage. Use = {0}.
  • Using sizeof on a parameter to get the length, as shown above.
  • Returning a local array from a function: it is destroyed when the function returns. Return a malloced array, or let the caller pass one in.
  • Forgetting the ‘\0’ in a char array that is used as a string.

Arrays vs Pointers

Array (int a[5])Pointer (int *p)
What it isA block of 5 intsA variable that holds an address
sizeofWhole array (20 bytes)The pointer (8 bytes on 64-bit)
Can it point elsewhere?No, a = ... is an errorYes
Indexinga[i] means *(a + i)p[i] means *(p + i)
In expressions and function callsDecays to &a[0]Already a pointer

They look interchangeable because indexing works the same on both, but an array owns its memory and knows its size, while a pointer only knows an address. A good next step is processing arrays recursively; see recursion in C.

Conclusion

An array in C is a fixed-size block of same-type elements, indexed from 0 to length − 1. Initialise it with a brace list (or {0}), loop with i < n, get the length with sizeof a / sizeof a[0] where the array is declared, and pass the length to functions, because arrays become pointers when passed. Use 2D arrays for grids (stored row by row), char arrays with a terminating '\0' for strings, and malloc/realloc when the size is only known at runtime. Above all, remember that C never checks bounds for you.

FAQs

What is an array in C?

A fixed-size collection of elements of the same type stored next to each other in memory. You declare it as type name[size], for example int scores[5], and access elements by index from 0 to size – 1.

How do you find the size of an array in C?

Use sizeof(a) / sizeof(a[0]) in the scope where the array is declared. Inside a function that receives the array as a parameter, this does not work, because the parameter is a pointer; pass the length separately.

How do you declare and initialize an array in C?

Write the type, name and size, optionally with a brace list: int a[5] = {1, 2, 3, 4, 5};. Missing values are set to 0, int a[5] = {0}; zeroes the whole array, and int a[] = {1, 2, 3}; lets the compiler count the elements.

How are 2D arrays stored in C?

In row-major order: all elements of row 0 first, then row 1, and so on. Element [r][c] of an array with COLS columns is (r * COLS + c) elements from the start.

Can the size of an array change in C?

No. A normal array has a fixed size. To grow, allocate memory with malloc and resize it with realloc, then free it when you are done.

What happens if you access an array out of bounds in C?

The behaviour is undefined: C does not check, so the program may read garbage, overwrite other data or crash. Tools like AddressSanitizer (-fsanitize=address) catch these errors during testing.

Snehasish Konger
Developed @scientyficworld.org | Technical writer @Nected | Content Developer
Connect with Snehasish Konger

On This page

Take a Pause with Intervals

A Sunday letter on building, writing, and thinking deeper as a developer — short, honest, and worth your time.

Snehasish Konger profile photo

"Hey there — I'm Snehasish. Hope this post saved you some head-scratching time! I've spent years turning technical chaos into clarity, and I'm here to be your guide through the maze of modern tech. Stick around for more lightbulb moments — we're just getting started."

Related Posts