Arrays

C · Lesson 16 of 32 7 min read Updated

A class has 40 students and each has marks in 5 subjects. You could create 200 separate variables, but you would never finish writing the program. An array stores many values of the same type under one name, in one continuous block of memory, and you pick each value by its position.

In this lesson you will create and initialise arrays, loop over them, find totals and maximums, pass arrays to functions, build two-dimensional tables, and learn why C's lack of bounds checking makes careful indexing essential.

Declaring and using an array

You declare an array with a type, a name and the number of elements in square brackets. Each element is reached by its index, and indexes start at 0: an array of 5 elements has indexes 0 to 4.

C
#include <stdio.h>

int main(void)
{
    int marks[5] = {78, 85, 91, 66, 88};

    printf("First subject: %d\n", marks[0]);
    printf("Last subject: %d\n", marks[4]);

    marks[3] = 70;   // re-evaluation raised the fourth mark

    size_t count = sizeof marks / sizeof marks[0];
    int total = 0;
    for (size_t i = 0; i < count; i++) {
        printf("Subject %zu: %d\n", i + 1, marks[i]);
        total += marks[i];
    }
    printf("Total: %d, average: %.1f\n", total, (double) total / count);
    return 0;
}
Output
First subject: 78
Last subject: 88
Subject 1: 78
Subject 2: 85
Subject 3: 91
Subject 4: 70
Subject 5: 88
Total: 412, average: 82.4

sizeof marks is the size of the whole array in bytes (20 here) and sizeof marks[0] is the size of one element (4), so their ratio is the number of elements. This trick keeps the loop correct even if you add more marks later. Array indexes and lengths are naturally size_t, which is why i has that type and is printed with %zu.

Ways to initialise an array

C
#include <stdio.h>

int main(void)
{
    int a[5] = {1, 2};                  // the rest become 0: {1, 2, 0, 0, 0}
    int b[4] = {};                      // all zero (C23)
    int c[] = {4, 8, 15};               // size 3, counted by the compiler
    int d[6] = {[2] = 50, [5] = 99};    // only some positions set

    for (int i = 0; i < 6; i++) {
        printf("%d ", d[i]);
    }
    printf("| c has %zu elements, b[3] = %d, a[4] = %d\n", sizeof c / sizeof c[0], b[3], a[4]);
    return 0;
}
Output
0 0 50 0 0 99 | c has 3 elements, b[3] = 0, a[4] = 0

If you give fewer values than the size, the remaining elements are set to zero. The [2] = 50 form is a designated initialiser (C99): it sets chosen positions and zeroes the rest. An array declared inside a function without any initialiser contains garbage, just like a single uninitialised variable.

Finding the highest value

Many array tasks follow one pattern: start with the first element, then walk through the rest and update a "best so far" variable.

C
#include <stdio.h>

int main(void)
{
    int scores[] = {72, 95, 64, 95, 81};
    size_t n = sizeof scores / sizeof scores[0];

    size_t best = 0;
    int above_80 = 0;
    for (size_t i = 0; i < n; i++) {
        if (scores[i] > scores[best]) {
            best = i;
        }
        if (scores[i] > 80) {
            above_80++;
        }
    }
    printf("Highest score %d by student number %zu\n", scores[best], best + 1);
    printf("Students above 80: %d\n", above_80);
    return 0;
}
Output
Highest score 95 by student number 2
Students above 80: 3

Two students scored 95. Because the test is > and not >=, the first one (student 2) stays the best. Storing the index of the best element, rather than just its value, tells you who scored it as well.

C does not check array bounds

Using an index outside 0 to n-1 is a serious bug. C does not stop you or raise an error:

C
int seats[3] = {1, 2, 3};
seats[3] = 99;   // bug: the valid indexes are 0, 1 and 2

This writes into whatever memory happens to follow the array, perhaps another variable. The program may seem fine, crash later somewhere unrelated, or behave differently on another computer. Writing past the end of an array is called a buffer overflow, and it is one of the most common causes of security holes in C software. Always make sure that loops run while i < n, not i <= n. The sanitizers in Debugging with gdb & Sanitizers can catch these mistakes while you test.

Passing arrays to functions

When you pass an array to a function, C does not copy it. The function receives the address of the first element, so it works on the original array. The function cannot find the length by itself, so you always pass the length as a separate argument.

C
#include <stdio.h>

double average(const int marks[], size_t n)
{
    int total = 0;
    for (size_t i = 0; i < n; i++) {
        total += marks[i];
    }
    return (double) total / n;
}

void add_grace(int marks[], size_t n, int grace)
{
    for (size_t i = 0; i < n; i++) {
        marks[i] += grace;
    }
}

