20

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?

superM
  • 7,363
  • 4
  • 29
  • 38
user619818
  • 1,757
  • 2
  • 14
  • 23
  • There is nothing in the C standard forcing a compiler to implement it as a macro. The macro is most likely a residue from the old days when C didn't have inlining. Though a smart compiler should be able to inline that function if needed, without an explicit inline keyword. So the function-like macro is only there because the compiler was implemented by someone who was not brilliant at making compilers. –  Aug 08 '12 at 12:59

1 Answers1

23

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);
ecatmur
  • 346
  • 3
  • 6