The #define preprocessor directive is used to associate a particular string of characters with a replacement string. The operation of the preprocessor is then very similar to an editor performing a global substitution. The basic syntax is
#define <identifier> <replacement> The replacement can be any sequence of characters and is terminated by the end of line. The normal C language rules apply to the formation of identifiers. This is particularly useful for referring to frequently used items in a program, for example the size of a buffer. The following variant of the line reversing program illustrates the point.
#include <stdio.h>
#define LINMAX 80
main()
{
char inbuf[LINMAX];
int c;
while(1)
{
int i=0;
while((c=getchar())!='\n' && c!=EOF)
if(i!=LINMAX)inbuf[i++]=c;
if(c==EOF) break;
if(i--)while(i>=0)putchar(inbuf[i--]);
putchar('\n');
}
}
The virtue of this approach is that it is possible to change the size of the buffer by simply altering the single line that defines the symbol
LINMAX ,
once this change has been made the program can be re-compiled and the preprocessor will replace every occurrence of the symbol
LINMAX
by the new actual value. In a larger program with several references to
LINMAX
the advantages of this approach are considerable. If the actual buffer size were quoted then it would be necessary to track down every reference and alter it when the buffer size was changed. It would be all to easy to overlook one or, possibly worse, change an instance of a number that happened to look the same but was completely unrelated to the buffer size.
It is good programming practice to use upper case letters for identifiers defined by #define directives, this makes them stand out in the actual C code and makes it clearer to the programmer that they are not normal variables. Remember that the preprocessor operates purely by textual substitution so the error in
#define MAXCNT 100 . . . MAXCNT = 50;is not immediately obvious unless you realise what MAXCNT actually stands for.
Identifiers defined using #define in this fashion are sometimes called manifest constants although this notation does not really reflect the full capabilities of #define as the example below will make clear. Occasionally they are called object-like macros .
It is quite possible to use #define directives for all sorts of purposes as the following program shows.
#include <stdio.h>
#define LINMAX 80
#define BEGIN {
#define END }
#define AND &&
#define FOREVER while(1)
#define NEWLINE '\n'
main()
BEGIN
char inbuf[LINMAX];
int c;
FOREVER
BEGIN
int i=0;
while((c=getchar())!=NEWLINE AND c!=EOF)
if(i!=LINMAX)inbuf[i++]=c;
if(c==EOF) break;
if(i--)while(i>=0)putchar(inbuf[i--]);
putchar('\n');
END
END
The program now looks quite unlike a C program and bears a marginal resemblance to certain other programming languages. This practice is to be strongly discouraged even though it does demonstrate the text substitution nature of the preprocessor.