Skip to content

Understanding the C Language void Keyword

Published: at 12:00 PM

In the C programming language, the void keyword is used in three different contexts: function definition, variable declaration, and generic data storage.

Table of Content

Function Definition

When defining a function, using the void keyword indicates the function does not return a value.

// Example using void return value
void clearScreen();

A function with a void return value is not required to use return in the function body. For instance, if a function performs an action but doesn’t produce a result for further computation, it’s declared with a void return type and the function simply returns when the end of function body is reached.

void clearScreen() {
  system("clear");  // For Unix/Linux/MacOS
}

If the function body logic has a condition to return before the end of the function body, then return; is used.

Function Argument List

In ANSI C, a function declaration without parameters, empty argument list, implicitly allows any number of arguments. Specifying void as a parameter explicitly indicates no arguments.

void clearScreen(void);

The use of void in this context emphasizes clarity, ensuring the function’s intent is explicit.

Variable Declaration

When declaring variables, the only valid usage of void is as a pointer.

  int counter = 0;
  void *pCnt = NULL;
  pCnt = &counter;

Casting is necessary to access the value pCnt points to:

  fprintf(stdout, "The count is: %i\n", (*(int*)pCnt));

Generic Data Storage

A void pointer can hold the address of any data type. This makes it extremely versatile for generic programming. This flexibility allows functions and structures to operate on data without knowing its type in advance.

The Real Power

The true strength of void pointers lies in their ability to enable the creation of reusable sorting and searching algorithms that work with different data types.

Heap Algorithm

A heap is used to quickly retrieve the maximum or minimum value. To achieve this, a heap algorithm is typically used to store data in an array and uses a binary tree structure to arrange the order of the data.

The heap algorithm abstracts the complexity of data storage and organization while providing functions for inserting and extracting data, as well as controlling the sorting process.

The following example demonstrates using a heap algorithm (#include “heap.h”) to sort parcels by their importance. Such an algorithm is useful for businesses that prioritize product shipments based on an importance factor.

// ---- test.c ----

#include <stdio.h>
#include "heap.h"

typedef struct Parcel_ {
  int importance;
  int weight;
} Parcel;

...

The data to be sorted by the heap is stored in a struct. The Heap algorithm typically calls a compare function to control whether the data is sorted in ascending (min-heap) or descending (max-heap) order.

static int compare(const void *p1, const void *p2) {

  Parcel *parcel1, *parcel2;
  parcel1 = (Parcel *)p1;
  parcel2 = (Parcel *)p2;

  if (parcel1->importance < parcel2->importance) return -1;
  else if (parcel1->importance > parcel2->importance) return 1;
  else  return 0;
}

Note the use of void * in the argument list and how casting is used to dereference the void * to a Parcel struct. This allows the programmer to control whether the data is sorted as a min- or max-heap.

Full example test code:

// ---- test.c ----

#include <stdio.h>
#include "heap.h"

typedef struct Parcel_ {
  int importance;
  int weight;
} Parcel;

static int compare(const void *p1, const void *p2) {

  Parcel *parcel1, *parcel2;
  parcel1 = (Parcel *)p1;
  parcel2 = (Parcel *)p2;

  if (parcel1->importance < parcel2->importance) return -1;
  else if (parcel1->importance > parcel2->importance) return 1;
  else  return 0;
}

static void destroy(void *tree) {
  return;
}

int main(void) {

  int idx;

  Heap thePostOffice;

  heap_init(&thePostOffice, compare, destroy);

  Parcel parcels[8];

  for (idx = 0; idx < 8; idx++) {
    parcels[idx].importance = idx;
    parcels[idx].weight = idx * 10;
    heap_insert(&thePostOffice, &parcels[idx]);
  }

  for (idx = 0; idx < 8; idx++) {
    Parcel *data;
    int Rt = heap_extract(&thePostOffice, (void**)&data);
    fprintf(stdout, "The parcel's importance is: %i, and its weight is: %d\n", data->importance, data->weight);
  }

  heap_destroy(&thePostOffice);

  return 0;
}

Conclusion

The void keyword in C is much more than a placeholder. It plays a critical role in defining functions, enabling generic programming, and fostering encapsulation. Whether you’re using it for functions without return values, pointers to arbitrary data, or advanced library designs, understanding void will help you write more flexible and powerful C programs.


Previous Post
A Hybrid Project Plan