Pointers and Arrays in C: Decay, Indexing and Worked Examples

Separate arrays from pointers with runnable traces covering indexing, one-past boundaries, function parameters, two-dimensional arrays and declaration traps.

KnowledgeGate Team

Exam prep & CS education

Updated 24 Sep 20265 min read

a[i], *(a + i) and p[i] can reach the same element, yet an array and a pointer are not the same object. Arrays and Pointers in C: Complete Guide with Worked Examples for GATE and Interviews owns the first-pass memory model, 2D address arithmetic and *p++ precedence. The harder handoff is between expression types: indexed mutation, array-parameter decay and row-pointer stepping. Predict each output or address before reading its trace, then check who owns the storage, what type the expression has and which element the offset reaches.

Pointers and arrays in C: one memory picture

Start with int a[5] = {12, 7, 25, 4, 18};. The array owns five contiguous int elements indexed 0 through 4. In int *p = a;, a converts to a pointer to a[0], so p == &a[0].

The separate variable p stores that address and can later point elsewhere. The array name cannot be reassigned or incremented. An array is not a pointer.

Memory map of int a[5] = {12, 7, 25, 4, 18} with a and p at a[0], p + 2 at 25, and a + 5 marked as one-past.

Array indexing and pointer arithmetic: a runnable trace

c
#include <stdio.h>

int main(void) {
    int a[5] = {12, 7, 25, 4, 18};
    int *p = a;

    printf("%d %d %d\n", a[2], *(a + 2), p[2]);
    p[3] = p[0] + a[1];

    for (int i = 0; i < 5; ++i) {
        printf("%d%c", a[i], i == 4 ? '\n' : ' ');
    }
    return 0;
}

The exact output is:

Code
25 25 25
12 7 25 19 18

For a valid index, x[i] means *(x + i), so all three expressions reach a[2]. Next, p[0] is 12 and a[1] is 7, giving p[3] = 12 + 7 = 19. Since p[3] is a[3], the fourth value changes from 4 to 19.

With the diagram's four-byte int and base address 1000, p + 3 is 1000 + 3 * 4 = 1012. Pointer subtraction reports elements: &a[4] - &a[1] == 3, not 12 bytes. Forming a + 5 for comparison is valid; dereferencing it is undefined behaviour.

Array-to-pointer decay: where conversion stops

Expression

Type or meaning

Result in this example

a in int *p = a

Converts to int *

Points at a[0]

a + 2

int *

Points at 25

sizeof a

Whole-array size

Five elements

sizeof a[0]

One-int size

One element

&a

int (*)[5]

Points at the whole array

In the declaring scope, sizeof a / sizeof a[0] == 5. Under the four-byte assumption, that is 20 / 4 = 5. sizeof p is sizeof(int *).

a = p; and a++ are invalid because a is not modifiable. p = &a[2]; validly makes *p == 25. Arrays and Strings in C: Array-to-Pointer Decay, sizeof Traps and 2D Address Arithmetic owns string sentinels, a versus &a and full row-major address derivation. Function calls add a different contract: the caller owns the array, while the parameter receives only a pointer view.

Passing an array to a function needs a length

c
#include <stddef.h>
#include <stdio.h>

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

int main(void) {
    int values[] = {3, 5, 8, 13};
    size_t n = sizeof values / sizeof values[0];
    printf("%d\n", sum(values, n));
    return 0;
}

The caller gets n = 4. The running total is 0 -> 3 -> 8 -> 16 -> 29, so the output is 29. The call supplies a pointer to values[0]; no array copy occurs.

In a parameter list, const int values[] adjusts to const int *values. Thus a sizeof ratio inside sum uses the pointer size and cannot recover the count. Pass n; const prevents changes through values.

Pointer to an array versus array of pointers

Parentheses decide the declaration. int (*row)[3] is one pointer to an array of three integers; int *refs[2] is an array of two pointers.

For int matrix[2][3] = {{2, 4, 6}, {1, 3, 5}}; int (*row)[3] = matrix;, row[0][1] == 4 and *(*(row + 1) + 2) == 5. With four-byte integers and illustrative base 2000, a row is 3 * 4 = 12 bytes, so row + 1 is 2000 + 12 = 2012.

For int x = 7, y = 11; int *refs[2] = {&x, &y};, refs owns two pointer elements and *refs[1] == 11. The row pointer steps by a complete row; refs indexes separate pointers.

Matrix rows at addresses 2000 and 2012 beside an array of pointers referencing x = 7 and y = 11.

Common pointer-and-array errors and repairs

Faulty idea

Why it happens

What goes wrong

Repair

int *p; p[0] = 5;

p looks indexable

Undefined behaviour

Point it at a live object

Loop while i <= 5

Index confused with length

Access at index 5

Use i < 5

*(a + 5)

One-past can be formed

One-past dereference

Stop before it

a++

Array treated as pointer variable

Array is not modifiable

Increment a pointer

int *flat = matrix

Matrix treated as flat

Type is int (*)[3]

Use int (*row)[3]

Parameter length from sizeof

Brackets resemble an array

Parameter is a pointer

Pass the count

Returning a pointer to local int local[3] also leaves a dangling pointer. Let the caller own the destination array and pass its pointer plus length.

How exams and interviews test pointers with arrays

Three checks that come up repeatedly, with answers:

  1. int b[] = {4, 9, 16, 25}; int *q = b + 1; followed by printf("%d %d\n", *q, *(q + 2)); prints 9 25.

  2. int (*q)[4] is a pointer to an array of four integers. int *q[4] is an array of four integer pointers.

  3. A numeric value for sizeof q, when q is a pointer, needs implementation information.

Reverse the array with two valid pointers:

c
int main(void) {
    int b[5] = {2, 4, 6, 8, 10};
    int *left = b, *right = b + 4;
    while (left < right) {
        int temp = *left;
        *left++ = *right;
        *right-- = temp;
    }
    return 0;
}

The states are {10, 4, 6, 8, 2}, then {10, 8, 6, 4, 2}. The middle 6 stays unchanged.

Typical tasks cover output tracing, index translation, one-past boundaries, parameter sizeof and declarations. They sit inside CS Fundamentals for Exams & Placements; solve the three checks above from types and bounds rather than guessing a machine's pointer size.

Pointers and arrays in C: the short version and next step

  • An array owns contiguous element storage.

  • Most array expressions convert to a pointer to the first element.

  • For valid indexing, x[i] means *(x + i).

  • Pointer arithmetic scales by the pointed-to type.

  • A one-past pointer may be formed but not dereferenced.

The three traces agree: p[3] changes 4 to 19; the sum is 29; and row + 1 reaches illustrative address 2012. Storage, type and offset explain all three.

Compile both programs with warnings, redraw the first diagram, then change a[2] from 25 to 30. Predict 30 30 30 first while a[3] still becomes 19. Continue with the C Language Course: Concepts, MCQs & Coding Questions for structured concepts and practice.