C · Beginner

Arrays in C

10 min readUpdated September 24, 2026Every example verified

In short: A C array is a fixed-size block of elements of one type stored side by side, declared like int scores[5]; and accessed by a zero-based index from scores[0] to scores[4]. The array does not carry its own length and C never checks that an index is in range, so the program tracks the size itself and keeps every index between 0 and length minus 1.

Many values under one name

Seven days of sales are seven numbers that belong together and are processed the same way. Declaring seven separate variables would force you to repeat every line of code seven times; an array gives all seven one name and lets a loop visit them by number. int loaves[7]; reserves space for seven ints in a row, and loaves[3] is the fourth of them, because indexes start at 0. The index in brackets can be any integer expression, which is what makes loaves[i] inside a loop work.

An array can be filled at declaration with an initialiser list in braces. int stops[] = {3, 7, 12, 18}; lets the compiler count the elements, here four. If you give a size and fewer values, as in double temps[5] = {21.5, 19.0};, the remaining elements are set to zero, and int seen[10] = {0}; is the standard way to zero a whole array. Without any initialiser, a local array holds garbage. The size must be a constant for these forms; C99 also allows variable-length arrays sized at run time, but they are optional in C11 and cannot take an initialiser list, so a fixed maximum is the safer habit.

C does not check indexes. loaves[7] on a seven-element array compiles and runs; it reads or writes the memory just past the end, which belongs to some other variable or to nothing at all. That is undefined behaviour: wrong values, a crash, or a program that works until the day it does not. The array does not remember its size either, so every loop needs the length from somewhere: a constant, a count read from input, or sizeof(arr) / sizeof(arr[0]), which divides the total bytes by the bytes per element. That idiom only works where the array itself is in scope; it is wrong inside a function that received the array as a parameter.

That is because an array is passed to a function as the address of its first element, not as a copy. The parameter int values[] is really a pointer, sizeof on it gives the size of a pointer, and writes through values[i] change the caller's array. So always pass the length alongside the array, and declare the parameter const int values[] when the function should not modify it, so the compiler enforces that promise. Assigning one whole array to another with = is not allowed; copy it element by element or with memcpy from <string.h>.

Two-dimensional arrays, int grid[3][4], are arrays of arrays: three rows of four ints, indexed grid[row][col] and walked with one loop inside another. A string in C is a char array with a terminating zero; it gets its own lesson: strings.

Syntax

 C · syntax
type name[SIZE];                     /* SIZE elements, contents undefined */
type name[SIZE] = {v0, v1};          /* listed elements set, the rest 0 */
type name[] = {v0, v1, v2};          /* size taken from the list: 3 */
type name[SIZE] = {0};               /* every element 0 */

name[i]                              /* element i; valid i is 0 .. SIZE-1 */
sizeof(name) / sizeof(name[0])       /* element count, in the declaring scope only */

type grid[ROWS][COLS];               /* two-dimensional; grid[r][c] */
void f(const int values[], int n);   /* array parameter: pass the length too */

SIZE must be a constant expression for an array that is initialised with a list. #define SIZE 7 at the top of the file is the usual way to name it.

Declaration forms

DeclarationElementsContents
int a[4];4undefined (garbage) for a local array
int a[4] = {5, 6};45, 6, 0, 0
int a[] = {5, 6, 7};35, 6, 7
int a[4] = {0};40, 0, 0, 0
char c[3] = {'a', 'b', 'c'};3three chars, not a string: no terminating zero

Filling an array from input and scanning it

Seven daily loaf counts are read into an array; the input line is 42 38 51 47 60 73 55.

 C
#include <stdio.h>

#define DAYS 7

int main(void)
{
    int loaves[DAYS];
    for (int i = 0; i < DAYS; i++) {
        if (scanf("%d", &loaves[i]) != 1) {
            return 1;
        }
    }

    int total = 0;
    int best_day = 0;
    for (int i = 0; i < DAYS; i++) {
        total += loaves[i];
        if (loaves[i] > loaves[best_day]) {
            best_day = i;
        }
    }
    printf("Total: %d\n", total);
    printf("Best day: %d (%d loaves)\n", best_day + 1, loaves[best_day]);
    printf("Average: %.1f\n", (double) total / DAYS);
    return 0;
}

