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
  6. The Brilliant But Evil for ***NEW!
  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

<ASIN:B0D6LZZQ8R>

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


Meta Releases OpenSource Podcast Generating Tool
28/11/2024

Meta has released an open source project that can be used to automatically convert a PDF file into a podcast. Meta says Notebook Llama can be considered an open-source version of Google's NotebookLM.

 [ ... ]



Looking Forward To NAO 7
03/11/2024

Introduced to the world in 2004 by its creator Bruno Maisonnier the kid-sized, autonomous humanoid robot NAO, turns 20 this year. At less than 2 ft tall, it is small in stature, but plays a big r [ ... ]


More News

espbook

 

Comments




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



Last Updated ( Monday, 17 June 2024 )