Void Pointer in C

Void Pointer in C. Void Pointer in C Tutorial. Learn Void Pointer in C
Void Pointer in C

What is Void Pointer in C ?

In C, a void pointer is a pointer that has no specific data type associated with it. It is a generic pointer type that can be used to point to objects of any type. Since the void pointer does not have a defined type, you cannot directly dereference it or perform pointer arithmetic on it. However, you can use a void pointer to store the address of any object and later cast it to a specific type before using it.

The syntax of a void pointer in C is simply void*. Here, void is the keyword indicating an empty data type, and * denotes that it is a pointer type.

You would typically use a void pointer in C when you need to create a generic or type-agnostic function or data structure. It allows you to handle different types of data using the same set of functions or structures. By using a void pointer, you can achieve flexibility and avoid code duplication.

Example of void Pointer in C

Example 1:

#include <stdio.h>

void printValue(void* ptr) {
    int* intValue = (int*)ptr;
    printf("Value: %d\n", *intValue);
}

int main() {
    int num = 10;
    void* ptr = #
    printValue(ptr);
    return 0;
}

Output:

Value: 10

Explanation:

In above example, we define a function printValue that takes a void pointer as an argument. Inside the function, we cast the void pointer to an int* and then dereference it to print the value. In the main function, we declare an integer num, assign its address to the void pointer ptr, and pass ptr to the printValue function. The void pointer allows us to pass a generic pointer to the function, and we can cast it to the appropriate type (in this case, int*) to access the value.

Example 2:

#include <stdio.h>

void printArray(void* ptr, int size, size_t elemSize) {
    for (int i = 0; i < size; i++) {
        unsigned char* bytePtr = (unsigned char*)ptr;
        printf("%d ", bytePtr[i * elemSize]);
    }
    printf("\n");
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    void* ptr = arr;
    printArray(ptr, 5, sizeof(int));
    return 0;
}

Output:

1 2 3 4 5

Explanation:

In above example, we define a function printArray that takes a void pointer, the size of the array, and the size of each element as arguments. Inside the function, we cast the void pointer to an unsigned char* to treat the data as a byte array. We then iterate over the array and print each element by accessing the appropriate byte offset based on the element size. In the main function, we declare an integer array arr and assign its address to the void pointer ptr. We pass ptr along with the size of the array and the size of each element to the printArray function. The void pointer allows us to pass the array to the function without explicitly specifying its type.

Different ways to use void pointer in C

1. Void pointers can be used as function arguments to create generic functions that can handle different types of data.

#include <stdio.h>

void printValue(void* ptr, char dataType) {
    if (dataType == 'i') {
        int* intValue = (int*)ptr;
        printf("Integer value: %d\n", *intValue);
    } else if (dataType == 'f') {
        float* floatValue = (float*)ptr;
        printf("Float value: %.2f\n", *floatValue);
    } else if (dataType == 'c') {
        char* charValue = (char*)ptr;
        printf("Character value: %c\n", *charValue);
    }
}

int main() {
    int num = 10;
    float pi = 3.14;
    char letter = 'A';
    
    void* ptr1 = #
    void* ptr2 = π
    void* ptr3 = &letter;
    
    printValue(ptr1, 'i');
    printValue(ptr2, 'f');
    printValue(ptr3, 'c');
    
    return 0;
}

Output:

Integer value: 10
Float value: 3.14
Character value: A

Explanation:

In above example, the printValue function takes a void pointer (ptr) and a character (dataType) as arguments. Based on the dataType character, the function casts the void pointer to the appropriate type (int*, float*, or char*) and prints the corresponding value. In the main function, we create three variables (num, pi, and letter) of different types and assign their addresses to void pointers (ptr1, ptr2, and ptr3). We then call the printValue function three times, passing the appropriate void pointer and data type character. This allows us to create a generic function that can handle different types of data by using void pointers.

2. Void pointers can be used in dynamic memory allocation, where a single pointer type can handle various object types.

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

void allocateAndPrint(int dataType) {
    void* ptr;
    
    if (dataType == 1) {
        int* intValue = malloc(sizeof(int));
        *intValue = 42;
        ptr = intValue;
    } else if (dataType == 2) {
        float* floatValue = malloc(sizeof(float));
        *floatValue = 3.14;
        ptr = floatValue;
    }
    
    if (ptr != NULL) {
        if (dataType == 1) {
            int* intValue = (int*)ptr;
            printf("Allocated integer: %d\n", *intValue);
        } else if (dataType == 2) {
            float* floatValue = (float*)ptr;
            printf("Allocated float: %.2f\n", *floatValue);
        }
        free(ptr);
    } else {
        printf("Invalid data type\n");
    }
}

