Prerequisites: basic knowledge of C; access to a good C book.
/* Returns the number of elements in the array */ #define NumElm(array) (sizeof (array) / sizeof ((array)[0]))This macro computes the number of elements in an array by dividing the total size of an array by the size of a single element. The most important point to note is that this calculation is done at compile time; this not only makes the use of this macro as efficient at run-time as replacing the macro call with a constant, but also allows the use of the macro call in a context where constant expressions are required, so you can write something like this:
int a[10]; int b[NumElm(a)]; /* equivalent to int b[10]; */This is a standard way to calculate array length in C; it is described in K&R.
Note that this works only with "real" arrays; the result of this macro when
given a pointer as an argument is meaningless (there is no general way
to get a number of objects pointed to by a pointer -- pointers to objects and
pointers to arrays of objects are the same thing in C. That's a feature.)
/* WRONG! */ #define SQUARE(x) x * xDoes it work? Most of the time. But when someone (not necessarily the author of the macro) attempts to calculate SQUARE(3 - 1), he or she gets -1. Surprising, isn't it? The problem is especially hard to trace if the macro call is hidden in a large expression.
These problems arise because C macros are expanded at the preprocessor level, and all syntax and semantic analysis is done after this stage. Preproccesor simply replaces SQUARE(3 - 1) with 3 - 1 * 3 - 1, which is, of course, parsed as 3 - (1*3) - 1 and evaluated to -1. This bug can be fixed by puting parentheses around all references to arguments in a macro definition; but that still leaves some problems unsolved. Let's look at a bit different macro:
/* STILL WRONG! */ #define DOUBLE(x) (x) + (x)Again, this works sometimes and misteriously gives incorrect results in other expressions. For example, DOUBLE(2)*10 gives an unexpected result, because precedence of multiplication is higher than that of addition. This can be fixed by enclosing the macro body in parentheses:
/* Sort of right, but see below.. */ #define DOUBLE(x) ((x) + (x))This is the best we can do as far as preprocessor macros go. But there are still pitfalls: x is evaluated twice, so if an argument to a macro has any side effects (for example, ++ or --, or a call to a function), then again the Wrong Things will happen. This can't be avoided, and therefore this behaviour is officially declared a Feature. BTW, the convention to use upper case letters for macro names is a good thing because it alerts the programmer to potential problems.
Morale: write macros defensively, puting parentheses around arguments and
macro body; do not pass expressions with side effects to macros; use
alternatives (such as inline functions) whenever possible.
/* It compiles without a warning. Really. Try it. */ int x[10]; 5[x] = 42;Disclaimer: do not use this expression in production code - I will not pay your unemployment benefits. Don't say I didn't warn you.
This example demonstartes an interesting point. The subscript operator, x[y], is really symmetric, despite the asymmetric notation. Really, x[y] is exactly the same as *(x+y) which is of course the same as *(y+x) which is equivalent to y[x]. So, x[y] is equivalent to y[x]. QED.
Depending on your point of view, this can be either cute or disgusting.
/* equivalent to (x ? 1 : 0) */ int y = !!x;Another one of the "don't try this at home" C syntax hacks. Evaluates to 1 if x is non-zero; evaluates to 0 if x is 0. This example llustrates the difference between ~ (bitwise NOT) and ! (logical NOT). While ~~x is x, the result of ! is 1 or 0; so double application of ! "maps" all non-zero numbers to 1. For example, in the following expression
int count = !!x + !!y + !!z;count is assigned the number of non-zero variables in {x, y, z}.
Surgeon general's warning: using this syntax can be bad for your health.
BTW, x can sometimes be a pointer, too.
For your convinience, I'll include an example here. If you have a function that takes an integer and returns a pointer, such as
char * foo (int);then defining a corresponding function pointer type is easy -- just replace function name with (*varname):
char * (*fooptr) (int) = foo;Note that parentheses are required; otherwise, instead of a declaration of a pointer to a function that returns a pointer you will get a declaration of a function that returns a pointer to a pointer, which is obviously not what you intended.
It is often handy to a define a function pointer type. This is done by puting typedef in front of the declaration the and substituting type name for variable name:
typedef char * (*footype) (int);You can you use your type to declare variables, but not functions:
footype baz = foo; /* Ok */
foo this_doesnt_work { return (NULL); } /* Can't do that */
You can use function pointers to do many interesting things, some more
acceptable in production code than others. Example of the latter: you
can shorten the notation of
if (z) then x(a, b); else y(a, b);not only to
z ? x(a, b) : y(a, b);but further to
(z ? x : y) (a, b);
const char **base; int nel; qsort(base, nel, sizeof (char *), strcmp) /* Wrong! */seems like a perfect candidate for the job, and it usually compiles; but it doesn't work as expected! Figuring out why it doesn't work leads to better understanding of pointers and is a good investment of time; I will not include a complete explanation here and will just provide the correct solution:
/* of course this function can be written in one line... */
int strptrcmp (void *p1, void *p2)
{
const char *s1 = *((const char **) p1);
const char *s2 = *((const char **) p1);
return (strcmp (s1, s2));
}
/* ... */
const char **base;
int nel;
qsort(base, nel, sizeof (char *), strptrcmp);
#ifndef _SOMEHEADER_H_ #define _SOMEHEADER_H_ /* Header file body... */ #endif /* ! _SOMEHEADER_H_ */This #ifdef's out the body of the header file on second and subsequent includes; this avoids duplicate declarations and potential infinite recursion in inclusion. Smart compilers (such as gcc) don't even open the file the second time it's referenced.
Here's how to use preprocessor to avoid declaring global variables two times (one, as extern, in header file, and one in .c file). In your header file, use the following code:
#ifdef I_AM_MAIN #define GLOBAL /*nothing*/ #else #define GLOBAL extern #endif /* Global variables declared below... */ GLOBAL int foo; GLOBAL char *bar; /* etc. */Just include the header file as usual; all the GLOBAL declarations will be converted to extern. And in exactly one source file (usually the main file of the project), insert the line
#define I_AM_MAINbefore including the header file. As a result, the variables will be declared without extern, and storage will be allocated for them. This trick avoids duplicate declarations and helps keep variable declarations in sync (just think of the consequences of declaring a char x[]; in one file as extern char *x in another...) It is trivial to write a macro to handle initializations of global variables declared in this way.
Oh, and one more thing: don't call global variables foo or
bar.
The C Programming Language, Second Edition
By Kernighan, Brian W. / Ritchie, Dennis; Softcover; 272 Pages
Published by Prentice Hall; 06/88; ISBN: 0131103628
I have two copies; one of them -- hardcover (in Russian). How many
copies of K&R do you have?