This book uses the words “routine” and “function” interchangeably. However, the term “routine” actually encompasses both functions and macros. Because functions and macros have different properties, you should pay attention to which form you are using. The descriptions in the reference section indicate whether routines are implemented as functions or as macros.
Most routines in the Microsoft run-time library are functions. They consist of compiled C code or assembled Microsoft Macro Assembler (MASM) code. However, a few library routines are implemented as macros that behave like functions. You can pass arguments to library macros and invoke them in the same way you invoke functions.
The main benefit of using macros is faster execution time. Every library macro is defined with a #define directive in a header file. A macro is expanded (replaced by its definition) during preprocessing, creating inline code. Thus, macros do not have the overhead associated with function calls. On the other hand, each use of a macro inserts the same code in your program, whereas a function definition occurs only once regardless of how many times it is called. Functions and macros thus offer a trade-off between speed and size.
Apart from speed and size issues, macros and functions have some other important differences:
Some macros treat arguments with side effects incorrectly when the macro evaluates its arguments more than once (see the example that follows this list). Not every macro has this effect. To determine if a macro handles side effects as desired, examine its definition in the appropriate header file.
A function name evaluates to an address, but a macro name does not. Thus, you cannot use a macro name in contexts requiring a function pointer. For instance, you can declare a pointer to a function, but you cannot declare a pointer to a macro.
You can declare functions, but you cannot declare macros. Thus, the compiler cannot perform type checking of macro arguments as it does of function arguments. However, the compiler can detect when you pass the wrong number of arguments to a macro.
The following example demonstrates how some macros can produce unwanted side effects. It uses the toupper routine.
#include <ctype.h>
int a = 'm';
a = toupper(a++);
The example increments a when passing it as an argument to the toupper routine, which is implemented as a macro. It is defined in CTYPE.H:
#define toupper(c) ( (islower(c)) ? _toupper(c) : (c) )
The definition uses the conditional operator (? :). The conditional expression evaluates the argument c twice: once to check if it is lowercase and again to create the result. This macro evaluates the argument a++ twice, increasing a by 2 instead of 1. As a result, the value operated on by islower differs from the value operated on by _toupper.
Like some other library routines, toupper is provided in both macro and function versions. The header file CTYPE.H not only declares the toupper function but also defines the toupper macro.
Choosing between the macro version and function version of such routines is easy. If you wish to use the macro version, you can simply include the header file that contains the macro definition. Because the macro definition of the routine always appears after the function declaration, the macro definition normally takes precedence. Thus, if your program includes CTYPE.H and then calls toupper, the compiler uses the toupper macro:
#include <ctype.h>
int a = 'm';
a = toupper(a);
You can force the compiler to use the function version of a routine by enclosing the routine's name in parentheses:
#include <ctype.h>
int a = 'm';
a = (toupper) (a);
Because the name toupper is not immediately followed by a left parenthesis, the compiler cannot interpret it as a macro name. It must use the toupper function.
A second way to do this is to “undefine” the macro definition with the #undef directive:
#include <ctype.h>
#undef toupper
Since the macro definition no longer exists, subsequent references to toupper use the function version.
A third way, not generally recommended, to make sure the compiler uses the function version is to declare the function explicitly:
#include <ctype.h>
int toupper(int _c);
Since this function declaration appears after the macro definition in CTYPE.H, it causes the compiler to use the toupper function.