Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A pointer to a pointer (often called a double pointer) is a variable that stores the memory address of another pointer, rather than the address of a standard data value. It introduces multiple levels of indirection, requiring the compiler to perform sequential memory lookups to resolve the final underlying value.

Syntax

Declaration requires the use of two consecutive asterisk (*) operators, indicating two levels of indirection.
data_type **pointer_name;

Mechanics and Initialization

To initialize a pointer to a pointer, you must assign it the memory address of a single pointer using the address-of (&) operator. The base data types of all variables in the indirection chain must match.
int value = 42;
int *ptr = &value;       // ptr stores the memory address of 'value'
int **d_ptr = &ptr;      // d_ptr stores the memory address of 'ptr'

Memory Layout

Understanding double pointers requires distinguishing between a variable’s own memory address and the value stored at that address. Consider the following hypothetical memory map based on the initialization above:
VariableOwn Memory AddressStored ValuePoints To
value0x100042N/A
ptr0x20000x1000value
d_ptr0x30000x2000ptr

Dereferencing

The indirection operator (*) is used to traverse the pointer chain. The number of asterisks applied determines the depth of the memory lookup.
  • d_ptr (Zero dereferences): Evaluates to the value stored inside d_ptr, which is the memory address of ptr (0x2000).
  • *d_ptr (Single dereference): Evaluates to the value stored at the address held by d_ptr. It resolves to the contents of ptr, which is the memory address of value (0x1000).
  • **d_ptr (Double dereference): Evaluates to the value stored at the address held by *d_ptr. It resolves to the actual integer stored in value (42).

Common Use Cases

Double pointers are required for specific memory management and architectural patterns in C:
  • Modifying a Pointer via Function (Pass-by-Reference): C passes arguments by value. To modify a pointer itself from within a function (such as dynamically allocating memory for it via malloc), you must pass the address of the pointer (**) so the function can dereference and update the original pointer’s target address.
  • Dynamically Allocating 2D Arrays: A double pointer is used to create an array of pointers, where each pointer element subsequently points to a dynamically allocated 1D array. This allows for non-contiguous memory allocation and jagged arrays (rows of varying lengths).
  • Handling Arrays of Strings: Because a string in C is represented as a character pointer (char *), an array of strings is represented as a pointer to a character pointer (char **). This is standard in command-line argument parsing, as seen in the entry point signature: int main(int argc, char **argv).

Code Visualization

The following snippet demonstrates the relationship between the variables, their addresses, and the dereferencing process.
#include <stdio.h>

int main() {
    int val = 100;
    int *ptr = &val;
    int **d_ptr = &ptr;

    // 1. Memory Addresses (using %p for pointer formatting)
    printf("Address of val:   %p\n", (void*)&val);
    printf("Address of ptr:   %p\n", (void*)&ptr);
    printf("Address of d_ptr: %p\n\n", (void*)&d_ptr);

    // 2. Stored Values
    printf("Value of ptr:     %p\n", (void*)ptr);     // Matches &val
    printf("Value of d_ptr:   %p\n\n", (void*)d_ptr); // Matches &ptr

    // 3. Dereferencing
    printf("Direct val:       %d\n", val);            // 100
    printf("*ptr:             %d\n", *ptr);           // 100
    printf("**d_ptr:          %d\n", **d_ptr);        // 100

    return 0;
}

N-Level Indirection

C supports multiple levels of indirection (e.g., int ***t_ptr = &d_ptr;). However, pointer chaining is not indefinite. The C standard (C99/C11 section 5.2.4.1) mandates that conforming implementations support a minimum of 12 pointer, array, and function declarators modifying a base type in a declaration. Furthermore, all compilers have finite parsing limits. Each additional level of indirection represents another distinct memory address lookup required by the CPU to reach the base value.
Master C with Deep Grasping Methodology!Learn More