Return to lecture notes index

October 7, 2008 (Lecture 11)

Test Review

We began class today by reviewing intersting aspects of the test -- those things that were most often missed (or solved the hard way).

Unions

There is another construct, the union. In syntax is very, very, very similar to that of a struct. But, as it turns out, it is very different than a struct. As an aside, we could further contract this defintion, using an anonymous union, as we did for the struct defintions in the prior examples:

  union measurement_t {
    unsigned int cubiccentimeters;
    unsigned float pounds;
    unsigned float kilograms;
  };

  typedef union measurement_t measurement;
  

This union indicates that a measurement is ONE OF an unsigned int cc, an unsigned float pounds, or an unsigned float kilograms. As it turns out the compiler need only allocate enough space the represent any one of of them -- not all of them. Consider the following example which shows that the storage for the three types is overlapped:

  #include <stdio.h>

  typedef union {
    int cc;
    float kg;
    float lbs;
  } measurement;


  int main() {

    measurement m;

    /*
     * It makes no sense to initialize all three, we can only use one
     * at a time. But, I want to prove a point here -- they are all 0
     */
    m.cc = 0;
    m.kg = 0;
    m.lbs = 0;
  
    /*
     * Notice that, no matter how we print the union -- and there is
     * only "one thing", not three, it is 0
     */
    printf ("%d cc, %f lbs, %f kgs\n", m.cc, m.kg, m.lbs);

    /*
     * Notice, we're only setting the value of the union using one
     * of its presentations, as "int kg"
     */
    m.kg = 10;
  
    /*
     * Notice that both of the "int" presentations have the same
     * value -- and the other one has a random value, because its
     * bits have changed, but aren't interpreted as an int and might,
     * or might not, be exactly overlapping in memory.
     */

    /* 1092616192 cc, 10.000000 lbs, 10.000000 kgs */
    printf ("%d cc, %f lbs, %f kgs\n", m.cc, m.kg, m.lbs);


    return 0;
  }
  

So, why would one use a union? Often times unions are used when one wants to allow for more than one type of data -- but does not want to allow things to be opened up to all types of data, as might be the case if a "void pointer", a.k.a., "generic pointer" is used. We'll talk more about "void pointers" soon.

Consider the case of a shipping company which quotes some clients rates by volume and others by weight. They might structure code as follows. Notice the combination of the struct and union. As an aside, we could use an enum, an enumeration, in place fo the #defined constants, but we haven't learned those, yet -- and they aren't alwasy used, in practice:

  #include <stdio.h>


  #define CC 1
  #define KG 2
  #define LBS 3


  typedef union {
    int cc;
    float kg;
    float lbs;
  } measurement;

  typedef struct {
    int units;
    measurement boxMeasurement;
  } shipmentrecord;


  int ship(shipmentrecord *, unsigned long *);


  int main() {

    shipmentrecord sr;
    unsigned long charges;

    sr.units = KG;
    sr.boxMeasurement.kg = 10;

    ship (&sr, &charges);

    return 0;
  }


  int ship (shipmentrecord *sr, unsigned long *charges) {

    switch (sr->units) {

    case CC:
      /* Compute charges by volume ... */
      break;

    case LBS:
      /* Compute charges by weight in pounds ... */
      break;

    case KG:
      /* Compute charges by mass in kilograms ... */
      break;

    }

    return 0;

  }