Tutorials Logic, IN info@tutorialslogic.com

C Arrays 1D, 2D, Multi dimensional

Contiguous Element Storage

A C array is a fixed-size sequence of elements of one type stored contiguously. Its size is part of the array type at the declaration site, valid indexes run from zero through count minus one, and C performs no automatic bounds check. Reading or writing outside the array is undefined behavior even when the program appears to work.

This lesson connects storage, initialization, traversal, pointer decay, function parameters, and multidimensional layout. You will learn where sizeof can recover an element count, where that information is lost, and how to carry lengths explicitly through program boundaries.

Declaration and Initialization

The element count in int values[5] reserves space for five int objects. An initializer can provide every value, infer the count, or initialize the remaining elements to zero. Automatic arrays without an initializer contain indeterminate values and must be assigned before they are read.

Array length is normally fixed for its lifetime. A variable length array, where supported by the chosen C implementation and language mode, uses a runtime bound at block scope; it is not a resizable container. For portable APIs and large dynamic sizes, allocate deliberately and keep the count with the pointer.

1D Arrays

1D Arrays
// Declaration
int numbers[5];

// Declaration with initialization
int numbers[5] = {10, 20, 30, 40, 50};

// Size inferred from initializer
int numbers[] = {10, 20, 30, 40, 50};

// Access element
printf("%d", numbers[0]);  // 10
numbers[2] = 99;           // modify element

Bounds and Traversal

A loop over count elements uses index < count, not index <= count. Prefer size_t for counts and indexes derived from sizeof because sizeof returns size_t. Check an external index before using it, and be careful when counting downward with an unsigned type because it cannot represent a negative stop value.

The expression sizeof values / sizeof values[0] computes the element count only where values is still an array. It works in the same scope as the declaration, but not after the array has adjusted to a pointer parameter.

Bounded Sum

Bounded Sum
#include <stdio.h>

int main(void) {
    int values[] = {4, 7, 1, 9};
    size_t count = sizeof values / sizeof values[0];
    int total = 0;

    for (size_t i = 0; i < count; ++i) {
        total += values[i];
    }

    printf("count=%zu total=%d\n", count, total);
    return 0;
}
Output
count=4 total=21

Array Parameters

In most expressions, an array expression converts to a pointer to its first element. A function parameter written int values[] is adjusted to int *values, so the function does not receive the array length and sizeof values measures a pointer. Pass the count as a separate parameter and document whether null is allowed when count is zero.

The pointer points to existing elements; it does not own or copy the array. The caller must keep the array alive for the duration of the call. Add const to the pointed-to element type when the function only reads values.

Length Travels with the Pointer

Length Travels with the Pointer
#include <stddef.h>

int sum(const int values[], size_t count) {
    int total = 0;
    for (size_t i = 0; i < count; ++i) {
        total += values[i];
    }
    return total;
}

const prevents the function from modifying elements through values. count supplies the boundary that the adjusted pointer does not carry.

Multidimensional Layout

A declaration such as int grid[2][3] is an array of two elements, where each element is an array of three int values. C stores the rows contiguously in row-major order. grid[row][column] first selects a row array and then an element inside that row.

When passing a multidimensional array, later dimensions must be known so pointer arithmetic can locate each row. A parameter such as int grid[][3] adjusts to a pointer to an array of three int values. Pass the row count separately.

1D Array - Declare, Initialize, Traverse

1D Array - Declare, Initialize, Traverse
// Declaration: rows x columns
int matrix[3][4];

// Initialization
int matrix[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};

// Access: matrix[row][col]
printf("%d", matrix[1][2]);  // 6

2D Array - Matrix Addition

2D Array - Matrix Addition
#include <stdio.h>

