What are Dereferencing Pointers in C – C Programming For Beginners

Need Help or More Information?

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

Free C Programming Course · Chapter 8 · Pointers · Lecture 5

Dereferencing Pointers in C

Follow a pointer to its target with the indirection operator, read and write variables without using their names, then test yourself with a quiz and interview questions. Free C tutorial for embedded C programming.

*p
The object p points to
12
Quiz questions
10
Interview questions

This is the lecture where pointers start to pay off. In this part of our free C programming course you learn dereferencing pointers in C: using the indirection operator * to reach the variable whose address a pointer holds. Until now we could only store and compare addresses. Now we can use them.

If you are following this free embedded C course to master c programming for embedded systems, dereferencing is how every hardware register is read and written. At the end of this free C tutorial page you will find a quiz and interview questions to lock in the whole opening of the pointers chapter.

What You Will Learn

By the end of this lecture you can:

  • Use the indirection operator * to access the target of a pointer.
  • Read a variable and change it through a pointer.
  • Tell apart the star in a declaration from the star in an expression.
  • Explain why a pointer needs a base type, with a byte-level demonstration.
  • Recognise how dereferencing a pointer to a register controls hardware.
  • Answer common pointer interview questions on the opening topics.

Prerequisites

You should have worked through the earlier lectures in Chapter 8: memory and addresses, the address operator, declaring pointer variables, and assigning addresses to pointers. Make sure you can write int *p = &x; and explain what it means. This free C programming course builds each lecture on the one before, so a quick review of lecture 4 is worth five minutes.

Dereferencing Pointers in C

So far every program in this course has reached a variable by its name. A pointer lets you reach it indirectly, by going through its address. The operator that does this is the indirection operator, also called the dereference operator, and it is written *.

Put * in front of a pointer inside an expression and you get the object the pointer points to. If p holds the address of level, then *p is level: the same storage, not a copy.

Following a pointer
p holds an address. Applying * follows that address to the object stored there.
p (an address) apply * reach the target *p is level
int level = 87;
float gain = 4.5f;

int   *lp = &level;
float *gp = &gain;

With these declarations, *lp can be used anywhere level could be, and *gp anywhere gain could be. The base type decides what you get: *lp is an int and *gp is a float.

Two stars, two meanings

The asterisk appears in two different roles, and beginners often mix them up. Position tells you which one you are looking at.

Where you see itExampleMeaning
In a declarationint *lp;lp is a pointer to int
In a declaration with initialiserint *lp = &level;lp is a pointer, and it starts out holding the address of level
In an expression*lp = 5;Follow lp and store 5 in the target
In an expressionx = *lp;Follow lp and copy the target’s value into x
Between two operandsa * bMultiplication, unrelated to pointers
Do not read int *lp = &level; as “store the address in *lp”. The initialiser is given to lp, the pointer, not to the object *lp. In a declaration the star only says “this is a pointer”.

Reading and Writing Through a Pointer

Because *lp is the target itself, you can use it on either side of an assignment. On the right it reads the value. On the left it changes the value of the original variable.

Reading versus writing through a pointer
StatementActionEffect on level
x = *lp;Read through the pointerNone, level stays as it was
*lp = 10;Write through the pointerlevel becomes 10
*lp = *lp + 3;Read, add 3, write backlevel increases by 3
(*lp)++;Increment the targetlevel increases by 1

Two identities are worth knowing. Applying the address operator and then the indirection operator brings you back where you started: *&level is simply level. And going the other way, &*lp is just lp. The two operators cancel each other out.

Parentheses protect you

Write (*lp)++ with parentheses when you want to increment the value that lp points to. Without them, *lp++ does something different (it moves the pointer). We explain why in the pointer arithmetic lecture; for now, use the parentheses.

Dereferencing Demo Program

