What is Double Pointers in Embedded C- C Programming Course For Engineering Students

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

Double Pointers in Embedded C

Free embedded C course: out parameters, pointer swaps, string tables, plus a quiz and interview questions.

A double pointer in C is the answer to one practical question: how does a function change a pointer that belongs to its caller? In this final lecture of the pointer-arithmetic section of our free C programming course you will see four working double pointer patterns used daily in embedded C programming, then test yourself with a chapter quiz and a set of interview questions. It is the perfect finish to this free C tutorial on pointers.

What You Will Learn

  • Why passing a pointer by value cannot change the caller’s pointer
  • Four real patterns: cursor skip, out parameter, pointer swap and string table
  • How to document ownership of memory returned through an out parameter
  • How to test yourself with verified quiz questions
  • The pointer questions most often asked in embedded C interviews

Prerequisites

  • Pointer to pointer basics from the previous lecture
  • Functions with parameters and return values
  • Dynamic memory allocation with calloc and free, at a basic level

Why a Function Needs a Double Pointer in C

C passes every argument by value. When you call a function with a pointer, the function receives a copy of that pointer. It can follow the copy to change the data, but if it changes the copy itself, the caller never notices. To let a function change the pointer in the caller, you pass the address of that pointer, which is a double pointer.

By value versus by address
Callerowns the pointer cursor
By valuecallee gets a copy of cursormoving the copy leaves the caller’s pointer unchanged
By addresscallee gets &cursor*cursor is the caller’s pointer, so moving it changes the caller

Hands-On Program: Four Double Pointer Patterns

Save this as ptr_out.c. It shows the broken version next to the working one for the cursor skip, an out parameter that returns an allocated buffer, a pointer swap, and a walk over an array of strings.

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

static void skip_spaces_wrong(const char *cursor)
{
    while (*cursor == ' ')
        cursor++;
}

static void skip_spaces(const char **cursor)
{
    while (**cursor == ' ')
        (*cursor)++;
}

static int make_buffer(uint8_t **out, size_t n)
{
    *out = calloc(n, 1);
    return *out ? 0 : -1;
}

static void swap_ptrs(int **a, int **b)
{
    int *t = *a; *a = *b; *b = t;
}

int main(void)
{
    const char *line = "   AT+RST";

    const char *c1 = line;
    skip_spaces_wrong(c1);
    printf("by value  : starts with '%c'\n", *c1);

    const char *c2 = line;
    skip_spaces(&c2);
    printf("by address: starts with '%c'\n", *c2);

    uint8_t *buf = NULL;
    if (make_buffer(&buf, 8) == 0) {
        buf[0] = 0xA5;
        printf("buf[0]=0x%02X buf[7]=0x%02X\n", buf[0], buf[7]);
        free(buf);
    }

    int hi = 90, lo = 10;
    int *pa = &hi, *pb = &lo;
    swap_ptrs(&pa, &pb);
    printf("pa->%d pb->%d  (hi=%d lo=%d untouched)\n", *pa, *pb, hi, lo);

    const char *cmds[] = {"START", "STOP", "RESET"};
    const char **walk = cmds;
    for (size_t i = 0; i < 3; i++) {
        printf("cmd %zu: %s (first char %c)\n", i, *walk, **walk);
        walk++;
    }
    return 0;
}
gcc -std=c11 -Wall -Wextra -o ptr_out ptr_out.c
./ptr_out

Expected output:

by value  : starts with ' '
by address: starts with 'A'
buf[0]=0xA5 buf[7]=0x00
pa->10 pb->90  (hi=90 lo=10 untouched)
cmd 0: START (first char S)
cmd 1: STOP (first char S)
cmd 2: RESET (first char R)

Pattern 1: advancing the caller’s cursor

skip_spaces_wrong receives a copy of the cursor and moves the copy. When it returns, the caller’s c1 still points at the first space, which is why the first result line shows a blank. skip_spaces takes a const char **, so (*cursor)++ moves the caller’s own pointer and the second line prints A. This is exactly how tokenisers and command parsers consume input.

