#define

The #define directive is the second most frequently used preprocessor directive. It is used to associate a meaningful identifier with: constants, keywords, and commonly used statements and expressions.

When an identifier is used to represent a constant (of any type) it is called a "manifest constant". Manifest constants have two main advantages. The first, and perhaps most valuable, is that the value of the constant, may which be used in many places in the program, can be changed by changing the value in one place - in the #define directive. The identifiers used in manifest constants are usually made in all capital letters (plus underscore) to help indicate that it is a constant. And of course, the identifier helps to document the meaning of the constant.

The general form for this usage of the directive is:

	#define identifier substitution_text

where 'identifier' is any legal identifier and 'substitution_text' can be anything - but of course the substituted text must be lexically and syntactically correct after the substitution. The substitution text can even be the null or empty string. This is not as vacuous as it seems, as will be show later with other preprocessor directives which interact with this one.

If in the substituted text, there is an identifier which has appeared in a previous #define directive, then it too will be replaced. This repeats until no further substitutions are possible. If a recursive substitution is created, the compiler will detect it and give an error message.

p> This brings us to the second valuable purpose served by using a #define directive, especially for constants. A constant which appears explicitly somewhere in a program is given the fanciful name of a 'magic number'. This is because on the surface, it is not clear why that constant is used and not another. By using a manifest constant, the identifier helps explain the meaning of the constant. For example,

   #define BOILING_POINT 100

Clearly, the number 100 could have many uses in a given program. If the manifest constant is not sufficient, one can put a comment just before the directive, such as:

   /* --- in degrees centigrade --- */

This comment only needs to appear in one place (before the #define directive), instead of everywhere the constant is used.

A third value of manifest constants, but related to the second, is the following. It may turn out, that more than two constants happen to have the same numeric value. By using the identifier in the directive instead it becomes clear which constant is being used where. This can avoid many errors. For example, if one of the constants needs to have its value changed, and the program only had constants, not identifiers, it would be difficult to be sure which constant of that value should be changed and which should not.

Let us say that we were using 20 to be size of a certain array, and 20 was the maximum value of each entry in the array. Later, as the problem being programmed was understood, it became clear that the maximum value of each entry could be 22 instead of 20. Then some (but not all) of the 20's would have to be changed to 22. Using #define directives would make such a change simple and safe, thus avoiding many potential hard to find bugs in the program. The change need only be made in one place: the #define directive.

The #define directive also allows symbolic or string substitution of parameters making it act like a function, except that the function is in-line; there is no call and return. This trades speed for memory size, since each instance of the macro replicates the code. The general form of the directive then becomes:

	#define identifier(parameter_list) substituted_text

re 'identifier' is any legal identifier, and 'parameter_list' is one or more identifiers separated by commas, and the 'substituted_text' can be anything that is syntactically valid after substitution. For each occurrence of an identifier in the 'parameter_list' in the substituted_text, the actual string in that position when the 'identifier' is used below in the source file, will be substituted within the 'substituted_text'. Here is an example for taking the absolute value of an expression. It has the interesting advantage that it can be used with variables of any numeric type:

	#define absv(v)  (v<0) ? -v : v

This use of parameterized #define directives, or macros as they are called, is all well and good. But, one must be very careful. A macro is not a function, where expressions are passed by value, so several surprises can occur. One area of surprise is with operator precedence. Let us take the above 'absv' macro and use it as follows:

	z=absv(x-5)*z;
this will not do what one wants, that is multiply the absolute value of (x-5) by 'z'. Let us see by expanding as the preprocessor does. We will get:

	z=(x-5<0) ? -x-5 : x-5*z;

fact we got two errors!. First, the macro itself did not even work as we had expected. It literally or lexically substituted 'x-5' for 'v' everywhere as expected, but the result for a negative number is: -x-5 rather than -x+5. Also, only the positive case is multiplied by 'z'.

fix is to use parenthesis everywhere, as follows:

	#define absv(v) (((v)<0) ? (-(v)) : (v))

parenthesis are put around each instance of every parameter (there is only one in this case) and the parenthesis around the entire expression are both necessary, as shown in the example above. This fixes all problems but one. This last problem cannot always be fixed. Let us use the above improved macro as follows:

	z=absv(x++);

problem now is, as we see after the substitution:

	z=(((x++)<0) ? (-(x++)) : (x++));

variable 'x' will be incremented twice instead of once. This is a general problem with macros and the pre and post increment and decrement expressions. It occurs precisely because a macro is not a function.


© 1991-2008 Prem Sobel. All Rights Reserved.