int main() {
    allocateAndPrint(1);
    allocateAndPrint(2);
    allocateAndPrint(3);
    
    return 0;
}

Output:

Allocated integer: 42
Allocated float: 3.14
Invalid data type

Explanation:

In above example, the allocateAndPrint function takes an integer (dataType) as an argument. Based on the value of dataType, the function dynamically allocates memory for either an integer or a float value. It assigns the allocated memory’s address to a void pointer (ptr). Then, using conditional statements, it casts the void pointer to the appropriate type (int* or float*) to access and print the allocated value. Finally, the allocated memory is freed. In the main function, we call allocateAndPrint three times, passing different values for dataType to demonstrate dynamic memory allocation using void pointers.

3. Void pointers can be used in data structures like linked lists or binary trees to allow them to hold elements of different types.

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

typedef struct Node {
    void* data;
    struct Node* next;
} Node;

void printList(Node* head) {
    Node* current = head;
    while (current != NULL) {
        int* intValue = (int*)(current->data);
        printf("%d ", *intValue);
        current = current->next;
    }
    printf("\n");
}

int main() {
    Node* head = NULL;
    
    int num1 = 10;
    int num2 = 20;
    int num3 = 30;
    
    Node* node1 = malloc(sizeof(Node));
    node1->data = &num1;
    node1->next = NULL;
    
    Node* node2 = malloc(sizeof(Node));
    node2->data = &num2;
    node2->next = NULL;
    
    Node* node3 = malloc(sizeof(Node));
    node3->data = &num3;
    node3->next = NULL;
    
    head = node1;
    node1->next = node2;
    node2->next = node3;
    
    printList(head);
    
    free(node1);
    free(node2);
    free(node3);
    
    return 0;
}

Output:

10 20 30

Explanation:

In above example, we create a linked list where each node contains a void pointer (data) and a pointer to the next node (next). We define a printList function that traverses the linked list and casts the void pointer in each node to an int* to access and print the integer value. In the main function, we create three nodes (node1, node2, and node3) and assign their data pointers to the addresses of integer variables (num1, num2, and num3). We then connect the nodes to form a linked list and pass the head of the list to the printList function to display the values.

4. Void pointers can be used in callback functions, where you want to pass a generic function pointer that can be cast to a specific function type within the callback implementation.

#include <stdio.h>

typedef void (*Callback)(void*);

void intCallback(void* data) {
    int* intValue = (int*)data;
    printf("Integer value: %d\n", *intValue);
}

void floatCallback(void* data) {
    float* floatValue = (float*)data;
    printf("Float value: %.2f\n", *floatValue);
}

void performCallback(void* data, Callback callback) {
    callback(data);
}

int main() {
    int num = 10;
    float pi = 3.14;
    
    performCallback(&num, intCallback);
    performCallback(&pi, floatCallback);
    
    return 0;
}

Output:

Integer value: 10
Float value: 3.14

Explanation:

In above example, we define two callback functions: intCallback and floatCallback. Each callback function takes a void pointer (data) and casts it to the appropriate type (int* or float*) to access and print the value. We also define a Callback type, which represents a generic function pointer that takes a void pointer as an argument. The performCallback function takes a void pointer (data) and a callback function as arguments and calls the callback function, passing the data as an argument. In the main function, we pass the address of an integer variable (num) along with the intCallback function pointer to performCallback to perform an integer callback. Similarly, we pass the address of a float variable (pi) along with the floatCallback function pointer to perform a float callback.

Rules to be followed while using void pointers in C

  1. You must cast a void pointer to the appropriate type before dereferencing it.
  2. Avoid performing pointer arithmetic directly on a void pointer since it has no known size.
  3. Be cautious when using void pointers, as incorrect type casting or dereferencing can lead to runtime errors or undefined behavior.
  4. Ensure that you cast a void pointer back to the original type before accessing the actual data to maintain type safety.
  5. Do not dereference a void pointer without first ensuring it points to a valid object of the correct type.

More: