Skip to content

C Void Pointers — Complete Guide

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about C Void Pointers. We cover key concepts, practical examples, and best practices to help you master this topic.

C void pointers (void*) are generic pointers that can point to any data type, enabling type-agnostic functions like qsort and bsearch that work with user-defined types through callback comparators.

Why It Matters

Void pointers are the foundation of generic programming in C. The standard library's sorting, searching, and memory functions all use void pointers. Understanding them lets you write reusable utility functions that work with any data type, and understand how the C standard library implements its most useful algorithms.

Real-World Use

The qsort function can sort any array type. Memcpy copies any data type. Bsearch searches any sorted array. Callback systems pass void* context pointers. Event-driven systems use void* for user data. Thread libraries pass void* for thread arguments.

What You Will Learn

  • The void* generic pointer type
  • Casting to and from void*
  • Using qsort with custom comparators
  • Using bsearch for binary search
  • Writing type-agnostic functions with void*

Learning Path

flowchart LR
  A[Typedef] --> B[Void Pointers\nYou are here]
  B --> C[Functions]
  C --> D[File I/O]
  style B fill:#f90,color:#fff

What Is a Void Pointer?

A void pointer (void*) can hold the address of any data type. It is a generic pointer with no type information:

#include <stdio.h>

int main() {
    int i = 42;
    double d = 3.14;
    char c = 'A';
    void *vp;

    vp = &i;
    printf("Pointing to int: %d\n", *(int*)vp);

    vp = &d;
    printf("Pointing to double: %.2f\n", *(double*)vp);

    vp = &c;
    printf("Pointing to char: %c\n", *(char*)vp);

    printf("Size of void*: %zu bytes\n", sizeof(void*));
    return 0;
}

Output:

Pointing to int: 42
Pointing to double: 3.14
Pointing to char: A
Size of void*: 8 bytes

Void Pointer Rules

  • void* can hold any address
  • Cannot be dereferenced directly (compiler does not know the type size)
  • Cannot participate in pointer arithmetic (in standard C)
  • Any pointer type can be assigned to void* without casting
  • Must cast back to the original type before dereferencing

qsort -- Quick Sort

qsort sorts any array using a comparator function:

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

int compare_int(const void *a, const void *b) {
    return *(const int*)a - *(const int*)b;
}

int compare_int_desc(const void *a, const void *b) {
    return compare_int(b, a);
}

int main() {
    int numbers[] = {5, 2, 8, 1, 9, 3, 7, 4, 6};
    int n = sizeof(numbers) / sizeof(numbers[0]);

    qsort(numbers, n, sizeof(int), compare_int);
    printf("Ascending: ");
    for (int i = 0; i < n; i++) printf("%d ", numbers[i]);
    printf("\n");

    qsort(numbers, n, sizeof(int), compare_int_desc);
    printf("Descending: ");
    for (int i = 0; i < n; i++) printf("%d ", numbers[i]);
    printf("\n");
    return 0;
}

Output: Ascending: 1 2 3 4 5 6 7 8 9 / Descending: 9 8 7 6 5 4 3 2 1

How qsort Works

qsort does not know what type it is sorting. It receives a void pointer to the array, the number of elements, the size of each element, and a comparator function. It uses the element size to calculate addresses: given element index i, it accesses (char*)base + i * size.

bsearch performs a binary search on a sorted array:

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

int compare_int(const void *a, const void *b) {
    return *(int*)a - *(int*)b;
}

int main() {
    int sorted[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
    int n = sizeof(sorted) / sizeof(sorted[0]);
    int key = 50;
    int *found = bsearch(&key, sorted, n, sizeof(int), compare_int);

    if (found) printf("Found %d at index %ld\n", key, found - sorted);
    else printf("%d not found\n", key);

    key = 55;
    found = bsearch(&key, sorted, n, sizeof(int), compare_int);
    if (found) printf("Found %d\n", key);
    else printf("%d not found (requires sorted array)\n", key);
    return 0;
}

Output: Found 50 at index 4 / 55 not found (requires sorted array)

bsearch Requirements

  • The array must be sorted in ascending order
  • The comparator must be consistent with the sort order
  • Returns NULL if the key is not found

Writing Type-Agnostic Functions

You can write generic functions with void*:

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

// Generic swap
void generic_swap(void *a, void *b, size_t size) {
    char temp;
    char *ca = (char*)a;
    char *cb = (char*)b;
    for (size_t i = 0; i < size; i++) {
        temp = ca[i];
        ca[i] = cb[i];
        cb[i] = temp;
    }
}

int main() {
    int a = 5, b = 10;
    printf("Before swap: a=%d, b=%d\n", a, b);
    generic_swap(&a, &b, sizeof(int));
    printf("After swap:  a=%d, b=%d\n", a, b);
    return 0;
}

Output: Before swap: a=5, b=10 / After swap: a=10, b=5

Sorting Structs with qsort

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

typedef struct { char name[50]; int score; } Player;

int compare_by_score(const void *a, const void *b) {
    return ((const Player*)b)->score - ((const Player*)a)->score;
}

int main() {
    Player players[] = {{"Alice",95},{"Bob",82},{"Charlie",91},{"Diana",78}};
    int n = sizeof(players) / sizeof(players[0]);
    qsort(players, n, sizeof(Player), compare_by_score);
    for (int i = 0; i < n; i++)
        printf("%s: %d\n", players[i].name, players[i].score);
    return 0;
}

Common Mistakes

  1. Dereferencing void* without casting: always cast to the correct type first
  2. Pointer arithmetic on void*: cast to char* for byte-level arithmetic (GCC extension allows void* arithmetic)
  3. Not casting in comparator: the qsort comparator receives void* parameters
  4. Wrong element size in qsort: must be sizeof(element), not sizeof(pointer)
  5. Array not sorted before bsearch: results are undefined

Practice Questions

  1. What is a void pointer? A generic pointer type that can point to any data type.
  2. Why can't you dereference a void* directly? The compiler does not know the size or type of the pointed-to data.
  3. How does qsort compare elements? It calls a user-provided comparator function with void pointers.
  4. What must be true before calling bsearch? The array must be sorted in ascending order.
  5. Challenge: Write a generic function that reverses any array in place.

Mini Project: Generic Array Reversal

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

void reverse_array(void *arr, size_t n, size_t elem_size) {
    char *start = (char*)arr;
    char *end = start + (n - 1) * elem_size;
    char temp;
    while (start < end) {
        for (size_t i = 0; i < elem_size; i++) {
            temp = start[i];
            start[i] = end[i];
            end[i] = temp;
        }
        start += elem_size;
        end -= elem_size;
    }
}

int main() {
    int nums[] = {1, 2, 3, 4, 5};
    int n = sizeof(nums) / sizeof(nums[0]);
    reverse_array(nums, n, sizeof(int));
    for (int i = 0; i < n; i++) printf("%d ", nums[i]);
    printf("\n");
    return 0;
}

FAQ

Can I dereference void* directly?

No. You must cast it to the correct pointer type first because the compiler needs to know how many bytes to read.

Can I use pointer arithmetic on void*?

Not in standard C. GCC allows it as an extension (treated as char*). Cast to char* for portable byte-level arithmetic.

Why does qsort take element size?

qsort needs to know how many bytes each element occupies so it can swap them using byte-level copy operations.

Can I modify elements inside the comparator?

No. The comparator should only compare, not modify. The parameters are const void* for this reason.

What happens if bsearch finds multiple matches?

Any matching element may be returned. The C standard does not specify which one.

What is Next

Proceed to Functions to learn about function declarations, definitions, and prototypes.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro

Home Browse C