Skip to content
VLSI Mentor

C Part I · Module 7 · Pointers

Pointers and Arrays: The Real Relationship

Two chapters have deferred the same explanation. Chapter 5.3 warned that passing an array to a function would appear to break the rule that arguments are copied, and insisted it does not. Chapter 6.1 proved that an array stores no length anywhere, then declined to pass one to a function at all, saying the reason could be explained rather than asserted once pointers existed. Both were waiting for one rule, which is short enough to state in a single sentence and is almost always summarised wrongly. The usual summary — that an array is a pointer — is false, and you can disprove it with sizeof in ten seconds. What is true is more specific and more useful, and it comes with a list of exactly three places where it does not apply.

Intermediate18 min readCPart IFundamentalsPointersArrays

Part I · Module 7 · Chapter 7.6 · Pointers and Arrays: The Real Relationship

1. They Are Not the Same Thing

Start by killing the summary you will hear most often, because everything else is clearer once it is gone.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    int arr[4] = {10, 20, 30, 40};
    int *p = arr;

    printf("sizeof arr  %zu\n", sizeof arr);
    printf("sizeof p    %zu\n", sizeof p);
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sizeof arr  16
sizeof p    8

Sixteen bytes and eight bytes. An array object and a pointer object have different sizes, so whatever the relationship between them is, being the same thing is not it.

Chapter 6.1 established what arr is: four int objects laid end to end, occupying sixteen bytes, with no stored length and nothing else. p is one object holding an address, occupying eight. Those are two different kinds of thing, and the compiler treats them as two different types.

2. The Rule

So why does int *p = arr; compile at all, with no cast and no complaint?

In almost every expression, an array is converted to a pointer to its first element.

That is the rule. The array itself does not change; what happens is that using its name in an expression yields a pointer value instead of the array.

Azvya Education Pvt. Ltd.VLSI Mentor
the conversion, observed
#include <stdio.h>

int main(void)
{
    int arr[4] = {10, 20, 30, 40};
    int *p = arr;

    printf("arr == &arr[0] ?  %d\n", arr == &arr[0]);
    printf("p   == &arr[0] ?  %d\n", p == &arr[0]);
    printf("*arr              %d\n", *arr);
    printf("*p                %d\n", *p);
    printf("arr[0]            %d\n", arr[0]);

    return 0;
}

Output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
arr == &arr[0] ?  1
p   == &arr[0] ?  1
*arr              10
*p                10
arr[0]            10

arr in the comparison was converted to &arr[0], so the comparison is true. *arr dereferences that same converted pointer and gives element 0. And int *p = arr; works because the initialiser is an expression, the conversion applies, and what is assigned is a pointer — exactly the type p wants.

The phrase that captures it:

An array name used as a value decays to a pointer to its first element.

Decay is the word you will hear. It is slightly unfortunate — nothing degrades — but it is universal, so it is worth knowing.

3. The Three Places It Does Not Happen

The rule says almost every expression. The exceptions are short, and they are where every confusion about arrays and pointers lives.

Five stages describing when an array name is converted to a pointer. First, an array name appears in an expression and the question is what its value is. Second, if it is the operand of sizeof there is no conversion and you get the size of the whole array. Third, if it is the operand of the address-of operator there is no conversion and you get a pointer to the whole array. Fourth, anywhere else it is converted to a pointer to the first element. Fifth, that pointer is a value rather than an object, so an array name cannot be assigned to.When is an array name converted?01AN ARRAY NAME APPEARS IN AN EXPRESSIONthe question is what its value is02OPERAND OF SIZEOF — NO CONVERSIONyou get the size of the whole array03OPERAND OF & — NO CONVERSIONyou get a pointer to the whole array04ANYWHERE ELSE — CONVERTEDto a pointer to the first element05THAT POINTER IS A VALUE, NOT AN OBJECTso an array name cannot be assigned to
Figure 1 — the decision to run on any expression containing an array name. The two exceptions come first because they are the ones that surprise people: as the operand of sizeof the array is not converted, so you get the size of the whole object, and as the operand of the address-of operator you get a pointer to the entire array rather than to its first element. Everywhere else the name yields a pointer to the first element — and that pointer is a value, not an object, which is why an array name cannot be assigned to.

sizeof

sizeof arr is 16, not 8. The array is not converted, so you get the size of the whole object — which is exactly what made Chapter 6.1's sizeof arr / sizeof arr[0] idiom work.

This is why that idiom works, and it is also why it will stop working in the next chapter. Keep the connection in mind: the idiom depends on the array not being converted, and there is a situation where the array has already been converted before you get to write sizeof.

&

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    printf("sizeof *(&arr)    %zu\n", sizeof *(&arr));
    printf("sizeof *(&arr[0]) %zu\n", sizeof *(&arr[0]));
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sizeof *(&arr)    16
sizeof *(&arr[0]) 4

&arr and &arr[0] hold the same address and are different types. Dereferencing the first gives something 16 bytes long — the whole array. Dereferencing the second gives 4 bytes — one element.

This is Chapter 7.3's point in its purest form: same address, different type, different meaning. &arr is a pointer to an array of 4 int, which is a type this curriculum does not otherwise need, and it matters mainly because it explains why &arr is not interchangeable with arr even though they print the same number.

The third exception

A string literal used to initialise a character array is not converted either — the literal's characters are copied into the array rather than the array being made to point at them. That case belongs to Chapter 8.2, where strings exist; it is listed here so the set is complete.

4. An Array Name Is Not an Object

