CVoid Pointers

Void Pointers

Every pointer type you have seen so far — int *, char *, struct Point * — points to a specific, known kind of data. A void * is different: it is a generic pointer that can hold the address of any data type, without saying what that data actually is. It is C's way of writing code that works with "some memory, type unspecified."

C
int i = 10;
double d = 3.14;
char c = 'A';

void *anything;

anything = &i;   // OK, void* can point to anything
anything = &d;   // still OK
anything = &c;   // still OK
You cannot dereference a void pointer directly
Cast before you dereference
Because a `void *` carries no information about what it points to, the compiler has no way to know how many bytes to read or how to interpret them. `*anything` is a compile error. You must first cast the `void *` to a concrete pointer type, and it is entirely your responsibility to cast it to the correct type — the compiler cannot check this for you.

C
#include <stdio.h>

int main(void) {
    int i = 10;
    void *anything = &i;

    // printf("%d\n", *anything);       // ERROR: cannot dereference void*

    int *as_int = (int *)anything;       // cast first
    printf("%d\n", *as_int);             // OK

    return 0;
}
10
Where void pointers show up in the standard library

void * is the backbone of several familiar standard library functions, precisely because it lets them work with any data type without being rewritten for each one:

  • void *malloc(size_t size) — returns a generic block of memory; you cast it to whatever pointer type you need.

  • void *memcpy(void *dest, const void *src, size_t n) — copies raw bytes between any two buffers, regardless of type.

  • void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) — sorts an array of any element type by calling back into a comparison function you supply.

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

int main(void) {
    // malloc returns void*; it works for any type.
    int *nums = malloc(5 * sizeof(int));
    double *vals = malloc(5 * sizeof(double));

    // memcpy takes void* source/destination; it doesn't care what type
    // the buffers actually are.
    int src[3] = {1, 2, 3};
    int dst[3];
    memcpy(dst, src, sizeof(src));

    printf("dst = {%d, %d, %d}\n", dst[0], dst[1], dst[2]);

    free(nums);
    free(vals);
    return 0;
}
dst = {1, 2, 3}
A crude form of generic programming

C has no templates or generics built into the language. void * combined with an explicit size parameter (so the function knows how many bytes each element occupies, even though it does not know their type) is how the standard library fakes it: qsort can sort an array of int, struct Employee, or anything else, because it never actually looks inside the elements itself — it just moves raw bytes around and calls your comparison function to decide their order.

The price of genericity
This flexibility comes at the cost of type safety: the compiler cannot catch a mismatched cast the way it catches an assignment between two incompatible concrete pointer types. Careful, disciplined casting is the tradeoff for C-style generic code.