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?
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. ;-)