What is Array Name vs Pointer-Best C Language Course For Beginners

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

Array Name vs Pointer

Learn why arr++ fails, what &arr really means and how negative subscripts work. Free C programming course for embedded systems.

free c programming coursefree c tutorialc programming for embedded systemsembedded c programmingarray name vs pointer

What You Will Learn in This Lecture

  • Why the array name in C is not a pointer variable, even though it behaves like one in most expressions
  • Why arr++ is a compile error while p++ is fine
  • What &arr means and why &arr + 1 jumps further than arr + 1
  • How negative subscripts such as p[-1] work on a pointer
  • How (arr + 1)[2] is evaluated and why it equals arr[3]
  • The array-versus-pointer rules that matter in embedded C programming

Prerequisites

Complete the earlier lectures on pointer arithmetic and on pointers with one-dimensional arrays in this free C programming course. You should know that arr[i] and *(arr + i) are interchangeable.

Array Name vs Pointer: The Core Difference

In most expressions the name of an array is converted to the address of its first element, and that is why beginners treat arr as a pointer. The conversion is only an expression rule. The array itself is a block of memory with a fixed location chosen by the compiler or linker. The name arr is a label for that block, not a variable that stores an address. A pointer, by contrast, is a real object holding an address that you may change at any time.

PropertyArray int arr[5]Pointer int *p
What it isA block of 5 consecutive int objectsOne object that stores an address
sizeof5 * sizeof(int), 20 bytes hereSize of an address, 8 bytes on 64-bit Linux
Can it be reassigned?No, the name is not a modifiable lvalueYes, it can point elsewhere
++ / --Compile errorAllowed, moves by one element
Own storage for elementsYesNo, it only refers to storage somewhere else

One block, two different ways to name it

arr 1224364860
p = arr p holds the address of arr[0] → p can move, arr cannot

Why arr++ Does Not Compile

The expression arr++ means “add one to arr and store the result back into arr“. That requires arr to be a modifiable lvalue, a named location that can receive a new value. An array name is not one, because the array cannot be moved in memory. GCC reports it like this:

int main(void)
{
    int arr[3] = { 1, 2, 3 };
    arr++;
    return 0;
}
error: lvalue required as increment operand
    4 |     arr++;
      |        ^~

The fix is to copy the address into a pointer and move the pointer: int *p = arr; p++;. The array stays put, the pointer walks along it.

Array Name vs &Array: Same Address, Different Type

arr and &arr print the same numeric address, but they have different types, and the type controls pointer arithmetic.

ExpressionTypeAdding 1 moves by
arrint * (after decay)sizeof(int), one element
&arrint (*)[5], pointer to a 5-element arraysizeof(arr), the whole array
&arr[0]int *sizeof(int)

This is why assigning &arr to an int * is a type mismatch. GCC says:

int main(void)
{
    int arr[3] = { 1, 2, 3 };
    int *p;
    p = &arr;
    return *p;
}
warning: assignment to 'int *' from incompatible pointer type 'int (*)[3]' [-Wincompatible-pointer-types]

The warning is easy to dismiss because the address value is right, but the type is wrong. Increment that pointer and you skip the entire array, which is almost never what you meant. If you really want a pointer to the whole array, declare it as int (*whole)[5].

Complete Example: Array Name, Pointer and &arr

#include <stdio.h>

int main(void)
{
    int arr[5] = { 12, 24, 36, 48, 60 };
    int *p = arr;

    p++;                                  /* legal: p is a variable */
    printf("*p after p++  : %d\n", *p);

    printf("sizeof(arr)   : %zu\n", sizeof arr);
    printf("sizeof(p)     : %zu\n", sizeof p);

    int (*whole)[5] = &arr;               /* pointer to the WHOLE array */
    printf("(*whole)[2]   : %d\n", (*whole)[2]);
    printf("step of arr+1 : %td bytes\n", (char *)(arr + 1) - (char *)arr);
    printf("step of &arr+1: %td bytes\n", (char *)(&arr + 1) - (char *)&arr);

    int *q = &arr[4];
    printf("q[-1] = %d, *(q-2) = %d\n", q[-1], *(q - 2));
    printf("(arr+1)[2]    : %d\n", (arr + 1)[2]);
    return 0;
}
gcc -std=c17 -Wall -Wextra -o arr_vs_ptr arr_vs_ptr.c
./arr_vs_ptr

Expected output on 64-bit Linux:

*p after p++  : 24
sizeof(arr)   : 20
sizeof(p)     : 8
(*whole)[2]   : 36
step of arr+1 : 4 bytes
step of &arr+1: 20 bytes
q[-1] = 48, *(q-2) = 36
(arr+1)[2]    : 48

