tags:

views:

498

answers:

11

What would be the differences between using simply a void* as opposed to a union? Example:

struct my_struct {
    short datatype;
    void *data;
}

struct my_struct {
    short datatype;
    union {
        char* c;
        int* i;
        long* l;
    };
};

Both of those can be used to accomplish the exact same thing, is it better to use the union or the void* though?

+7  A: 

In my opinion, the void pointer and explicit casting is the better way, because it is obvious for every seasoned C programmer what the intent is.

Edit to clarify: If I see the said union in a program, I would ask myself if the author wanted to restrict the types of the stored data. Perhaps some sanity checks are performed which make sense only on integral number types. But if I see a void pointer, I directly know that the author designed the data structure to hold arbitrary data. Thus I can use it for newly introduced structure types, too. Note that it could be that I cannot change the original code, e.g. if it is part of a 3rd party library.

swegi
intent and use are two different things when it comes to memory consumption. A union is as big as its biggest member.
Tim Post
but in this casee.. all the members in union are pointers.. So it should be same size as a void * right?
FatDaemon
nothing in the spec requires pointers to be the same size, but void * has to be large enough to handle any other pointer. That said, in practice, pointers are generally the same size
Mikeage
Just curious, how is the intent any less clear in the union version?
Dan Olson
@Dan Olson -- because C, historically, always used a void * for this. THat's what they're intended for.
Mikeage
Wouldn't you say that this usage also covers the intent of unions?
Dan Olson
@Dan: not with regard to indirection or dynamically allocated buffers. While you can use a union to accomodate all possible data types, ADT implementations in C always use void*, because it's not desirable to restrict it to hold only certain data types.
Michael Foukarakis
+2  A: 

Although using union is not common nowadays, since union is more definitive for your usage scenario, suits well. In the first code sample it's not understood the content of data.

eyazici
+3  A: 

The union approach requires that you know a priori all the types that might be used. The void * approach allows storing data types that might not even exist when the code in question is written (though doing much with such an unknown data type can be tricky, such as requiring passing a pointer to a function to be invoked on that data instead of being able to process it directly).

Edit: Since there seems to be some misunderstanding about how to use an unknown data type: in most cases, you provide some sort of "registration" function. In a typical case, you pass in pointers to functions that can carry out all the operations you need on an item being stored. It generates and returns a new index to be used for the value that identifies the type. Then when you want to store an object of that type, you set its identifier to the value you got back from the registration, and when the code that works with the objects needs to do something with that object, it invokes the appropriate function via the pointer you passed in. In a typical case, those pointers to functions will be in a struct, and it'll simply store (pointers to) those structs in an array. The identifier value it returns from registration is just the index into the array of those structs where it has stored this particular one.

Jerry Coffin
Yes, but if yo don't know the data type you have no choice, so "Which one?" question will be meaningless.
eyazici
+2  A: 

My preference would be to go the union route. The cast from void* is a blunt instrument and accessing the datum through a properly typed pointer gives a bit of extra safety.

Dr. Tim
it provides no protection; if you store a char * and access it as int *, the compiler will be perfectly happy, although if it's not aligned, you will have problems...
Mikeage
Note that I said a "bit" of safety. The truth is that C will let you make any mistake you want.
Dr. Tim
which bit is that? forcing you to only cast int * to char * or vice versa?
Mikeage
For me, safety in programming is not just in using the mechanisms that the compiler provides but extends to knowing the way I think and how I make mistakes. For me, the union better, maybe.
Dr. Tim
+2  A: 

Toss a coin. Union is more commonly used with non-pointer types, so it looks a bit odd here. However the explicit type specification it provides is decent implicit documentation. void* would be fine so long as you always know you're only going to access pointers. Don't start putting integers in there and relying on sizeof(void*) == sizeof (int).

I don't feel like either way has any advantage over the other in the end.

Dan Olson
+2  A: 

It's a bit obscured in your example, because you're using pointers and hence indirection. But union certainly does have its advantages.

Imagine:

struct my_struct {
   short datatype;
   union {
       char c;
       int i;
       long l;
   };
};

Now you don't have to worry about where the allocation for the value part comes from. No separate malloc() or anything like that. And you might find that accesses to ->c, ->i, and ->l are a bit faster. (Though this might only make a difference if there are lots of these accesses.)