int main() {
    int scores[] = {85, 92, 78, 95, 88};
    int n = sizeof(scores) / sizeof(scores[0]);  // number of elements = 5

    // Traverse and print
    printf("Scores: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", scores[i]);
    }
    printf("\n");

    // Find sum and average
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += scores[i];
    }
    printf("Sum: %d\n", sum);
    printf("Average: %.2f\n", (float)sum / n);

    // Find maximum
    int max = scores[0];
    for (int i = 1; i < n; i++) {
        if (scores[i] > max) max = scores[i];
    }
    printf("Max: %d\n", max);

    return 0;
}

/*
Scores: 85 92 78 95 88
Sum: 438
Average: 87.60
Max: 95
*/

Passing Array to Function - Find Max Element

Passing Array to Function - Find Max Element
#include <stdio.h>

int main() {
    int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
    int b[2][3] = {{7, 8, 9}, {10, 11, 12}};
    int c[2][3];

    // Matrix addition
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            c[i][j] = a[i][j] + b[i][j];
        }
    }

    // Print result matrix
    printf("Matrix A + B:\n");
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%4d", c[i][j]);
        }
        printf("\n");
    }

    return 0;
}

/*
Matrix A + B:
   8  10  12
  14  16  18
*/

2D Arrays (Matrices)

2D Arrays (Matrices)
#include <stdio.h>

// Arrays are always passed by reference (pointer to first element)
int findMax(int arr[], int size) {
    int max = arr[0];
    for (int i = 1; i < size; i++) {
        if (arr[i] > max) max = arr[i];
    }
    return max;
}

void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

// Passing 2D array to function
void print2D(int rows, int cols, int arr[rows][cols]) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%4d", arr[i][j]);
        }
        printf("\n");
    }
}

int main() {
    int nums[] = {34, 12, 67, 45, 89, 23};
    int n = sizeof(nums) / sizeof(nums[0]);

    printf("Array: ");
    printArray(nums, n);
    printf("Max element: %d\n", findMax(nums, n));

    int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
    printf("\n2D Array:\n");
    print2D(2, 3, matrix);

    return 0;
}

/*
Array: 34 12 67 45 89 23
Max element: 89

2D Array:
   1   2   3
   4   5   6
*/

Copy and Lifetime

Arrays are not assignable as whole objects. Copy elements with a loop or use memcpy only when bytewise copying is valid for the element type and source and destination do not overlap. Use memmove when byte ranges may overlap.

A local automatic array stops existing when its block ends, so returning a pointer to it creates a dangling pointer. Return data through caller-provided storage, allocate with a documented ownership rule, or wrap the array inside a structure that can be returned by value.

  • Keep count and capacity separate when an array is used as a growable buffer.
  • Validate every external index before the first element access.
  • Do not use sizeof(pointer) as an array length.
  • Choose memcpy or memmove according to overlap requirements.
Before you move on

Array Safety Review

5 checks
  • State the element type, count, valid index range, and lifetime.
  • Initialize elements before reading them.
  • Use index < count and validate indexes received from outside the function.
  • Pass a length when an array crosses a function boundary.
  • Avoid returning pointers to automatic local arrays.

Array Boundary Failures

  • Loop uses <= count

    Stop at index < count because the last valid index is count - 1.
  • sizeof used inside pointer parameter

    Pass the element count from the scope where the object is an array.
  • Local array address returned

    Use caller storage, owned dynamic allocation, or a returnable structure.

Try this next

Exercise Array Boundaries

0 of 3 completed

  1. Accept const int values[] and count, then define behavior for an empty input.
  2. Transpose a 2 by 3 matrix into a 3 by 2 destination and trace every index.
  3. Rewrite an unsafe unsigned countdown so it terminates without wrapping.

Array Model Questions

No. An array is an object containing elements; it converts to a pointer in many expressions, but sizeof and address-of reveal the distinction.

No. Track dynamic storage with a pointer, current count, and capacity when resizing is required.

The compiler needs each row size to calculate the address produced by pointer arithmetic.

Next Step
Next Practice

Finish the concept here, then reinforce it with hands-on coding, interview prep, or a tool that matches the topic.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.