Pattern 2: the out parameter

make_buffer returns a status code and hands the new buffer back through uint8_t **out. Returning the status separately from the result is a very common embedded style because the status can carry an error code. After the call, buf[0] is 0xA5 and buf[7] is 0x00, because calloc zeroes the memory.

Pattern 3: swapping two pointers

swap_ptrs exchanges what two pointers point at. Afterwards pa aims at the 10 and pb aims at the 90, yet the variables hi and lo still hold their original values. Only the pointers changed.

Pattern 4: arrays of strings

An array of const char * decays to a const char ** when you pass it or walk it. In the sample, *walk is the current string and **walk is its first character, which is why RESET prints an R. Command tables in firmware are built exactly this way.

Own your allocations

Whoever receives a buffer through an out parameter must know who frees it. Document the rule in a comment, free exactly once, and set the pointer to NULL afterwards. On bare-metal targets without a heap, the same pattern works with a static pool instead of calloc.

Security and Reliability Notes

  • Validate the out pointer itself: if the caller passes NULL where a double pointer is expected, dereferencing it crashes immediately.
  • Set *out to NULL on failure so callers can safely test the result.
  • Never return the address of a local variable through an out parameter; it dies when the function returns.
  • Use const on the inner type when the callee only reads through the pointer, as in const char **.

Common Mistakes with Double Pointers in C

  • Expecting a function that receives char * to move the caller’s pointer.
  • Dereferencing the pointer-to-pointer parameter without checking it for NULL.
  • Forgetting to document who frees the memory returned through the out parameter.
  • Returning the address of a local variable through *out.
  • Using char ** where const char ** communicates the intent better.

Best Practices for Embedded C Programming

  • Return a status code and deliver the result through an out parameter.
  • Set *out to NULL on every failure path.
  • Use const correctly on the inner type.
  • Keep ownership rules in a comment next to the function prototype.
  • Prefer a struct and single pointers when a function needs many out values.

Key Takeaways

  • C is pass by value, so changing a caller’s pointer needs its address.
  • A pointer-to-pointer parameter is the standard way to write an out parameter for a pointer.
  • *pp is the caller’s pointer and **pp is the data it points to.
  • Arrays of strings are naturally walked with a const char **.
  • Test your understanding with the quiz before moving on.

Chapter Quiz: Pointer Increment, Comparison and Double Pointers

Try each question before you open the answer. Everything here was verified by compiling and running the code.

Multiple choice

  1. An int is 4 bytes on your target. If p holds the address 0x1000, what does p + 3 hold?
    A) 0x1003 B) 0x100C C) 0x1012 D) 0x1004
    Show answer

    B) 0x100C. Scaling multiplies 3 by 4 and adds 12 to the address.

  2. Which of these is a compile error?
    A) p - q (same array) B) p + 2 C) p + q D) p++
    Show answer

    C) p + q. Adding two pointers is not allowed.

  3. What type does p - q have when both are int * into one array?
    A) int B) size_t C) ptrdiff_t D) int *
    Show answer

    C) ptrdiff_t, printed with %td.

  4. x = *p++; parses as
    A) (*p)++ B) *(p++) C) ++(*p) D) (*p++) with data increment
    Show answer

    B) *(p++): postfix ++ binds tighter than *.

  5. After int v = 3; int *q = &v; int **pp = &q; **pp += 4; the value of v is
    A) 3 B) 4 C) 7 D) undefined
    Show answer

    C) 7. Two dereferences reach v, and the compound assignment adds 4.

  6. Which comparison is always well defined?
    A) < between pointers to two different arrays B) == between two compatible pointers C) >= between unrelated pointers D) none of these
    Show answer

    B) Equality between compatible pointers is always well defined.

  7. What does *ppl evaluate to when ppl is int **?
    A) an int B) an int * C) an int ** D) an address of address of address
    Show answer

    B) an int *, the inner pointer.

  8. A function must change the caller’s char *cursor. Its parameter type should be
    A) char * B) char ** C) char D) char ***
    Show answer

    B) char **, so the function can modify the caller’s pointer.