Pointer arithmetic on the same five integers

arr + 1 arr[0]arr[1]arr[2]arr[3]arr[4]
&arr + 1 arr[0]arr[1]arr[2]arr[3]arr[4] → first byte after the array

Walk through the output. p++ moved the pointer to the second element, so *p is 24. sizeof(arr) is the whole array, but sizeof(p) is only the pointer. Stepping arr + 1 advances 4 bytes, while &arr + 1 advances 20, the size of the complete array.

Warning: &arr + 1 points one past the end of the array. Forming that address is legal, but dereferencing it is undefined behaviour. The program above only subtracts addresses, which is allowed.

Negative Subscripts and the (arr + 1)[2] Form

Subscripting is defined as a[i] equals *(a + i). Nothing in that rule requires i to be non-negative or a to be an array name. If q points at arr[4], then q[-1] is *(q - 1), which is arr[3] (48), and *(q - 2) is arr[2] (36). The index just has to keep the final address inside the same array.

Likewise (arr + 1)[2] first forms a pointer to arr[1] and then subscripts it by 2, giving *(arr + 1 + 2), which is arr[3]. The same rule makes 2[arr] legal C, because *(2 + arr) is the same as *(arr + 2). It is a fun trivia question for interviews and a style crime in real code.

Where This Matters in Embedded C Programming

  • **Function parameters:** void send(int buf[]) is silently rewritten as void send(int *buf). Inside the function sizeof(buf) is the pointer size, so always pass the length separately.
  • **Lookup tables in flash:** a const array of calibration data is a fixed object; if you need to walk it, copy its address into a const int * cursor.
  • **Ring buffers:** head and tail pointers move around a fixed array. The array name never changes, the pointers do.
  • **Linker symbols:** a symbol such as _end declared as an array can be used as an address, but its value is the address itself, not something stored there.

Common Mistakes and Troubleshooting

MistakeWhat happensFix
arr++ or arr = otherCompile error about lvalueUse a separate pointer to walk the array
int *p = &arr;Type mismatch warning; p + 1 skips the whole arrayWrite int *p = arr; or int *p = &arr[0];
Using sizeof(buf) on a function parameterReturns pointer size, not array sizePass the element count as an argument
Walking a pointer past the last element and dereferencingUndefined behaviourCompare against arr + N and stop before it
Assuming arr and &arr behave alike in arithmeticOff-by-array-size bugsRemember the type decides the step size

Best Practices, Performance and Security

  • Use sizeof arr / sizeof arr[0] in the scope where the array is defined to get its length.
  • Prefer const pointers into read-only tables so the compiler flags accidental writes.
  • Pointer walking and indexing generate near-identical machine code on modern compilers, so choose whichever reads better.
  • Security: out-of-range subscripts, including negative ones, are a frequent source of buffer overflows. Validate indexes that come from outside your code.
  • Compile with -Warray-bounds (included in recent -Wall levels with optimisation) to catch constant out-of-range accesses.

Summary and Key Takeaways

  • An array name is the label of a fixed block; a pointer is a variable that holds an address.
  • Array names convert to a pointer to the first element in most expressions, except with sizeof and &.
  • arr + 1 moves one element; &arr + 1 moves the whole array.
  • p[-1] is valid when it stays inside the array, because p[i] means *(p + i).
  • Function parameters declared as arrays are really pointers.

Conclusion

Once you separate the two ideas, many output-prediction puzzles in this free embedded C course stop being tricks. Ask two questions: is this name an array or a pointer, and what is the type of the expression I am adding to? The next lecture applies the same thinking to the increment and decrement operators combined with the dereference operator.

Frequently Asked Questions

Is an array name a pointer in C?

No. An array name refers to a block of elements. In most expressions it is converted to a pointer to its first element, but it is not a variable and cannot be modified.

Why can I increment a pointer but not an array name?

A pointer is a modifiable variable. An array name is not a modifiable lvalue, so arr++ is rejected by the compiler.

What is the difference between arr and &arr?

They share the same address but have different types. arr becomes int *, so adding 1 moves one element. &arr is a pointer to the entire array, so adding 1 moves past the whole array.

Are negative array indexes legal in C?

Yes, on a pointer that points inside an array. p[-1] equals *(p - 1). The resulting address must still lie within the same array.

Why does sizeof on an array parameter return the pointer size?

Because an array parameter is adjusted to a pointer type. Pass the length explicitly or wrap the array in a struct.

What does (arr + 2)[3] evaluate to?

It forms a pointer to arr[2] and then subscripts by 3, so it is arr[5]. It must still be inside the array for the access to be valid.

Continue the Free C Tutorial

Next up: how increment, decrement and dereference interact in pointer expressions.

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *