Rest of Chapter 14

Unions

The union is a structure in which only one field can be active. The syntax for accessing union fields is identical to the syntax for accessing structure fields.

Unions are confusing.

Color example

Color can be represented in many ways.

union Color {
  float hsb[3] ;
  int   rgb[3] ;
}

union Color Red, Blue ;
Red.rgb[0] = 255 ;
Red.rgb[1] = 0 ;
Red.rgb[2] = 0 ;
Blue.hsb[0] = 0.6666666 ;
Blue.hsb[1] = 1.0 ;
Blue.hsb[2] = 1.0 ;

But how can you tell RGB from HSB?

struct Color {
  int   type ;    /* 0 for HSB, 1 for RGB */
  union uc {
    float hsb[3] ;
    int   rgb[3] ;
  }
}

struct Color Red ;
Red.type = 1 ;
Red.uc.hsb[0] = 255 ;
Red.uc.hsb[1] = 0 ;
Red.uc.hsb[2] = 0 ;

Issues

Enumerated types

Enumerated types are a great way to assign logical numbers to useful integers. They look better than those DEFINE's in the ICMP include file.

/* p. 575 of textbook */
enum spectrum {red, orange, yellow, green, blue, violet} ;
enum spectrum color ;
enum ececourses {ece109 = 109, ece209 = 209} ;

Flexible array members

The way it is done

Sometimes you will encounter odd declarations of arrays at the end of structures, such as this one from the sys/msg.h file for Unix messages.

struct mymsg {
    int mtype;             /* message type (+ve integer) */
    char mtext[1];         /* message body */
};

The above declaration is legal according to the C89 standard, but why bother with an array like mtext with only one element?

The reason is obscure. You’ll never see one element declarations except at the end of the structures. The array is sneaky way to have a variable sized structure and array. If you wanted to allocate a struct mymsg that could hold 209 characters, you'd do something like the following.

struct mymsg *char209 ;
char209 = malloc(sizeof(struct mymsg) + 208*sizeof(char)) ;

In this case, the allocated structure is big enough to hold a struct msgbuf and an additional 208 characters. This would allow you to safely store in char209->mtext[200] without writing into unallocated memory.

C99 and flexible arrays

Because this use of arrays is so common, C99 has added the flexible array member. This allows arrays to be declared at the end of structures with no size.

struct mymsg {
    int mtype;             /* message type (+ve integer) */
    char mtext[];          /* message body */
};

When the array is allocated extra room is given for the array.

struct mymsg *char209 ;
char209 = malloc(sizeof(struct mymsg) + 209*sizeof(char)) ;

Functions as parameters

In C, functions can be passed function pointers, but not functions.

/* Function to perform a numeric integration */
double Integrate0to1(double(*f)(double), int N) {
  double sum = 0.0 ;
  int i ;
  sum = f(0.0)*0.5 ;
  for(i=1; i < N-1; ++i) {
    sum += f(1.0/N) ;
  }
  sum += f(1.0)*0.5 ;
  return sum/N ;
}
/* Prototype for the standard C quicksort routine */
void qsort(void *base, size_t nmemb, size_t size,
           int(*compar)(const void *, const void *));

struct course {
  char Dept[4] ;
  int  Number ;
}

int CompareCourses(const void *V1, const void *V2) {
  struct course *C1 = (struct course *)V1 ;
  struct course *C2 = (struct course *)V2 ;
  int R = strncmp(C1->Dept, C2->Dept, 4) ;
  if (R) {
    return R ;
  } else {
    return C1->Number - C2->Number ;
  }
}

void SortCourseArray(struct course ManyCourses[1000]) {
  qsort((void *)ManyCourses, 1000, sizeof(struct course), CompareCourses) ;
}

Function of functions or function returning a parameter

The following two function prototypes are very different.

int *pointy(int) ;        /* a function returning a pointer to an integer */

int (*funky)(int) ;       /* a pointer to a function returning an integer */

Inconsistencies

Unfortunately there are two conflicting styles for refering to functions passed as parameters. In one the * is used and in the other it isn't.

Consequently, the following two statements are legal and perform the same action when f is a parameter refering to function that maps a double into a double.

  sum = (*f)(0.0)*0.5 ;

  sum = f(0.0)*0.5 ;   

Similarly, there is two different ways of passing functions to a functions. Either of the following will work.

  Integrate0to1(sqrt, 1000) ;

  Integrate0to1(&sqrt, 1000) ;

A complex, though functional, example

int Always(int ID) {
  return 1 ;
}

int Perhaps(int ID) {
  return ID%7 == 3 ;
}

int Never(int ID) {
    return 0 ;
}

/* Array of ID checkers */
int (*IDChecker[5])(int ID) = { Always, Always, Perhaps, Never, Always } ;

/* Function to see if ID checker accepts 0 as ID */
int TestIf0Accepted(int(*f)(int ID)) {
    return (*f)(0)==1 ;
}

/* Function to test if function to test ID checker really does */
int TooDurnComplicated(int(*T)(int(*f)(int ID))) {
    return (*T)(Always) ;
}

/* You really don't need that f or ID in the parameter list */
int TooHardToRead(int(*T)(int(*)(int))) {
    return (*T)(Always) ;
}

/* Function to test if function to test
 *                  if function to test ID checker really does */
int WasntTheLastOneEnough(int T(int(*)(int(*)(int)))) {
    return (*T)(TestIf0Accepted) ;
}

Complex declarations

  1. overriding parentheses go first
  2. postfix functional parentheses and array braces go next
  3. prefix pointer asterisks go last

Try out these examples from the textbook (p. 581).

Try out some examples from the functional section