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 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 ;
union
structure
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} ;
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.
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)) ;
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) ; }
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 */
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) ;
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) ; }
Try out these examples from the textbook (p. 581).
int board[8][8] ;
int ** ptr ;
int * risks[10] ;
int (* rusks)[10] ;
int * off[3][4] ;
int (* uuf)[3][4] ;
int (* uof[3])[4] ;
Try out some examples from the functional section
int(*compar)(const void *, const void *) ;
int (*IDChecker[5])(int ID) ;
int(*T)(int(*f)(int ID)) ;
int(*T)(int(*)(int)) ;