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",
};
}