answersLogoWhite

0

The preprocessor is the program that prepares your source files for compilation. In some implementations the preprocessor is a separate program from the compiler but in others it is part of the compiler program itself. Regardless, preprocessing occurs before compilation, hence the preprocessor is often commonly referred to as the precompiler.

The preprocessor parses each of your source files (*.c files) and generates intermediate files that contain nothing but pure C code. Note that the compiler never sees your source code, it only sees the code output by the preprocessor. Each intermediate file output by the preprocessor is known as a translation unit. The compiler converts each translation unit into an object file, which is part machine code, part symbol table. Once all translation units have been compiled, the linker can examine the symbol tables within the object files and thus link the machine code together to produce the final executable.

You might think that your source files are already pure C code but they are not. For instance, source files usually contain user-comments but a user-comment is not C code; it is human-readable code. So part of the preprocessor's job is to strip out all user-comments. This is why compiler's ignore user-comments; they never see them!

Aside from stripping out comments, the primary role of the preprocessor is to act upon the preprocessor directives within your source files. That is, header files are imported into your source code, conditional compilation is applied and macros are expanded.

Importing headers is relatively straightforward. The #include directive is simply replaced with the contents of the specified header file in the intermediate file. However, the imported file must be preprocessed as it is imported because header files often include directives of their own, which may including importing other header files. In some cases, a header file might end up being included two or more times in a single translation unit which is why we use conditionally compiled header guards to ensure each header is included only once.

Conditional compilation relies upon symbols that are either defined or not defined. We can use directives to define or undefine symbols as appropriate, although its best to supply conditional compilation symbols via the command line. We can test if a symbol is defined or not by using the #ifdef and #ifndef directives as appropriate. Depending on the evaluation of the test, the code that follows is either included or excluded from the intermediate file, up to the corresponding #endif directive. Header guards make use of this to define a new symbol if the header hasn't already been included. If the symbol already exists, then we can ignore the content because it has already been included. In addition, the #else directive allows the programmer to provide alternate versions of code, only one of which will be included in the intermediate file. This is useful when writing cross-platform code by providing code specific to each platform.

Macros are the most complex part of preprocessing. Consider the following macro definitions:

#define WHEREARG __FILE__, __LINE__

#define WHERESTR "[file %s, line %d]: "

#define DEBUGPRINT2(...) fprintf(stderr, __VA_ARGS__)

#define DEBUGPRINT(_fmt, ...) DEBUGPRINT2(WHERESTR _fmt, WHEREARG, __VA_ARGS__)

The first two macros are simple text replacements while the second two are macro functions. All four work together. The DEBUGPRINT macro function is the macro we will actually use in our code. For instance:

DEBUGPRINT("x=%d\n", x);

If we suppose this line of code appears on line 42 of the source file "foo.c", this macro would expand to:

fprintf(stderr, "[file %s, line %d]: x=%d\n", "foo.c", 42, x);

To understand how it works, we need to look at the individual macro substitutions performed by the preprocessor. First we substitute the original DEBUGPRINT macro function with its definition:

DEBUGPRINT2(WHERESTR "x=%d\n", WHEREARG, x);

Note that arguments we supplied are substituted into the arguments of the macro function. This substituted code is another macro function. After simple text substitution of WHERESTR and WHEREARG we get:

DEBUGPRINT2("[file %s, line %d]: x=%d\n", __FILE__, __LINE__, x);

Note that when two strings are adjacent in a macro, the preprocessor automatically appends one to the other.

Again, we have two simple text substitution macros. The __FILE__ and __LINE__ macros are actually defined by the preprocessor. Given that the original function was called from line 42 of the "foo.c" file, the macro will now expand to:

DEBUGPRINT2("[file %s, line %d]: x=%d\n", "foo.c", 42, x);

Finally, we substitute the DEBUGPRINT2 macro with its definition, substituting the supplied arguments accordingly:

fprintf(stderr, "[file %s, line %d]: x=%d\n", "foo.c", 42, x);

Note that the compiler never sees the macros, it only sees the code generated by the preprocessor. If the expanded code contains any compiler errors, the compiler cannot identify the source of the error. In addition, macros are not type safe and may introduce subtle bugs that are extremely difficult to track down. When used appropriately, they can greatly simplify your coding however the general rule is don't use them unless you have to. If you find yourself using a lot of macros to overcome a language limitation, then you're probably using the wrong programming language for the task at hand.

User Avatar

Wiki User

8y ago

Still curious? Ask our experts.

Chat with our AI personalities

JudyJudy
Simplicity is my specialty.
Chat with Judy
LaoLao
The path is yours to walk; I am only here to hold up a mirror.
Chat with Lao
ReneRene
Change my mind. I dare you.
Chat with Rene
More answers

Preprocessor instructions are used to modify the compiler's perspective of the source code during the parsing phase, before the compilation phase. The most common preprocessor directives are "include" (place the source code from another file into the current file's parse point), "pragma" (change the compiler's flags), and "define" (define a compile-time constant). Other uses are conditional compilation instructions, such as using one algorithm for a certain platform, and a different one for another platform. Preprocessor directives never appear in the final executable code directly, but instead influence how the rest of the source code will be compiled.

User Avatar

Wiki User

12y ago
User Avatar

For the computer programming languages C and C++, the C preprocessor functions as the macro preprocessor. It is also referred to simply as cpp.

User Avatar

Wiki User

10y ago
User Avatar

Add your answer:

Earn +20 pts
Q: What is a preprocessor in c programming?
Write your answer...
Submit
Still have questions?
magnify glass
imp