asveikau
maybe those pointers point to an array...?
Mikeage
@Mikeage - Then, if the array has a fixed maximum size and that size is acceptable to put into your structure, I'd still be tempted to use a union, not containing pointers but containing arrays. Otherwise I'd use void pointers. Since the example did not include a size member I assume that wasn't what he was thinking, though.
asveikau
+5  A: 

It's more common to use a union to hold actual objects rather than pointers.

I think most C developers that I respect would not bother to union different pointers together; if a general-purpose pointer is needed, just using void * certainly is "the C way". The language sacrifices a lot of safety in order to allow you to deliberately alias the types of things; considering what we have paid for this feature we might as well use it when it simplifies the code. That's why the escapes from strict typing have always been there.

DigitalRoss
+1  A: 

It really depends on the problem you're trying to solve. Without that context it's really impossible to evaluate which would be better.

For example, if you're trying to build a generic container like a list or a queue that can handle arbitrary data types, then the void pointer approach is preferable. OTOH, if you're limiting yourself to a small set of primitive data types, then the union approach can save you some time and effort.

John Bode
+9  A: 

I had exactly the case in our library. We had a generic string mapping module that could use different sizes for the index, 8, 16 or 32 bit (for historic reasons). So the code was full of code like that:

if(map->idxSiz == 1) 
   return ((BYTE *)map->idx)[Pos] = ...whatever
else
   if(map->idxSiz == 2) 
     return ((WORD *)map->idx)[Pos] = ...whatever
   else
     return ((LONG *)map->idx)[Pos] = ...whatever

There were 100 of lines like that. In a first step I changed it an union and I found it was more readable.

switch(map->idxSiz) {
  case 1: return map->idx.u8[Pos] = ...whatever
  case 2: return map->idx.u16[Pos] = ...whatever
  case 3: return map->idx.u32[Pos] = ...whatever
}

This alowed me to see better what was going on and I could then decide to remove completely the idxSiz variants using only 32 bit indexes. But this was only possible once the code got more readable. PS: That was only a minor part of our project which is about several 100 thousands of line of code written by people who do not exist anymore. So the changes in code are gradual so as not to break the applications.

Conclusion: Even if people are less used to the union variant, I prefer it because it can make the code much lighter to read. On big projects, it is extremely important to make the code more readable, even if it is yourself that will read it later.

Edit: Added the comment, as comments do not format code:

The change to switch came before (this is now the real code as it was)

switch(this->IdxSiz) { 
  case 2: ((uint16_t*)this->iSort)[Pos-1] = (uint16_t)this->header.nUz; break; 
  case 4: ((uint32_t*)this->iSort)[Pos-1] = this->header.nUz; break; 
}

was changed to

switch(this->IdxSiz) { 
  case 2: this->iSort.u16[Pos-1] = this->header.nUz; break; 
  case 4: this->iSort.u32[Pos-1] = this->header.nUz; break; 
}

I shouldn't have combined all the beautification I did in the code and only show that step. But I posted my answer from home where I had no access to the code

tristopia
I think the readability comes from using a switch instead of nested ifs.
swegi
The change to switch came before (this is now the real code as it was)switch(this->IdxSiz) { case 2: ((uint16_t*)this->iSort)[Pos-1] = (uint16_t)this->header.nUz; break; case 4: ((uint32_t*)this->iSort)[Pos-1] = this->header.nUz; break;}was changed toswitch(this->IdxSiz) { case 2: this->iSort.u16[Pos-1] = this->header.nUz; break; case 4: this->iSort.u32[Pos-1] = this->header.nUz; break;}I shouldn't have combined all the beautification I did in the code and only show that step. But I posted my answer from home where I had no access to the code.
tristopia
+2  A: 
Peeter Joot
+1  A: 

The union reservs enough space for the largest member, they don't have to be same, as void* has a fixed size, whereas the union can be used for arbitrary size.

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

struct m1 {
   union {
    char c[100];
   };
};

struct m2 {
 void * c;
 };


 int
 main()
 {
printf("sizeof m1 is %d ",sizeof(struct m1));
printf("sizeof m2 is %d",sizeof(struct m2));
exit(EXIT_SUCCESS);
 }

Output: sizeof m1 is 100 sizeof m2 is 4

EDIT: assuming you only use pointers of the same size as void* , I think the union is better, as you will gain a bit of error detection when trying to set .c with an integer pointer, etc'. void* , unless you're creating you're own allocator, is definitely quick and dirty, for better or for worse.

Liran Orevi