Predict the output

  1. int a[] = {5, 6, 7, 8}; int *p = a; p += 2; printf("%d %td", *p, p - a);
    Show answer

    Output: 7 2

  2. int b[] = {1, 2, 3}; int *r = b; int x = (*r)++; printf("%d %d %td", x, b[0], r - b);
    Show answer

    Output: 1 2 0

  3. int c[] = {10, 20, 30}; int *s = c; int y = *++s; printf("%d %td", y, s - c);
    Show answer

    Output: 20 1

  4. int d[] = {10, 20, 30}; int *t = d; int z = *t++; printf("%d %d", z, *t);
    Show answer

    Output: 10 20

  5. int e[5] = {0}; int *m = e, *n = e + 4; printf("%td %d", n - m, m < n);
    Show answer

    Output: 4 1

Embedded C Interview Questions on Pointers

  1. What is the difference between *p++ and (*p)++?
    Show answer

    *p++ reads the data and then moves the pointer. (*p)++ reads the data and then increments the data, leaving the pointer where it is.

  2. Why does p + 1 on an int * advance by 4 bytes?
    Show answer

    Pointer arithmetic is scaled by the size of the pointed-to type, so the compiler adds 1 * sizeof(int).

  3. What does subtracting two pointers give you?
    Show answer

    The number of elements between them as a ptrdiff_t, valid only for pointers into the same array.

  4. Can you compare two arbitrary pointers with <?
    Show answer

    No. Ordering is only defined inside one array object. Equality is safe for compatible pointers.

  5. What is a one-past-the-end pointer and what can you do with it?
    Show answer

    It is the address just after the last array element. You can form and compare it but never dereference it.

  6. What is a pointer to pointer and when do you need one?
    Show answer

    It stores the address of a pointer. You need it when a function must modify the caller’s pointer, for example an out parameter or a list head.

  7. Is int ** the same as a two-dimensional array?
    Show answer

    No. A two-dimensional array is one contiguous block with type like int [3][4]. An int ** points to an array of separate row pointers.

  8. Why must you not write *p++ = *p?
    Show answer

    It modifies and reads p without a sequence point, which is undefined behaviour.

  9. How do you print a pointer value portably?
    Show answer

    Use %p and cast the pointer to void *.

  10. Why is arithmetic on void * not portable?
    Show answer

    void has no size, so standard C cannot scale the step. GCC treats it as 1 byte as an extension.

Conclusion

Double pointers turn from a puzzle into a tool as soon as you connect them to pass by value. The four patterns in this lecture cover most of what you will write in firmware: advancing a parser cursor, returning an allocated resource, swapping targets, and walking tables of strings.

You have now finished this block of the pointer chapter in our free embedded C course. Review the quiz answers, rewrite the programs from memory, and then continue with the next topic in the course.

Frequently Asked Questions

Why does my function not change the pointer I pass in?

C passes arguments by value, so the function gets a copy of the pointer. Pass the address of the pointer, using a pointer-to-pointer parameter, to change the original.

What is an out parameter in C?

A pointer parameter that the function writes its result through. For a pointer result, the parameter is a double pointer.

How do I swap two pointers?

Write a function taking two double pointers and swap the values they point to, which are the pointers themselves.

What is const char ** used for?

It points to an array of read-only strings, such as a table of command names.

Who should free memory returned through an out parameter?

The caller usually owns it, and the function’s comment must say so. Free it once and set the pointer to NULL afterwards.

Can bare-metal code without malloc use this pattern?

Yes. The same out parameter pattern works with a static pool or a fixed array of slots.

Where can I find more free embedded C programming material?

The free C programming course on EmbeddedPathashala continues with strings, structures and memory topics.

Keep Going With the Free C Course

Pointers are the heart of embedded C. Continue through the free C programming course to build real firmware skills.

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 *