Title says it all: I am wondering in what kind of situations a programmer would use a union vs using a structure. I'm pretty new to this stuff and my book only devotes about 2 pages to Unions so it isn't the best source of info on Unions really, but they look like they could be used to save a lot of RAM and I would like to know what kind of programs or functions they are intended for. Thanks, James.

Save lots of RAM? Not really.

Here's a union example to find the byte order of a system.

#include <iostream>

int main(int argc, char**argv)
{
	union
	{
		short s;
		char c[sizeof(short)];	
	} un;

	un.s = 0x0102;

	if (sizeof(short) == 2)
	{
		if ( un.c[0] == 1 && un.c[1] == 2 )
			std::cout << "Big endian" << std::endl;
		else if (  un.c[0] == 2 && un.c[1] == 1  )
			std::cout << "Little endian" << std::endl;
	} 
	return 0;
}

Unions is a poor man's way to express the idea of polymorphism. See for example a definition of X Windows event and related API.

Wow what a poor example. How is a union a poor man's way to express polymorphism? Your link doesn't work.

I am wondering in what kind of situations a programmer would use a union vs using a structure.

By far the most common use of unions is for variant types:

#include <stdio.h>

typedef enum { DOUBLE, INTEGER, UNKNOWN } type_t;

typedef struct {
    type_t type;
    union {
        double d;
        int    i;
    } value;
} value_t;

void display(const value_t *val)
{
    switch (val->type) {
        case DOUBLE:  printf("%f\n", val->value.d); break;
        case INTEGER: printf("%d\n", val->value.i); break;
        default: fputs("Unrecognized type\n", stderr); break;
    }
}

int main(void)
{
    value_t val = {DOUBLE, {1.2}};
    
    display(&val);
    
    val.type = INTEGER;
    val.value.i = 22;
    
    display(&val);
    
    return 0;
}

The next most common is a non-portable form of type punning:

#include <stdio.h>

int main(void)
{
    union pun {
        int           value;
        unsigned char bytes[sizeof(int)];
    } conv = {12345};
    
    for (size_t i = 0; i < sizeof(int); i++)
        printf("%u\n", conv.bytes[i]);
        
    return 0;
}

While this isn't a portable use case, the sheer usefulness of the idiom can vastly outweigh it's implementation-defined behavior. As another example, consider a union of bit fields for elegant masking.

Unions is a poor man's way to express the idea of polymorphism.

Calling it polymorphism, while technically correct, has connotations that I'd argue are confusing. "Variant types" would be more accurate, in my opinion. :)

Basically, a union is a chunk of data that can contain one value of a number of data types. Example: you may have a union with an integer, float, double, or long, declared like this:

union {
int i;
float f;
double d;
long l;
} value;

value.i = (int)1;
value.f = (float)1.1;
value.d = (double)1.11;
value.l = (long)1;

At the end of the assignments, the value item contains a long integer with a value of 1L. Only value.l has any meaning at this point. The size of the value variable will be big enough for the biggest member (properly aligned).

Now, let's consider a structure:

struct valtype {
int i;
float f;
double d;
long l;
};

struct valtype value;
value.i = (int)1;
value.f = (float)1.1;
value.d = (double)1.11;
value.l = (long)1;

In this case, the structure has 4 different members, so each member such as value.i, value.d, value.f, and value.l will have a proper value, and the size of the structure will be the sum of all the member sizes (again, properly aligned and padded). So, the structure variable will be much larger, and contain much more data, than the union. Here is an example of using a union when you don't know in advance what the type it will hold may be:

enum valtype { inttype = 0, longtype, floattype, doubletype };

typedef struct {
    union {
        int i;
        float f;
        double d;
        long l;
    } val;
    enum valtype type;
} valtype_t;

valtype_t value;

value.type = inttype;
value.val.i = (int)1;

switch (value.type)
{
    case inttype: printf("%d", value.val.i); break;
    case longtype: printf("%l", value.val.l); break;
    case floattype: printf("%f", value.val.f); break;
    case doubletype: printf("%lf", value.val.d); break;
}

Which you will note is almost exactly what Narue gave for her first example... :-) and I didn't even look at it before I wrote this!

This article has been dead for over six months. Start a new discussion instead.