What is Pointer to Pointer in C- C Programming Course With Examples

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

Pointer to Pointer in C

Free C programming course: declaration, double dereference and the memory picture, with a tested program.

Pointer to pointer in C is the idea that a pointer can itself be pointed to. It sounds abstract, but it is the foundation of out parameters, linked lists and arrays of strings in embedded C programming. This lecture of our free C programming course draws the memory picture first, then proves it with a tested program, so you never have to guess what **ppl means again.

What You Will Learn

  • How to declare a pointer to pointer and what each asterisk means
  • How * and & change the type of an expression one level at a time
  • The difference between changing data and re-aiming a pointer
  • Why an int ** is not the same as a two-dimensional array
  • Where double pointers appear in embedded C programming

Prerequisites

  • Pointer declaration, address-of and dereference from the first lectures of this chapter
  • Pointer arithmetic, increment and comparison from the earlier lectures
  • Comfort reading C types from right to left

What Is a Pointer to Pointer in C?

A pointer is a variable that holds an address. Because it is a variable, it lives in memory and therefore has an address of its own. A pointer to pointer, also called a double pointer, is a variable that holds that second address. You declare it with two asterisks:

data_type **name;

The two stars tell the compiler that name holds the address of a pointer, and that pointer in turn holds the address of a data_type object. In practice you will almost never need more than two levels, so this lecture stays at two.

Three levels of memory (addresses are illustrative)
VariableTypeAddressValue it holdsMeaning
levelint0x50007the data
plint *0x50100x5000address of level
pplint **0x50200x5010address of pl
Following the chain from the outside in
pplholds the address ofpl
plholds the address oflevel
levelholds the value7

Reading Every Expression in the Chain

The most reliable way to understand a double pointer is to unwrap it one star at a time. Each dereference removes one level of indirection and lands you on the next object in the chain.

ExpressionTypeWhat it means
levelintthe integer itself
&levelint *address of the integer
plint *holds the address of level
*plintthe integer, reached through pl
&plint **address of the pointer pl
pplint **holds the address of pl
*pplint *the pointer pl itself
**pplintthe integer, reached through two hops
&pplint ***address of the double pointer

Notice the pattern: every * you add to an expression strips one star from its type, and every & adds one. This habit of tracking the type as you read is the single most useful debugging skill for pointer code.

Hands-On Program: Double Pointer in Action

Save this as ptr_ptr.c. It proves the relationships with equality tests rather than printing raw addresses, so the output is the same on every machine. It then writes through the double pointer and finally re-aims the inner pointer.

#include <stdio.h>

int main(void)
{
    int level = 7, other = 99;
    int *pl   = &level;
    int **ppl = &pl;

    printf("level=%d  *pl=%d  **ppl=%d\n", level, *pl, **ppl);
    printf("*ppl == pl     : %d\n", *ppl == pl);
    printf("*ppl == &level : %d\n", *ppl == &level);
    printf("ppl  == &pl    : %d\n", ppl == &pl);

    **ppl = 15;
    printf("after **ppl = 15   : level=%d\n", level);

    *ppl = &other;
    printf("after *ppl = &other: *pl=%d  level=%d\n", *pl, level);

    printf("sizeof pl == sizeof ppl : %d\n", sizeof pl == sizeof ppl);
    return 0;
}
gcc -std=c11 -Wall -Wextra -o ptr_ptr ptr_ptr.c
./ptr_ptr

Expected output:

level=7  *pl=7  **ppl=7
*ppl == pl     : 1
*ppl == &level : 1
ppl  == &pl    : 1
after **ppl = 15   : level=15
after *ppl = &other: *pl=99  level=15
sizeof pl == sizeof ppl : 1

