C pre-processor and ANSI standard

Radford Neal radford at calgary.UUCP
Sat Sep 8 07:02:29 AEST 1984

A number of articles have been posted recently regarding the C pre-processor
and the new C standard. None of the none of the authors seem to take 
the correct -:) view that the C pre-processor is a complete botch and
should be discarded as soon as possible.

How many macro definitions like the following have you seen?

        #define min(a,b) a>b ? b : a

The correct definition is

        #define min(a,b) ((a)>(b) ? (b) : (a))

but few programmers are that careful. If you don't see the difference, try
the expressions x+min(y,z) and min(1,q?x:z). 

If you think you'd never be that careless, go on to trying to write a
macro to write N spaces to standard output. This is IMPOSSIBLE if you
want the macro call to look like a procedure call. The best you can do
is the following:

        #define spaces(n) { int i; for (i = (n); i>0; i--) putchar(' '); }

This naturally doesn't work in a call such as spaces(i*2) (no concept of
scope!). Furthermore, the following code segment doesn't work:

        if (blat) spaces(10);
        else spaces(20);

(You have to remove the first semi-colon). 

The present pre-processor is also a fertile ground for programmers who
like to write obscure and misleading code. A relatively mild form of 
this is the following sort of macro:

        #define clear(v) ((v) = 0)

A call such as clear(i) looks just like a procedure call but alters
its argument, which would be impossible if it really was one.

Finally, the pre-processor doesn't fit with the rest of C syntax from
an aesthetic point of view. The commands can't be indented and they treat
end-of-line as significant, both contrary to the rest of the language.

I suggest that the new standard include a minimal form of the present 
pre-processor for compatibility plus the following two new constructs
which would be preferred for most of the legitimate uses.

    1) Allow a storage-class of 'const'. Examples would be

           const int a = 1234;
           const struct point origin = { 0.0, 0.0 };

       The initialization would be optional for global const's, but would
       be expected in some module. Integer constants initialized in the
       current module could be used in array bounds, etc. You could
       take the address of a constant. The compiler could figure out
       whether it really needed to allocate space for it (in a write-protected
       area of course).

    2) Allow a "storage-class" of 'inline' for procedures. For example:

           inline int min(a,b)
             int a, b;
           { return a>b ? b : a;

       Semantics identical to ordinary procedures except you can't get
       pointers to them. Any decent compiler would generate in-line
       code for these, which shouldn't be too difficult since C already
       has declarations in inner blocks.

The only legitimate use of a real macro-like facility I can see is
for conditional compilation, and even that might be better done in most
cases by a compiler that's smart enough to recognize if statements with
constant conditions.

   Radford Neal, University of Calgary, Calgary, Alberta, CANADA

More information about the Comp.lang.c mailing list