5.1.8 · D5C Programming

Question bank — Pointers — declaration, dereferencing ( - ), address-of (&)

1,425 words6 min readBack to topic

Reminders you may lean on (all built in the parent note):

  • &x = the address (house number) of x.
  • *p = the value living at the address stored in p (and it can be written to).
  • In a declaration int *p;, the * is part of the type; in an expression *p, the * is the operator.
  • *(&x) is just x — the two operators undo each other.

True or false — justify

* and & are inverse operations, so &(*p) also gives back p.
True — following p to its target with *, then asking that target's address with &, returns the same address p held. The round trip works in both directions when p points at valid memory.
int *p; immediately makes p point at a valid integer you can dereference.
False — the declaration reserves a pointer but leaves it holding a garbage address. Dereferencing it before assigning a real address (p = &x;) is undefined behaviour.
The * in int *p = &x; and the * in y = *p; mean the same thing.
False — the first * is part of the type ("p is a pointer to int"); the second is the dereference operator that fetches the value. Same symbol, two grammars.
All pointer variables on a given machine have the same size.
True — a pointer just stores an address, and addresses are a fixed width (e.g. 8 bytes on 64-bit) regardless of what type they point to. Only the interpretation of the target differs.
sizeof(p) for int *p equals sizeof(int).
False — sizeof(p) is the size of the pointer itself (e.g. 8 bytes), not the 4-byte int it points to. To get the target's size use sizeof(*p).
Passing swap(m, n) by value can still swap m and n in the caller.
False — C copies the arguments, so swap shuffles private copies and the caller's originals never change. You must pass &m, &n so the function can reach the real houses.
*p = 99; changes p to point somewhere new.
False — it does not touch p; it goes to the address inside p and writes 99 at the target. To change where p points you assign p = &something; (no *).
int *p, q; declares two pointers.
False — only p is a pointer; q is a plain int, because * binds to the individual declarator, not the whole line. Write int *p, *q; for two pointers.
A NULL pointer points to a valid memory location whose value happens to be 0.
False — NULL is a sentinel meaning "points to nowhere". Dereferencing it reads address 0, which is illegal and typically crashes; the pointer is 0, not the target's value.

Spot the error

int *p;
*p = 10;

::: p was never given an address, so it holds garbage — writing through it corrupts random memory or crashes. Fix: int x; int *p = &x; *p = 10;.

int x = 5;
int *p = x;

::: You stored the value 5 into a pointer, not an address; the compiler warns and p now "points" at address 5. Fix: int *p = &x; — take the address with &.

int a = 3, b = 4;
swap(a, b);   // void swap(int *x, int *y)

::: swap expects addresses but received values, so this is a type error (and even if it compiled, it couldn't reach the originals). Fix: swap(&a, &b);.

int *p = NULL;
printf("%d", *p);

::: Dereferencing NULL reads address 0 — an illegal access that segfaults. Guard it: if (p != NULL) printf("%d", *p);.

printf("%p", &x);

::: %p expects a void *; passing a bare int * is technically undefined and may warn. Fix: printf("%p", (void*)&x);.

int a[3] = {10,20,30};
int *p = &a[0];
printf("%d", *p + 1);

::: This computes (*p) + 1 = 11, not the next element. To read a[1]=20 you need *(p + 1) — parentheses matter because * binds tighter than +.


Why questions

Why does C need pointers at all, when we could just return new values?
Because C passes arguments by copy, a function can't otherwise change a caller's variable or share a big array without copying it — passing an address lets the function reach and edit the original.
Why does p + 1 skip sizeof(int) bytes instead of 1 byte for an int *?
Pointer arithmetic counts in elements, not bytes; the type tag tells the compiler how big one step is, so p+1 lands on the next whole int.
Why must a pointer carry a type and not just an address number?
The type tells the compiler how many bytes to read at the target and how to interpret them (int vs char vs double). An address alone can't say whether to read 1, 4, or 8 bytes.
Why is reading a declaration as "*p gives an int" the correct mental model for int *p;?
Because the declaration describes the result of dereferencing: when you later write *p, you get an int, which is exactly why p must be a pointer-to-int.
Why does *(&x) always equal x?
&x produces x's address, then * follows that address straight back to the same house — the operations cancel, leaving x itself.
Why do we cast to (void*) when printing an address with %p?
%p's contract is a generic void *; casting makes the type match, avoiding undefined behaviour and compiler warnings on mismatched pointer types.

Edge cases

What happens if you dereference an uninitialised (not NULL) pointer?
It holds an unpredictable garbage address, so *p reads or writes random memory — sometimes a silent wrong value, sometimes a crash. This is worse than NULL because it may not fail loudly.
Is *p = t; where p is int * and t is int valid?
Yes — *p names the int at the target, so assigning an int value into it is exactly right; this is how you write through a pointer.
Can two different pointers hold the same address, and what does that mean?
Yes — both then point at the same house, so *p = 5; is instantly visible through the other pointer. They are aliases for the same variable.
What is the value and behaviour of & applied to a literal, e.g. &42?
Illegal — 42 is a value, not a variable with a home, so it has no address. & only works on things that occupy memory (variables, array elements).
After int *p = &x;, does changing x directly (x = 8;) affect what *p reads?
Yes — p still points at x's house, so *p now reads 8. The pointer sees the target's current contents, not a snapshot.
If a function returns the address of its local variable, is the returned pointer safe to use?
No — the local's house is reclaimed when the function returns, so the pointer dangles and dereferencing it is undefined behaviour. See Dynamic Memory malloc free for heap memory that outlives the call.

Connections

  • Pass by Reference vs Pass by Value — the swap and "why pointers exist" traps live here.
  • Pointer Arithmetic — the p + 1 step-size and *(p+1) precedence traps.
  • Arrays in C&a[0] and array-name decay.
  • NULL and Segmentation Faults — the NULL/uninitialised dereference dangers.
  • Dynamic Memory malloc free — dangling pointers and returning local addresses.
  • Strings as char pointerschar * extends the same rules.