C Programming – Why Use Macros and Functions with the Same Name?

cfunctionsmacrosnaming

I am reading 'The Standard C Library' by PJ Plauger which is really interesting. The book explains not only how to USE the library but also how it is implemented.

I have finished reading the ctype.h section and in the header the functions are declared as both macros AND functions. For example

int isdigit(int);

but also

#define isdigit(c) (_Ctype[(int)(c)] & _DI)

I don't understand why BOTH are used?

Also, if I try to recreate my own custom ctype header and implementation, I can only compile successfully if I remove the macro (comment out the define).

This aspect was not really explained in the book. Can someone please explain?

Best Answer

The macro is (putatively) more efficient, as it doesn't involve a function call. It can be optimised more easily, as it just involves a pointer offset lookup.

The function call allows linking against the same library even if the program was compiled without the macro definition - if it was compiled with a different header, or just with a rogue declaration inside the source file. Should, for example, you have a compiler which has someone's "improved" version of ctype.h that didn't have the macro, the function would still exist at runtime for use.

If we look at the standard:

7.1.4 Use of library functions

Any function declared in a header may be additionally implemented as a function-like macro defined in the header, so if a library function is declared explicitly when its header is included, one of the techniques shown below can be used to ensure the declaration is not affected by such a macro. Any macro definition of a function can be suppressed locally by enclosing the name of the function in parentheses, because the name is then not followed by the left parenthesis that indicates expansion of a macro function name. For the same syntactic reason, it is permitted to take the address of a library function even if it is also defined as a macro.

That means that if you write:

int b = (isdigit)(c);

or

int (*f)(int) = &isdigit;
int b = f(c);

then you are invoking the actual function, not the macro. You can also legally write:

#undef isdigit
int b = isdigit(c);

or (in a source file not having #include <ctype.h> directly or transitively):

extern int isdigit(int);
int b = isdigit(c);
Related Topic