Understanding each result

  • The first line prints 7 three times: level, *pl and **ppl are all the same integer reached by zero, one and two hops.
  • The three equality tests all print 1, confirming *ppl is the pointer pl, *ppl equals &level, and ppl is &pl.
  • **ppl = 15 writes through two levels, and level really becomes 15.
  • *ppl = &other changes the inner pointer. Now *pl reads 99 from other, while level stays 15 because nothing touched it.
  • The size test prints 1 because on a normal platform every object pointer has the same size, no matter how many stars its type has.

Two Different Things You Can Change

This lecture hides an important distinction. Through a double pointer you can change the data (**ppl = 15) or you can change where the inner pointer aims (*ppl = &other). The second ability is the reason double pointers exist. A function that receives only pl can change the data but can never redirect the caller’s pointer. A function that receives ppl can do both.

Printing real addresses

If you want to see actual address values, print them with the %p conversion and cast to void *, for example printf("%p\n", (void *)ppl);. Avoid %u for addresses, because an address does not fit in an unsigned int on 64-bit systems.

Double Pointers and Two-Dimensional Arrays

A very common misunderstanding is to treat a two-dimensional array as an int **. They are different types. A real int grid[3][4] is one contiguous block, and passing it to a function expecting int ** compiles with a warning at best and crashes at worst. A pointer to a row array, such as int (*row)[4], is the correct type for that job. An int ** fits a different layout: an array of separate row pointers, each possibly allocated on its own.

Where Double Pointers Show Up in Embedded C

  • Driver init functions that hand back a newly created handle through an out parameter.
  • Linked list code that needs to change the head pointer.
  • Arrays of strings such as command tables, where the element type is const char *.
  • Command-line style arguments, the familiar char **argv.
  • Memory pool allocators that return a block and update the caller’s free-list pointer.

Common Mistakes with Pointer to Pointer in C

  • Dereferencing a double pointer before it is initialised.
  • Passing a two-dimensional array where an int ** is expected.
  • Losing track of the type and assigning &ppl to an int *.
  • Printing addresses with %d or %u instead of %p.
  • Using a double pointer when a single pointer is enough.

Best Practices for Embedded C Programming

  • When you read pointer to pointer in C code, write the type of each expression next to it.
  • Initialise every pointer at the point of declaration, with an address or NULL.
  • Name double pointers clearly, for example pp_head or out_handle.
  • Stay at two levels of indirection unless there is a strong reason.
  • Check for NULL at each level before dereferencing.

Key Takeaways

  • A pointer to pointer in C is declared with two asterisks and holds the address of a pointer.
  • Each * removes one level of indirection and each & adds one.
  • **ppl reaches the original data, *ppl reaches the inner pointer.
  • Through a double pointer you can change either the data or the inner pointer’s target.
  • An int ** is not an int [][] array.

Conclusion

A pointer to pointer in C is only a pointer whose target happens to be another pointer. Draw the three boxes, label each with its type, and every expression becomes a short walk from one box to the next.

In the final lecture of this section we put double pointers to work inside functions, finish the chapter with a quiz and collect the interview questions you are most likely to be asked.

Frequently Asked Questions

What is a pointer to pointer in C?

It is a variable that stores the address of another pointer. It is declared with two asterisks, for example int **ppl.

What does **ppl mean?

It dereferences ppl to get the inner pointer, then dereferences that to reach the final data.

What is the difference between *ppl and **ppl?

*ppl is the inner pointer itself, while **ppl is the data the inner pointer points to.

How many levels of pointers can I have?

The language allows many, but in practice only pointer to pointer is common. More levels quickly become hard to read.

Is a two-dimensional array the same as an int **?

No. A true two-dimensional array is one contiguous block with a different type, so the two are not interchangeable.

How should I print a pointer?

Use the %p conversion with the pointer cast to void pointer. Do not use %d or %u for addresses.

Why do embedded programmers use double pointers?

To let a function return a handle or modify a caller’s pointer, for example when initialising a driver or inserting at the head of a linked list.

Learn Embedded C Programming Free

Finish the pointer section of our free C programming course with double pointers in functions, a quiz and interview questions.

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 *