PlayOS wrote:
So then all preprocessors have no affect on the disk image.
Not directly. With the exception of the #pragma directives, which send options to the compiler, all preprocessor directives act on the source code before it is compiled; they are strictly textual replacements.
For a few examples:
A defined name's value is inserted directly into the source code, without regard to it's meaning. Thus,
becomes
after preprocessing; this is the code actually seen by the compiler itself. Thus one could write
Code: Select all
int x = 3;
int y;
#define DOUBLEX x*x
y = DOUBLEX;
and get the result
Macros work the same way, except that they replace the names of their parameters with the
text arguments given to them. Thus,
Code: Select all
#define SQUARE(x) x *x
int x, y, z;
char a, b;
y = z = 2;
x = SQUARE(y);
y = SQUARE(y + z);
z = SQUARE(x + y * z);
a = 'a';
b = SQUARE(a);
will result in the following code going to the compiler:
Code: Select all
int x, y, z;
char a, b;
y = z = 2;
x = y * y;
y = y + z * y + z;
z = x + y * z * x + y * z;
a = 'a';
b = a * a;
Determining all of the resulting programming errors is left as an excerise for the reader; let it suffice to say that line 8 (the last line, assuming that line numbering starts at zero) will cause a compiler error, while lines 5 and 6 have logical errors stemming from operator precedence conflicts. This is the classic example of both the power and the danger of too liberal a use of preprocessor macros, which any text on the language which is worth reading should cover in detail. It is important to understand just how powerful they really are: the only limitations on them are that the cannot be recursive (otherwise the preprocessor would get stuck in an infinite loop), and they must use the line extension marker ('/') if they are to span more than one line:
Code: Select all
#define BIGMACRO(x, y, z) if (x > z) /
{ /
x = pow(y, 4); /
y = 5; /
} /
else /
{ /
z = y * x; /
y = (int) time(NULL); /
}
This is an extreme example, but not an impossible one.
Includes add text from other files to the current file.
inserts the contents of the file stdio.h into your source code.
Conversely, preprocessor switches control which text should be excluded from the source code.
Code: Select all
srand((int) time(NULL)); /* use current time for random seed */
y = rand();
#ifdef ___SOLARIS___
x += y;
#elif defined ___LINUX___
x += y > 0 ? y : 1;
#elif defined ___WINDOWS___
if (phase_of_moon() != "GIBBOUS" && ulBills_bankroll > MAXULONG)
x += y * (x -y);
#endif
z = factorial(x);
causes only the code for in the clause matching the appropriate defined macro value to appear in the text; so, if the value "#define ___LINUX___" had appeared earlier in the text, the resulting code would be
Code: Select all
srand((int) time(NULL)); /* use current time for random seed */
y = rand();
x += y > 0 ? y : 1;
z = factorial(x);
It is important to remember that all of the changes are solely in the text of the source code, and are completely invisible to the compiler; it is completely oblivious to the proprocessor's very existence (at least in traditional implementations). The preprocessor is in fact a separate program, cpp, in most versions, which the copiler driver program (gcc) calls before running the compiler proper. Depending on the version, it may also do some houscleaning tasks, such as removing comments and redundant lines; some may even compact the source code into a single line of text, for faster compiling.
It should be noted thast most of what has been said about the CPP applies, generally speaking, to assembler macros as well - indeed, in the case of as or gas, the assembly macro processor often
is the C preprocessor, though the m4 preprocessor is also often used. While other macro systems (such as MASM's opr NASM's) are more closely tied to the assembler itself, the basic function of all of them is to alter the text of the source code before the actual compiling or assembling takes place.