C Mastery / Pointers Part 3: void*, Pointer-to-Pointer, Pointer Arrays
Part 1 — The Core Language

Pointers Part 3: void*, Pointer-to-Pointer, Pointer Arrays

This chapter covers three pointer forms that build on the basics: void * as a generic object pointer, pointers to pointers, and arrays of pointers. These are the mechanisms behind generic containers, out-parameters for pointers, and the classic argv.

Why This Matters

void * is how C writes generic code (sorting, copying, callbacks). Pointer-to-pointer is how a function can give the caller a new pointer (for example, allocating and returning a buffer). Arrays of pointers are how argv, string tables, and many data structures are represented.

Prerequisites

Core Concept

void* — generic object pointer

void * can hold the address of any object, and any object pointer converts to and from void * without a cast (in C). It cannot be dereferenced or used in arithmetic, because void has no size.

int x = 5;
void *p = &x;         /* ok */
int *q = p;           /* ok: implicit conversion back */
/* *p is illegal */

Pointer-to-pointer

A pointer to a pointer (T **) holds the address of a T * object. It is needed when a function must change *which* pointer the caller has (e.g., allocate a buffer and hand it back).

void allocate(int **out) {
    *out = malloc(sizeof(int));
}

Arrays of pointers

An array of pointers (T *arr[N]) holds N pointers. It is a natural representation for a table of strings or a list of objects.

char *names[] = { "alice", "bob", "carol" };

Syntax

void *vp;
int **pp;
int *arr[10];        /* array of 10 int pointers */

Examples

void* for generic data

#include <stdlib.h>

/* qsort comparator: receives pointers to elements as void* */
int compare_int(const void *a, const void *b)
{
    const int *ia = a;
    const int *ib = b;
    return (*ia > *ib) - (*ia < *ib);
}

Pointer-to-pointer out-parameter

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

int make_int(int **out, int value)
{
    *out = malloc(sizeof **out);
    if (*out == NULL)
        return -1;
    **out = value;
    return 0;
}

int main(void)
{
    int *p = NULL;
    if (make_int(&p, 42) == 0) {
        printf("%d\n", *p);
        free(p);
    }
    return 0;
}

Expected output: 42.

Array of pointers (argv pattern)

#include <stdio.h>

int main(int argc, char **argv)
{
    for (int i = 0; i < argc; i++)
        printf("%s\n", argv[i]);
    return 0;
}

argv is char **, but as a parameter it is equivalent to char *argv[] — an array of pointers to the argument strings.

How It Works

void * is a pointer type with no target size, so the compiler permits conversions but forbids dereference and arithmetic. A pointer-to-pointer is an ordinary pointer whose target is itself a pointer; dereferencing once yields a pointer, twice yields the ultimate object. An array of pointers is a contiguous array where each element is a pointer (not the pointed-to data).

Variations

Multiple levels of indirection

int ***ppp;   /* pointer to pointer to pointer */

Deeper indirection is occasionally needed but should be used sparingly.

Arrays of void *

void *table[8];   /* an array of generic pointers */

Common Mistakes

user argument.

Undefined Behavior

diagnose it).

a dangling or wild pointer) is UB.

Portability

Under the Hood

A void * is the same width as other object pointers on typical systems. Pointer-to-pointer is just a pointer stored in memory; accessing through it requires two loads. Arrays of pointers store pointer-sized elements.

Practical Usage

Exercises

1. Write a generic swap that swaps two objects of any type using void * and size_t (like qsort's approach). 2. Write a function that allocates a string and returns it through a char ** output parameter. 3. Explain the difference between int *a[10] and int (*a)[10]. 4. Iterate over argv and print each argument with its index.

Deep Challenge

Implement a dynamic array of void * pointers (a "vector of pointers") that can grow using realloc, and write functions to push/pop/get. Explain how void * makes it generic and where ownership and type-safety boundaries lie.

References

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.ptr.void06
c.ptr.ptr-to-ptr06
c.ptr.array-of-ptr05