Deep C Dives: Into the Void
Written by Mike James   
Tuesday, 11 June 2024
Article Index
Deep C Dives: Into the Void
The Void Type

The Void Type

Even in the days of Algol 68, the void keyword’s meaning had expanded beyond just stating that a function didn’t return a value – it had become a type. Rather than being a type that breaks the rules, as in “null and void”, void is a type that is anything. This is a little confusing for the beginner attempting to make sense of void used in a function – does it mean it returns a void type? Only if nothing is the void type and this isn’t a good conclusion to draw.

What does void being any type actually mean?

You can’t declare a variable of type “anything” because, in C at least, all variables have to have a known type so that the compiler can allocate storage for them. When you use:

int myVar;

the compiler usually allocates four bytes to the variable, although this is machine-dependent. What would the compiler allocate if you wrote:

void myVar; 

The only sense that a void type can exist is if we can allocate a sensible amount of storage for it. The solution is to use void pointers. As explained in Dive 10, a pointer is a variable that stores the address of another item of data. A pointer always takes the same amount of memory to store, even though it is system-dependent. Of course, what the pointer references can be anything, so:

void *myPointer;

creates a pointer that can reference anything.

How best to think of a void pointer?

At the lowest level you can think of a void pointer as referencing the start of a block of memory, but this view has a problem. To access parts of the block of memory you would need to do pointer arithmetic. For example, to access the 20th element of the block you might try:

myValue=*(myVoidPointer+20);

The idea is that the value stored at the address given by myVoidPointer plus 20 is to be retrieved and stored in myValue. The problem here is we don’t know what the basic unit of allocation is. In other cases, pointer arithmetic works in units of the basic type being referenced.

For example:

int *myIntPointer;
. . .
myValue=*(myIntPointer+20);

In this case the addition is equivalent to 20*sizeof(int) and we are retrieving the 20th integer stored in the block of memory as if the block was an array.

So what does myVoidPointer+20 mean?

By analogy it has to be: myVoidPointer+20*sizeof(void)

and the only sensible value for sizeof(void) is 0.

This doesn’t work and from C99 onwards void pointer arithmetic is not allowed. Earlier standards were confused on the issue. Some use: sizeof(void)=sizeof(char)

which sort of makes practical sense, but if this is what you want you could simply cast the pointer to char:

(char*)myVoidPointer+20

So don’t do void pointer arithmetic. It doesn’t make any sense, even if your compiler allows it.

If you can’t use a void pointer to manipulate memory, what use is it?

Its only use is when you don’t know what the type of the data will be or when you need to change its type on the fly, as for example in:

void *get(void *p, int type, int index)
{
switch (type) { case 1: return (int *)p + index; case 2: return (char *)p + index; } return NULL; }

You can see that the type of the pointer passed is resolved at runtime using the type parameter. Without this approach you would need a get function for each type.

Notice that we are making use of the rule that any pointer type can be cast to a void pointer and this is done automatically. So when we call:

int array1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *element = get(array1, 1, 5);

the int array1 is automatically upcast to void. If you prefer you can make this explicit:

int *element = get((void*)array1, 1, 5);

In short a void pointer is never used to process data, only to pass it around when its type isn’t determined at compile time.

Final Word

void is a useful construction, but it isn’t just one thing. It is an indicator that a function doesn’t return a result and it can be used to specify that a function takes no parameters. As a type it is useless for processing data, but excellent for passing data of unknown type to a function, or in any other situation where the data type isn’t known until runtime.

Deep C Dives
Adventures in C

By Mike James

Cdive360

Buy from Amazon.

Contents

Preface
Prolog C
Dive

  1. All You Need Are Bits
  2. These aren’t the types you’re looking for
  3. Type Casting
  4. Expressions
  5. Bits and More Bits
        Extract:
    Bits! ***NEW!
  6. The Brilliant But Evil for 
  7. Into the Void 
  8. Blocks, Stacks and Locals
  9. Static Storage
  10. Pointers
  11. The Array and Pointer Arithmetic
  12. Heap, The Third Memory Allocation
  13. First Class Functions
        Extract:
    First Class Functions
  14. Structs and Objects
  15. The Union
  16. Undefined Behavior
  17. Exceptions and the Long Jump

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

Banner


Hone Your SQL Skills With The Premier League
02/05/2025

Introducing sqlpremierleague,  another sql playground, but with puzzles specific to sports.



Linux Foundation Mentorship Program
15/04/2025

Applications are now open for Summer 2025 participation in the Linux Foundation Mentorship Program, a program that provides structured guidance and opportunities for newcomers to contribute to th [ ... ]


More News

espbook

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Monday, 17 June 2024 )