This program reads and writes two variables through pointers, checks the two identities, and then peeks at the bytes of a 32-bit value through a byte pointer.

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    int level = 87;
    float gain = 4.5f;
    int *lp = &level;
    float *gp = &gain;

    printf("read  via pointer: %d and %.1f\n", *lp, *gp);

    *lp = *lp + 3;
    *gp = 9.0f;
    printf("after writing through pointers: level=%d gain=%.1f\n", level, gain);

    printf("*&level = %d, &*lp == lp ? %d\n", *&level, &*lp == lp);

    uint32_t word = 0x11223344;
    uint8_t *bp = (uint8_t *)&word;
    for (int i = 0; i < 4; i++)
        printf("byte %d = 0x%02X\n", i, bp[i]);
    return 0;
}

Build and run

gcc -Wall -Wextra -o deref_demo deref_demo.c
./deref_demo

Expected output on a little-endian PC

read  via pointer: 87 and 4.5
after writing through pointers: level=90 gain=9.0
*&level = 90, &*lp == lp ? 1
byte 0 = 0x44
byte 1 = 0x33
byte 2 = 0x22
byte 3 = 0x11

The first line shows that *lp and *gp read the real values. After the writes, level and gain themselves have changed, even though we never assigned to their names. That is the whole point of indirection. The identity line confirms that the two operators cancel.

The last four lines look surprising at first. The 32-bit value 0x11223344 is stored with its least significant byte at the lowest address, so reading it byte by byte through a pointer gives 0x44 first. This is called little-endian storage, and it is used by x86 PCs and by ARM Cortex-M chips in their normal configuration. On a big-endian machine the order would be reversed. This demo also uses the fact that the language allows you to look at any object as a sequence of bytes through a byte pointer.

Why the Base Type Matters

In lecture 3 we promised to explain why a pointer needs a data type even though every pointer is the same size. The byte demo above is the answer. The pointer bp is a uint8_t *, so following it reads one byte. If the same address were held in a uint32_t *, following it would read four bytes and give you 0x11223344 instead. The address is identical; the base type decides the width and meaning of the read.

Same address, different base type
Pointer typeAddress heldWhat *ptr gives
uint8_t *address of wordOne byte: 0x44
uint16_t *address of wordTwo bytes: 0x3344
uint32_t *address of wordFour bytes: 0x11223344

(The 16-bit and 32-bit rows assume little-endian storage and a suitably aligned address, which holds for this variable.) Remember the second reason from last time too: the base type sets the step size in pointer arithmetic, which is the topic of the next stretch of this chapter.

Dereferencing in Embedded C Programming

In embedded C programming dereferencing is the mechanism behind memory-mapped input and output. A peripheral register is a memory location at a fixed address from the chip’s reference manual. To control hardware, you form a pointer to that address and dereference it:

#include <stdint.h>

/* Illustrative example for an STM32F4-style GPIOA output data register.   */
/* Do NOT run this on a PC: that address does not exist there.             */
#define GPIOA_ODR  (*(volatile uint32_t *)0x40020014u)

void led_on(void)
{
    GPIOA_ODR |= (1u << 5);      /* set bit 5: drive pin PA5 high */
}

Read the macro from the inside out. 0x40020014u is just a number. The cast (volatile uint32_t *) says “treat this number as the address of a 32-bit register”. The leading * dereferences it, so GPIOA_ODR behaves like a variable that happens to live inside the hardware. Writing to it changes real pin voltages.

From C statement to hardware
fixed address from datasheet cast to volatile uint32_t pointer dereference with * write changes the register pin level changes

Why volatile is there

A register can change without your code touching it (a status flag set by hardware), and a write can have an immediate side effect. The volatile qualifier stops the compiler from optimising away or reordering accesses, so every dereference really reaches the hardware.

Common Mistakes