Input given to the program: 42 38 51 47 60 73 55

Output

Total: 366
Best day: 6 (73 loaves)
Average: 52.3

DAYS is a compile-time constant, so it can size the array and bound both loops. The first loop stores each input at loaves[i], passing the element's address to scanf with &loaves[i]. The second loop accumulates the total and tracks the index of the largest value rather than the value itself, so the program can report which day it was; best_day + 1 converts the zero-based index into the day number a person expects.

Initialiser lists and the sizeof idiom

One array is sized by its initialiser and its length is computed; another is partly initialised.

 C
#include <stdio.h>

int main(void)
{
    int stops[] = {3, 7, 12, 18};
    size_t count = sizeof(stops) / sizeof(stops[0]);
    printf("%zu stops\n", count);
    for (size_t i = 0; i < count; i++) {
        printf("stop %zu at km %d\n", i + 1, stops[i]);
    }

    double temps[5] = {21.5, 19.0};
    for (int i = 0; i < 5; i++) {
        printf("%.1f ", temps[i]);
    }
    printf("\n");
    return 0;
}

Output

4 stops
stop 1 at km 3
stop 2 at km 7
stop 3 at km 12
stop 4 at km 18
21.5 19.0 0.0 0.0 0.0

sizeof(stops) is 16 bytes on this machine and sizeof(stops[0]) is 4, so the division gives 4 elements. size_t is the correct type for that result, and %zu prints it; making the loop counter a size_t too avoids a signed/unsigned comparison warning. temps was declared with five elements but given only two values, so the other three are 0.0, which the loop prints.

Passing an array to a function

One function counts readings above a limit and leaves the array alone; another doubles every element in place.

 C
#include <stdio.h>

int count_above(const int values[], int n, int limit)
{
    int count = 0;
    for (int i = 0; i < n; i++) {
        if (values[i] > limit) {
            count++;
        }
    }
    return count;
}

void scale(int values[], int n, int factor)
{
    for (int i = 0; i < n; i++) {
        values[i] *= factor;
    }
}

int main(void)
{
    int readings[6] = {12, 30, 7, 45, 28, 33};
    printf("above 25: %d\n", count_above(readings, 6, 25));
    scale(readings, 6, 2);
    printf("after scaling:");
    for (int i = 0; i < 6; i++) {
        printf(" %d", readings[i]);
    }
    printf("\n");
    printf("above 60 now: %d\n", count_above(readings, 6, 60));
    return 0;
}

Output

above 25: 4
after scaling: 24 60 14 90 56 66
above 60 now: 2

count_above takes const int values[] and the length n; the compiler would reject any assignment to the elements. scale takes the same array without const and modifies it, and main sees the changes because the function received the address of readings[0], not a copy: the opposite of what happens to a plain int parameter (see functions). Passing 6 alongside the array is not optional: inside either function, sizeof(values) would be the size of a pointer, 8, not 24.

Common mistakes

  • Indexing one past the end

    Why it goes wrong: For int a[5] the valid indexes are 0 to 4. a[5] compiles and touches memory outside the array; the loop for (i = 0; i <= 5; i++) does exactly that on its last round and the program may print garbage or crash somewhere unrelated.

    Fix: Loop with i < size, and treat the size as the number of elements, not the last index.

     C · fix
    int a[5] = {0};
    for (int i = 0; i < 5; i++) {
        a[i] = i * i;
    }
  • Using sizeof on an array parameter

    Why it goes wrong: Inside int total(int values[]), values is a pointer, so sizeof(values) / sizeof(values[0]) is 8 / 4 = 2 whatever the caller passed. gcc warns with sizeof on array function parameter.

    Fix: Pass the length as a second parameter and use it.

     C · fix
    int total(const int values[], int n)
    {
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += values[i];
        }
        return sum;
    }
  • Assigning one array to another

    Why it goes wrong: b = a; does not compile: an array is not an assignable value, and a in an expression turns into the address of its first element.

    Fix: Copy in a loop, or memcpy(b, a, sizeof a); from <string.h> when both have the same size.

     C · fix
    for (int i = 0; i < n; i++) {
        b[i] = a[i];
    }
  • Counting into an array that was never zeroed

    Why it goes wrong: int counts[10]; followed by counts[d]++ adds one to garbage. Local arrays are not zeroed automatically.

    Fix: Initialise with = {0} when the array accumulates counts.

     C · fix
    int counts[10] = {0};
    counts[d]++;

