Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

An int * can access elements of an int array because C defines array indexing in terms of pointer arithmetic: a[i] means *(a + i). The notation does not make an array and a pointer the same thing: the array is an object with a fixed extent and array type, while a pointer is a separate object that can hold an address. Understanding that distinction explains both how traversal works and where it must stop.

Why can an int * access an array in C?

In most expressions, an array expression is converted to a pointer to its first element. For example, in an expression using a, an int a[4] array typically becomes an int * pointing to a[0]. The C indexing rule then makes a[i] equivalent to *(a + i): start at the first element, advance by i elements, and read or modify the value there.

int a[4] = {10, 20, 30, 40};
int first = a[0];       /* same element as *(a + 0) */
int third = *(a + 2);   /* same element as a[2] */

This is the language explanation for the familiar syntax, not permission to treat any pointer as an arbitrarily long array. The pointer must refer into the relevant array object, and the access must remain within its bounds. The GNU C Language Manual explains the relationship between pointers and arrays.

Arrays and pointers are different objects

An array declaration reserves storage for a fixed number of elements as one array object. A pointer declaration reserves storage for an address. An array expression often converts to a pointer to its first element, but the conversion does not erase the array’s type or turn the array object into a pointer variable.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

That distinction is visible with sizeof. Where a is still an array object, sizeof a is the size in bytes of the whole array. Dividing by the size of one element gives its element count:

int a[4];
size_t count = sizeof a / sizeof a[0];

But a function parameter declared with array notation is adjusted to a pointer parameter. Inside the function, sizeof values is therefore the size of a pointer, not the size of the caller’s array. Pass the element count separately or keep it in another explicit part of the interface.

What pointer arithmetic does

Pointer arithmetic is scaled by the pointed-to type. If p has type int *, then p + 1 points to the next int element, not to the next byte. The compiler applies the element size as part of the operation; the C program expresses the distance in elements.

For a pointer to T, adding n advances by n elements of type T. Do not add a byte count such as sizeof a to an int * as though the addition itself were byte-based: that would advance by that many int elements. For the distinction between element counts and byte counts, see SEI CERT ARR39-C.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How to traverse a known-length array safely

A pointer does not carry the runtime length of the array it points into. An interface that accepts a pointer should also make the valid count available. This common pattern uses an index and stops before reaching that count:

#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;
}

The function assumes values points to at least count valid int elements. A non-null pointer by itself does not establish that range. The caller and function contract must preserve the count and ensure it matches the available storage.

Rank #3

Pointer iteration expresses the same access another way. The endpoint may point one position past the last element, but the loop dereferences only pointers before it:

const int *end = values + count;
for (const int *p = values; p != end; ++p) {
    /* use *p */
}

Here too, the assumption is that values starts a valid array range of at least count elements. The one-past pointer is an endpoint, not an element to read.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Where valid pointer arithmetic stops

For an array object, a pointer may refer to one of its elements or to the position immediately after its last element. The one-past pointer is useful for comparison and loop termination, but it must not be dereferenced. Forming or using a pointer beyond that permitted range is undefined behavior. SEI CERT states: “Pointer arithmetic must be performed only on pointers that reference elements of array objects.” Its guidance is in ARR37-C; related invalid-index cases are covered by ARR30-C.

Do not infer an array merely because unrelated objects appear next to one another in memory. Separate structure members are not an array, and their layout is not a portable contract for pointer traversal. Pointer arithmetic is defined relative to an array object, not to whatever storage happens to follow an object on a particular build.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Multidimensional arrays have bounds at each dimension

A declaration such as int a[4][5] is an array of four row arrays; each row contains five int elements. In an expression, a converts to a pointer to its first row, whose type is an array of five int. Thus a[r][c] indexes a row and then an element within that row.

The column bound remains five for each row. An index such as a[0][5] is out of bounds for that row, even if an address calculation seems to land where another row’s storage exists. Staying within the total contiguous storage does not make crossing a subarray’s boundary through an invalid index valid C. See SEI CERT ARR30-C.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choosing indexing or pointer notation in embedded C

For valid ranges, values[i] and *(values + i) access the same element. Choose notation that makes the extent and stopping condition easiest for the next reader to verify. An index loop puts the count directly in the condition; a pointer loop can make a start-to-end traversal apparent. Neither notation removes the need for a valid range, and the language rules cited here do not establish a general performance advantage for one form.

These are ordinary C rules, not special embedded-only semantics. The UPenn Embedded Systems Handbook C primer provides embedded-oriented context for C pointers and arrays. Target-specific memory maps or hardware registers may introduce additional constraints, but they do not turn standard array bounds into an unbounded traversal rule.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.