MistakeConsequenceFix
Dereferencing an uninitialised pointerUndefined behaviour, random crash or corruptionAlways assign a valid address first
Dereferencing NULLSegmentation fault on Linux, possibly no fault at all on bare metalCheck if (p != NULL) first
Writing p = 5; instead of *p = 5;Changes where p points, not the targetUse the star to write the target
Writing *p++ when you meant (*p)++Pointer moves instead of value changingUse parentheses
Following a pointer to a variable that no longer existsDangling pointer, undefined behaviourDo not keep addresses of expired locals
Mixing up the declaration star and the expression starConfused codeDeclaration says “is a pointer”, expression says “follow it”

Best Practices

  • Initialise, then check, then dereference.
  • Use const on pointers that are only read through.
  • Use volatile on pointers to hardware registers.
  • Prefer named macros or struct overlays for registers rather than raw addresses scattered through code.
  • Set a pointer to NULL once its target is no longer valid.

Performance and security note

An indirect access normally costs one extra memory read compared with using the variable directly, and it can prevent some compiler optimisations because the compiler cannot always tell what the pointer may refer to. Security-wise, dereferencing a bad pointer is the root of many exploits, so treat every pointer as untrusted until you have checked it.

Key Takeaways

  • *p in an expression means the object that p points to.
  • You can read it or assign to it, so a variable can be changed without using its name.
  • The star in a declaration means “this is a pointer”; the star in an expression means “follow it”.
  • *&x is x and &*p is p.
  • The base type decides how many bytes a dereference reads.
  • Embedded code reaches hardware by dereferencing volatile pointers to fixed addresses.

Conclusion

You now hold the full pointer toolkit for this opening part of the chapter: addresses, the address operator, pointer variables, assignment, NULL and dereferencing. Together they let a function change its caller’s variables, let a driver talk to hardware, and prepare you for pointer arithmetic, arrays, strings and dynamic memory in the lectures ahead. Work through the quiz and interview questions below before moving on. They are the same style of question you will meet in real embedded interviews, and this free C programming course is built to get you ready for exactly that.

Chapter Quiz

Try each question on paper first. Open the answer only after you have committed to your choice.

Multiple choice

Q1. If p is a pointer to int, what does *p mean in an expression?

  1. The address of p
  2. The int that p points to
  3. The size of p
  4. p multiplied by something
Show answer

B. The indirection operator follows the pointer to its target.

Q2. Which declaration makes both a and b pointers to int?

  1. int *a, b;
  2. int* a, b;
  3. int *a, *b;
  4. int a, b *;
Show answer

C. Each name needs its own star. In A and B only a is a pointer.

Q3. After int n = 5; int *p = &n;, what is the value of *p?

  1. The address of n
  2. 0
  3. 5
  4. Undefined
Show answer

C. p points to n, so *p is n, which is 5.

Q4. On one platform, how do sizeof(char *) and sizeof(int *) compare?

  1. char * is smaller
  2. int * is smaller
  3. They are equal
  4. It depends on the value stored
Show answer

C. Every object pointer stores an address, so they have the same size. Only the base type differs.

Q5. Which expression is invalid?

  1. &count
  2. &table[1]
  3. &289
  4. &config.mode
Show answer

C. A constant has no storage, so it has no address. The compiler reports that an lvalue is required.

Q6. What happens if you dereference a NULL pointer?

  1. It returns 0 safely
  2. It returns the address 0
  3. Undefined behaviour
  4. The compiler fixes it
Show answer

C. Undefined behaviour. On Linux this is usually a segmentation fault, but on some microcontrollers it may silently access real memory at address 0.

Q7. Which printf format should be used to print an address portably?

  1. %d
  2. %u
  3. %f
  4. %p with a (void *) cast
Show answer

D. %p is the standard format for pointers and works on 32-bit and 64-bit systems.

Q8. What is the value of *&x for an int variable x?

  1. The address of x
  2. x
  3. 0
  4. A compile error
Show answer

B. The two operators cancel: &x gives the address and * follows it back to x.

Predict the output

P1. What does this print?

int a = 10;
int *p = &a;
*p = *p + 5;
printf("%d", a);
Show answer

15. The write through p changes a itself.

P2. What does this print?

