From Void to Integer

You can’t just slap a data type upon a variable declared as void. No, a void type can be assigned a pointer. But even then, you can’t just typecast that pointer as an int and get away with it.

From last week’s Lesson, I devised the code below to properly allocate void variable a as storage for an integer. But the code generates a few mismatch warnings and crashes when the program runs:

2024_04_20-Lesson-c.c

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

int main()
{
    void *a;

    a = malloc(sizeof(int));

    printf("Enter an integer: ");
    scanf("%d",(int)a);
    printf("You entered %d\n",(int)a);

    return 0;
}

Starting with the scanf() function, the (int) typecast attempts to convert a pointer into an integer, which doesn’t work. Pointers are memory locations — an address. You must fetch the value at the address. The nerds say “to dereference” the address, which requires the asterisk pointer operator. But where to put it? Here are some options, which I’m sure every C programmer new to pointers would attempt:

A: (int)*a
B: *(int)a
C: (int *)a

You can rule out using the & address-of operator. The scanf() function requires an address as its second argument in this instance. Variable a is already an address. So which of the above typecasts work?

A doesn’t work because variable a is an address of a void type. You don’t want the value at the address, you want the address itself. B doesn’t work because a is a pointer, which you can’t cast as an integer. The answer is C because it typecasts the address as the location of an integer value, which is what the %d placeholder desires.

To fix the printf() statement takes a bit more brainwork. You need an integer value at an address, so the format (int)a (in the above code) doesn’t do anything. Rather than throw every combination of the characters (, ), *, and the word int at the wall to see what sticks, here is the proper format:

*((int *)a)

The (int *) cast is required as it was for the scanf() function to reference pointer a as containing an integer value. This expression, (int *)a, is then enclosed in parentheses and prefixed with the asterisk operator to fetch the integer value at the address stored at a. It’s ugly, but it works.

2024_04_27-Lesson.c

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

int main()
{
    void *a;

    a = malloc(sizeof(int));

    printf("Enter an integer: ");
    scanf("%d",(int *)a);
    printf("You entered %d\n",*((int *)a));

    return 0;
}

The final code runs like this:

Enter an integer: 100
You entered 100

These steps are convoluted and completely unnecessary. In practice, if I wanted to mess with void variable a as an integer, I would just declare a proper pointer type, int *b, and assign the void pointer to it:

2024_04_27-Lesson-b.c

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

int main()
{
    void *a;
    int *b;

    a = malloc(sizeof(int));
    b = a;

    printf("Enter an integer: ");
    scanf("%d",b);
    printf("You entered %d\n",*b);

    return 0;
}

This solution avoids the overhead (shown earlier) of referencing void variable a as an integer. Pointer b is a straight-up int pointer. It’s assigned the address stored in void pointer a, which handles the typecasting ordeal and renders unnecessary the extra decorations.

Finally, remember that just declaring a as an int in the first place, would have avoided all this nonsense altogether. But then I’d have less to write about.

3 thoughts on “From Void to Integer

  1. While Iʼm not the target audience, Iʼm beginning to enjoy this series.

    When it has finally clicked, all of the above seems clear as daylight. As a beginner, however, all this address-of, pointer and dereference business is often painful in the beginning… the given explanations should help to get over these initial hurdles, I think.

    One small thing though: seeing that the dereference operator * and (casts) are both in precedence class 2—which has right-to-left associativity—, the outer parenthesis around *((int *)a) are unnecessary, *(int *)a works just fine.

    Of course, for beginners the mantra “better too many than too little parentheses” surely is a good one, but syntax like ‘*(int *)a’ is seen so often in real code (e.g. in comparator functions) that Iʼd argue one should get used to this style quickly.

  2. I know that beginners hate pointers, but that doesn’t stop me from repeating how they work. I think exposure to these scary concepts is important.

    Yep, those parentheses aren’t needed. Thanks!

  3. I think the confusion is due to the terminology and syntax rather than the concept itself. The name “pointer” is a bit cryptic compared to, say, “location”, “address” or whatever. Also & and * are completely arbitrary and don’t provide any intuitive indication of what they mean. I also think the conventional void *a is a bit confusing which is why I always use void* a.

    It would be quite easy to abstract away much of the cryptic syntax with macros, but not a good idea because as soon as someone saw ordinary pointer code they’d be baffled.

Leave a Reply