Step five of Figure 1 is the one readers test:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    int arr[4] = {1, 2, 3, 4};
    int *p = arr;

    p = arr;          /* fine — assigning a pointer value to a pointer object */
    arr = p;          /* not valid C */

The conversion produces a value. A value is not something you can assign to — Chapter 7.2 §3 made the same distinction for *p, in the other direction. arr in an expression yields a pointer, and you can no more assign to it than you can write a + b = 5.

That is also a useful way to remember which is which: a pointer can be made to point somewhere else; an array cannot be made to be somewhere else. The array is sixteen bytes at a fixed place for its whole lifetime, which Chapter 6.1 established when it said an array's size is part of its type.

5. Where Indexing Comes From

Once you know the conversion rule, one more fact follows and will be made exact in the next chapter but one.

arr[0] and *arr gave the same value in §2, and that is not a coincidence. Indexing in C is defined in terms of pointers: a[i] means take the pointer, move it along by i elements, and dereference. The conversion is the first half of that; moving along by i elements is Chapter 7.8's subject, and it is where the index-as-distance idea from Chapter 6.1 becomes literal arithmetic.

Two consequences are worth previewing, because they are the kind of thing that looks like a trick until the rule is in place: indexing works on any pointer, not just on array names, and it is defined symmetrically in a way that permits some genuinely strange-looking but legal expressions. Chapter 7.8 covers both.

6. The Misconception, Stated and Dismissed

7. What You Should Be Able to Say Now

  1. An array object and a pointer object are different — sizeof says 16 and 8.
  2. In almost every expression, an array converts to a pointer to its first element.
  3. That is why int *p = arr; compiles and why arr == &arr[0] is 1.
  4. The conversion is called decay, and nothing actually degrades.
  5. sizeof does not convert — which is exactly what makes Chapter 6.1's count idiom work.
  6. & does not convert — &arr is a pointer to the whole array, and sizeof *(&arr) is 16.
  7. &arr and &arr[0] hold the same address and have different types — Chapter 7.3's point exactly.
  8. A string literal initialising a char array is the third exception, and belongs to Chapter 8.2.
  9. The conversion yields a value, so arr = p; is not valid C.
  10. Indexing is defined in terms of pointers, and Chapter 7.8 completes the definition.

8. Exercises

Exercise 1 — predict every size. For double d[6]; with double at 8 bytes and pointers at 8, give each value.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sizeof d        sizeof d[0]        sizeof &d[0]        sizeof *(&d)

Exercise 2 — converted or not? For each expression, say whether the array name is converted, and what the expression's value or size is, given int a[5];.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sizeof a        a == &a[0]        *a        &a        a + 0        sizeof a[0]

Exercise 3 — explain the equality. a == &a[0] is 1. Explain why, in terms of the conversion rule, without using the word decay.

Exercise 4 — two addresses, two types. &a and &a[0] print the same number. Say what each one's type is in words, give sizeof *(&a) and sizeof *(&a[0]) for int a[5], and say which one you would almost never need.

Exercise 5 — find the invalid line. Say which of these are valid C and why, given int a[3]; int b[3]; int *p;.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
p = a;
a = p;
a = b;
p = &a[1];
*a = 7;

Exercise 6 — correct the claim. A colleague writes a comment saying /* arrays are just pointers in C */. Write a two-sentence replacement comment that is accurate, and give the single-line program you would show them.

Exercise 7 — predict the idiom. For int a[8];, give the value of sizeof a / sizeof a[0] and explain which exception from §3 makes it work. Then say what would happen to that expression if a had already been converted to a pointer.

Exercise 8 — connect it forward. Chapter 6.1 said an array stores no length and Chapter 5.3 said arrays would look like an exception to pass-by-value. Using only this chapter's rule, predict what a function declared void f(int a[]) actually receives, and what sizeof a inside it will be.

9. Summary

  • An array is not a pointer. sizeof gives 16 and 8 for the same data.
  • In almost every expression an array converts to a pointer to its first element — informally, it decays.
  • That explains int *p = arr;, arr == &arr[0], and *arr being arr[0].
  • Three exceptions: sizeof, &, and a string literal initialising a char array (Chapter 8.2).
  • sizeof not converting is why Chapter 6.1's count idiom works.
  • &arr is a pointer to the whole array — same address as &arr[0], different type.
  • The conversion yields a value, so an array name cannot be assigned to.
  • Indexing is defined in terms of pointers, completed in Chapter 7.8.

Exercise 8 asked you to predict something this chapter has given you everything to derive. If an array converts to a pointer in almost every expression, and a function call is an expression, then what a function receives when you hand it an array is not an array at all. Chapter 6.1 proved there is nowhere for a length to be stored; this chapter explains what is actually passed; and together they produce a consequence that catches every C programmer exactly once, which the next chapter demonstrates with the compiler's own warning message describing it.

Standards & specifications

Governing standard
ISO/IEC 9899 (the C language standard)(opens ISO/IEC (JTC 1/SC 22/WG 14) in a new tab)

Defines the C language itself — the type system, expression semantics, object model, storage duration and the behaviour categories a conforming implementation must honour. It is a FAMILY of editions (C89/C90, C99, C11, C17, C23) and this curriculum names the edition wherever a claim depends on one. It does not define any foreign-function interface to a hardware description language, and it does not define platform behaviour: what a memory-mapped access does on real silicon is the implementation's and the device's contract, not this standard's. Working drafts published by WG14 are freely available and are what most engineers read day to day; a draft is not the published standard.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the C Programming curriculum.