Section Notes 2023-02-28
Table of Contents
1. Topics
1.1. Adaptability to Change
One of the ways to make code more adaptable is to use pointers to functions to generalize or abstract some behavior.
// Example of a function type, which can be used below to declare pointers // to functions. // typedef int AccumFunc(int running_total, int item); // A function taking a pointer to the above function type. // int loopThroughArray(const int *array, size_t count, AccumFunc *func) { int accum = 0; for (const int *ap = array; ap < array + count; ++ap) { // Calling a pointer to function is just like calling a function // in modern C. In old C you would need to dereference it first. accum = func(accum, *ap); } return accum; } // You can declare a function using the function type. // AccumFunc sumFunc; // However, because parameter names are treated as comments in a declaration, // to define the function you must do so in the normal way. // int sumFunc(int run, int item) { return run + item; } // Another function type // typedef int TransformFunc(int value); // It can be useful to bundle a set of these functions together into a // structure in order to pass them, especially when several functions need // to pass these function pointers along. // typedef struct funcs { AccumFunc *accum; TransformFunc *xform; } Funcs; int foo() { // You can use the C99 designated initializer syntax to initialize // such a structure (or any structure). Any field which is not so // designated gets 0-initialized by default. You don't have to give // these initializers in the same order but it is usually less confusing // to keep it. // // Note that when dealing with an initializer list you're allowed to // have a comma after the last entry; this makes it easier to move // lines around and less complicated to edit the list later. // Funcs myfuncs = { .accum = sumFunc, .xform = negateFunc, }; Funcs *myfuncs; // In order to use the designated initializer syntax outside of a // definition, you need to cast the initializer to the correct struct // type. // // (Note the extra leading comma before "*myfuncp" is a meta-quoting // character for Org mode, and not part of the C code.) // *myfuncp = (Funcs) { .accum = productFunc, .xform = add1To, }; // Designated initializers can also be used for arrays but this is // not very common. // int array[10] = { [2] = 4, [0] = 2 }; // I often use the extra comma trick for enums as well but there is // a common trick of having a fake enumerator as the last one which // is used to store the number of items in the enumeration. // typedef enum colors { RED, GREEN, BLUE, BLACK, NUM_COLORS } Colors; // This can be used to declare a parallel array of names for the // enumerators which can be used to print them in messages for example. // Strictly speaking the size is not needed but the compiler will // warn if the size is different from the number of initializers. // const char *color_names[NUM_COLORS] = { "red", "green", "blue", "black", }; }