int x = 4, y = 9;
int *p = &x;
int *q = &y;
p = q;
*p = 1;
printf("%d %d", x, y);
Show answer

4 1. After p = q, p points to y, so *p = 1 changes y. The variable x is untouched.

P3. What does this print?

int n = 7;
int *p = &n;
int *q = p;
*q = *q * 2;
printf("%d %d", n, *p);
Show answer

14 14. p and q both point to n, so the doubling is visible through every name.

P4. What does this print?

int v = 3;
int *p = &v;
printf("%d %d", *&v, *p == v);
Show answer

3 1. *&v is v, which is 3, and the comparison *p == v is true, which prints 1.

Interview Questions

These are the pointer fundamentals that interviewers for embedded and systems roles ask again and again. Practise saying the answers out loud in your own words.

1. What is a pointer in C?

A variable that stores the memory address of another object. It has its own name, type and storage, and its type (the base type) says what kind of object it points to.

2. What is the difference between &x and *p?

&x is the address-of operator and gives the address of x. *p is the indirection operator and gives the object that pointer p points to. They are opposite operations.

3. Why does a pointer need a data type if all pointers are the same size?

The type tells the compiler how many bytes to read or write when the pointer is dereferenced, how to interpret them, and how far to step in pointer arithmetic.

4. What is a wild pointer, and how do you avoid it?

A pointer that was never initialised and holds an unknown address. Avoid it by assigning a valid address or NULL at declaration, and by setting pointers to NULL when their target becomes invalid.

5. What is a NULL pointer, and is dereferencing it always a crash?

NULL is a pointer value guaranteed not to point to any valid object. Dereferencing it is undefined behaviour. On Linux it typically faults, but on many microcontrollers address 0 is valid, so there may be no fault. You must check for NULL yourself.

6. What is the size of a pointer in C?

It depends on the platform’s address width: typically 2 bytes on 8-bit AVR, 4 on 32-bit ARM Cortex-M and 8 on 64-bit PCs. All object pointers on a platform have the same size. Use sizeof to check.

7. In int *a, b; which variables are pointers?

Only a. The star binds to the name that follows it, so b is a plain int. Write int *a, *b; or declare each on its own line.

8. Why can’t you take the address of a constant or an expression?

The address operator needs an lvalue, an object with a storage location. A constant or a computed result is a temporary value with no fixed address.

9. How would you write to a memory-mapped hardware register in C?

Cast the register’s fixed address to a volatile pointer of the right width and dereference it, for example *(volatile uint32_t *)0x40020014u = value;. The volatile keyword forces every access to really happen.

10. What is the difference between a pointer and the variable it points to?

The variable holds data. The pointer holds the address of that data. Copying a pointer copies the address, so both pointers refer to the same variable; copying the variable creates a separate value.

Frequently Asked Questions

What does dereferencing a pointer mean?

It means following the address stored in the pointer to reach the object at that address, using the * operator.

What is the difference between * in a declaration and * in an expression?

In a declaration it says the variable is a pointer. In an expression it follows the pointer to its target.

Can I change a variable through a pointer?

Yes. Writing *p = value; changes the variable that p points to, even though you never used the variable’s name.

Why does reading a uint32_t byte by byte give the lowest byte first?

Most PCs and ARM Cortex-M systems are little-endian, which stores the least significant byte at the lowest address.

Why do embedded programmers use volatile with pointers?

Hardware registers can change on their own, and writes can have side effects. Volatile prevents the compiler from optimising away or reordering accesses.

Is dereferencing used in embedded C programming?

Constantly. Every read or write of a peripheral register, buffer or driver structure through a pointer is a dereference.

Where can I find more free lessons like this?

EmbeddedPathashala offers a free embedded systems course, a free Linux device drivers course, a free Linux kernel development course and this free C programming course.

Finished the Pointer Basics?

Great work. Continue with the next lectures of the free embedded C course, where pointers meet arrays, arithmetic and functions.

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 *