student@ubuntu:~$
advanced-c Lesson 14 9 min read

Void Pointers & Generics

How C achieves type-agnostic code with void* — and why you must cast before use

Reading: Hanly & Koffman: §13.1 (pp. 706–710, general pointers only)

Quick check before you start: In Java, Object is the root of every class — you can store anything as an Object reference. C’s void* serves the same role. Read on.

Practice this topic: Void Pointers skill drill

After this lesson, you will be able to:

  • Explain what void* means and why it exists
  • Cast a void* to a typed pointer before dereferencing
  • Describe how qsort uses void* to sort any type
  • Compare void* to Java’s Object type

What Is void*?

A void* is a pointer with no type information. It holds a memory address, but the compiler does not know what lives at that address.

int x = 42;
void *ptr = &x;  // legal — any pointer can become void*

You can assign any pointer to a void* without a cast. But you cannot dereference it directly:

printf("%d\n", *ptr);  // COMPILER ERROR — what type is *ptr?

The compiler does not know whether to read 1 byte, 4 bytes, or 8 bytes. You must cast first.

Casting Before Dereference

To use the data behind a void*, cast it to the correct type:

int x = 42;
void *ptr = &x;

int *iptr = (int*)ptr;
printf("%d\n", *iptr);  // prints 42

If you cast to the wrong type, you get garbage or a crash. The compiler cannot check this for you. The responsibility is yours.

How qsort Uses void*

Look at qsort’s signature:

void qsort(void *base, size_t count, size_t size,
            int (*cmp)(const void*, const void*));

Every parameter is type-agnostic:

  • base is void* — it accepts arrays of any type
  • size tells qsort how many bytes per element
  • cmp receives const void* pointers — the comparator knows the real type

This is how one function sorts arrays of int, double, char*, or Student — the caller provides the type knowledge through the comparator.

int compare_doubles(const void *a, const void *b) {
    double x = *(const double*)a;
    double y = *(const double*)b;
    if (x < y) return -1;
    if (x > y) return 1;
    return 0;
}

double arr[] = {3.1, 1.4, 2.7};
qsort(arr, 3, sizeof(double), compare_doubles);

Same qsort, different comparator, different type. That is generic programming in C.

void* Is Java’s Object

Java C
Object void*
Downcast with (String)obj Cast with (char*)ptr
ClassCastException at runtime Undefined behavior (no check)
Generics add compile-time safety No equivalent — you just cast correctly

Java generics are syntactic sugar over Object with compile-time checks. C gives you the raw mechanism without the safety net.

Rules for void*

  1. Any pointer converts to void* implicitly — no cast needed
  2. You must cast void* before dereferencing — the compiler does not know the type
  3. You cannot do pointer arithmetic on void* — the compiler does not know the element size
  4. Casting to the wrong type is undefined behavior — there is no runtime check

Check Your Understanding
Why must you cast a void* before dereferencing it?
ABecause void* pointers are read-only
BBecause void* is always NULL
CBecause the compiler does not know the size or type of the data, so it cannot generate the correct memory access
DBecause void* requires special hardware instructions
Answer: C. A void* carries no type information. The compiler needs to know whether to read 1 byte (char), 4 bytes (int), 8 bytes (double), or something else. Casting tells the compiler the actual type so it can generate the correct dereference instruction.

What Comes Next

You understand void pointers and function pointers separately. Next, you will combine them to build generic data structures — containers that store any type, just like Java’s ArrayList<T>.

Next: Generic Data Structures