Search code examples
cvoidunions

union vs void*, which is better for what?


When I first learned about union wasn't obvious, but after I learned C++ runtime polymorphism, I had a thought. Why do I use union instead of void*? I created a short code example for both.

Here is the union example:

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

const int N = 10;

union int_double_t
{
    int i;
    double d;
};

enum int_double_label
{
    INT, DOUBLE
};

struct labeled_int_double_t
{
    enum int_double_label label;
    union int_double_t value;
};

int main()
{
    struct labeled_int_double_t u[N];

    for (int i = 0; i < N; ++i)
    {
        if (rand() % 2)
        {   
            u[i].label = INT;
            u[i].value.i = i;
            printf("array[%i]:     %s\t%i\n", i, u[i].label ? "double" : "int", u[i].value.i);
        }
        else
        {
            u[i].label = DOUBLE;
            u[i].value.d = i + .1;
            printf("array[%i]:     %s\t%f\n", i, u[i].label ? "double" : "int", u[i].value.d);
        }
    }
}

Here is the void* example:

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

const int N = 10;

enum int_double_label
{
    INT, DOUBLE
};

struct labeled_int_double_t
{
    enum int_double_label label;
    void* value;
};

int main()
{
    struct labeled_int_double_t u[N];

    for (int i = 0; i < N; ++i)
    {
        if (rand() % 2)
        {   
            u[i].label = INT;
            u[i].value = malloc(sizeof(int));
            *(int*)u[i].value = i;
            printf("array[%i]:     %s\t%i\n", i, u[i].label ? "double" : "int", *(int*)u[i].value);
            free(u[i].value);
        }
        else
        {
            u[i].label = DOUBLE;
            u[i].value = malloc(sizeof(double));
            *(double*)u[i].value = i + .1;
            printf("array[%i]:     %s\t%f\n", i, u[i].label ? "double" : "int", *(double*)u[i].value);
            free(u[i].value);
        }
    }
}

Both compiled with: gcc -W -Wall -Wextra -pedantic -std=c99, both run as expected.

Here is the output:

array[0]:     int       0
array[1]:     int       1
array[2]:     double    2.100000
array[3]:     double    3.100000
array[4]:     int       4
array[5]:     double    5.100000
array[6]:     double    6.100000
array[7]:     double    7.100000
array[8]:     double    8.100000
array[9]:     double    9.100000

I kind of see the difference. With union, I'm able to use only stack memory, but is there any other fundamental differences?


Solution

  • While you know what you are doing, and still know what you did, there is not a lot of difference.
    But otherwise (the code is by a colleague, or by you before the three week vacation on a beach you just had) you will be happy for any enforced clarity and any small thing where the compiler can detect a mistake for you.
    This might seem irrelevant for the single programmer who is constantly working on the code in question, but for old, large or multi-location projects I feel like "*void is unlucky." so much so that I needed some time to realise that it is not a general rule before writing this. ;-)