int main(void)
{
    int marks[] = {35, 48, 52, 39};
    size_t n = sizeof marks / sizeof marks[0];

    printf("Average before: %.2f\n", average(marks, n));
    add_grace(marks, n, 5);
    printf("Average after:  %.2f\n", average(marks, n));
    printf("First mark now: %d\n", marks[0]);
    return 0;
}
Output
Average before: 43.50
Average after:  48.50
First mark now: 40

add_grace changed the array in main, because both refer to the same memory. const in average is a promise that the function only reads the array; the compiler rejects any attempt to change it there. Inside a function, sizeof marks gives the size of an address (8 bytes on 64-bit systems), not of the array, so the sizeof trick works only where the array was declared. You will see why in Pointer Arithmetic & Arrays.

Two-dimensional arrays

A two-dimensional array is an array of rows, perfect for tables such as students and subjects. marks[s][j] is row s, column j.

C
#include <stdio.h>

constexpr int STUDENTS = 3;
constexpr int SUBJECTS = 4;

int main(void)
{
    int marks[STUDENTS][SUBJECTS] = {
        {78, 85, 91, 66},   // Aarav
        {88, 72, 95, 81},   // Priya
        {59, 64, 70, 75},   // Kabir
    };

    for (int s = 0; s < STUDENTS; s++) {
        int total = 0;
        for (int j = 0; j < SUBJECTS; j++) {
            total += marks[s][j];
        }
        printf("Student %d total: %d\n", s + 1, total);
    }

    printf("Subject averages:");
    for (int j = 0; j < SUBJECTS; j++) {
        int sum = 0;
        for (int s = 0; s < STUDENTS; s++) {
            sum += marks[s][j];
        }
        printf(" %.1f", (double) sum / STUDENTS);
    }
    printf("\n");
    return 0;
}
Output
Student 1 total: 320
Student 2 total: 336
Student 3 total: 268
Subject averages: 75.0 73.7 85.3 74.0

In memory the rows are stored one after another. Row totals loop over columns inside a loop over rows; column averages swap the two loops.

Copying arrays

You cannot assign one array to another: b = a; gives the error "assignment to expression with array type". Copy element by element in a loop, or use memcpy from string.h: memcpy(b, a, sizeof a); copies all the bytes, provided b is at least as large as a.

Common mistakes

  • Using index n. An array of n elements ends at index n - 1; i <= n reads one past the end.
  • Using sizeof on an array parameter. It gives the size of an address. Pass the length instead.
  • Forgetting to initialise. Local arrays start with garbage; use = {} or fill them in a loop.
  • Assigning arrays with =. Use a loop or memcpy.
  • Returning a local array from a function. Its memory disappears when the function returns.

Key points

  • An array holds a fixed number of same-type elements in one block of memory, indexed from 0.
  • sizeof arr / sizeof arr[0] gives the element count, but only where the array is declared.
  • Missing initialisers become zero; C23 = {} zeroes everything; designated initialisers set chosen positions.
  • C never checks indexes; going out of bounds is undefined behaviour and a security risk.
  • Arrays passed to functions are not copied; pass the length too, and use const for read-only access.
  • Two-dimensional arrays such as marks[3][4] store tables row by row.

Exercise

Store a week of maximum temperatures for Kochi in an array: 31, 29, 33, 30, 34, 28, 32. Write a program that prints the lowest and highest temperature, the average to one decimal place, and how many days were above 30 degrees.

Show solution
C
#include <stdio.h>

int main(void)
{
    int temps[] = {31, 29, 33, 30, 34, 28, 32};
    size_t n = sizeof temps / sizeof temps[0];

    int low = temps[0];
    int high = temps[0];
    int total = 0;
    int hot_days = 0;

    for (size_t i = 0; i < n; i++) {
        if (temps[i] < low) {
            low = temps[i];
        }
        if (temps[i] > high) {
            high = temps[i];
        }
        if (temps[i] > 30) {
            hot_days++;
        }
        total += temps[i];
    }
    printf("Lowest: %d, highest: %d\n", low, high);
    printf("Average: %.1f\n", (double) total / n);
    printf("Days above 30: %d\n", hot_days);
    return 0;
}
Output
Lowest: 28, highest: 34
Average: 31.0
Days above 30: 4

Starting low and high at the first element, not at 0, keeps the program correct for any temperatures. The total is 217, and 217 / 7 is exactly 31.0; the days above 30 are 31, 33, 34 and 32.

Quick Quiz

1. What is the index of the last element of int a[10];?

2. What does int a[4] = {7}; contain?

3. Why must you pass an array's length to a function?

4. What happens when a C program writes to arr[10] in an array of 10 elements?

Learn C with a mentor

Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call