Counting occurrences with an index

An array indexed by the value itself is the quickest way to count how often something occurs. To tally the grades A to E in a class, declare int tally[5] = {0}; and for each grade add one to tally[grade - 'A'], using the fact that consecutive letters have consecutive character codes. The same trick counts digits with tally[d]++, or bins sensor readings into ranges with tally[value / 10]++. Just check that the index is within range before using it: a grade of 'Z' would land 25 elements past the end.

 C · in practice
int tally[5] = {0};
char grade;
while (scanf(" %c", &grade) == 1) {
    if (grade >= 'A' && grade <= 'E') {
        tally[grade - 'A']++;
    }
}
for (int i = 0; i < 5; i++) {
    printf("%c: %d\n", 'A' + i, tally[i]);
}

Key points

  • type name[N] holds N elements of one type at indexes 0 to N - 1.
  • Initialiser lists fill from the front; missing elements become 0; no initialiser means garbage.
  • C never checks an index; going out of range is undefined behaviour.
  • sizeof(a) / sizeof(a[0]) gives the length only where the array is declared.
  • A function receives the address of the array, so pass the length too and use const when it must not modify it.
  • Arrays cannot be assigned with =; copy element by element or with memcpy.
  • 2-D arrays are arrays of arrays: grid[row][col] with nested loops.

Try it yourself

Read five integers into an array and print them in reverse order on one line, separated by single spaces. For the input 4 9 1 7 3 print 3 7 1 9 4.

Your program
#include <stdio.h>

int main(void)
{
    int values[5];
    for (int i = 0; i < 5; i++) {
        if (scanf("%d", &values[i]) != 1) {
            return 1;
        }
    }
    /* print values[4] down to values[0], separated by spaces */
    return 0;
}
Input the program receives: 4 9 1 7 3
Expected output: 3 7 1 9 4

Practise this

Exercises for this lesson are in the C practice set.

Open the C playground

Frequently asked questions

How do I get the length of an array in C?

There is no length property. Where the array is declared, sizeof(arr) / sizeof(arr[0]) computes the element count from the byte sizes. Anywhere else, in particular inside a function that received the array as a parameter, that expression gives the size of a pointer instead, so the length has to be passed along explicitly or kept in a variable next to the array.

Why does C not report an error when I read past the end of an array?

Bounds checks cost time on every access and C leaves that decision to the programmer, so the language defines an out-of-range index as undefined behaviour rather than an error. The compiler may warn when the index is a constant it can see, and tools such as gcc's -fsanitize=address catch the mistake at run time during testing, but in a normal build the program simply reads or writes whatever memory lies there.

Can the size of an array be a variable?

With a variable-length array, int a[n]; where n is known only at run time, yes: C99 introduced them and gcc supports them, but C11 made them optional and they cannot be initialised with a list. For exercises the simplest route is a fixed maximum size from the input limits, such as int a[1000];, plus a count variable for how many elements are in use. Memory sized exactly at run time is the job of malloc, covered in dynamic memory.

Progress is stored only in this browser.

How this page was checked. Every program on it was run with GCC 13 (C11) at build time by the publishing checks, and the output shown is what it printed. Running C inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with GCC 13 (C11) locally.