An Interesting CPP Bug

The C preprocessor, being a dumb text-manipulating tool that does not understand the syntactic structure of the underlying program, is often a hiding place for subtle headache-inducing bugs. I’ve been bitten such bugs myself and one bug I had a particularly difficult debugging is often not mentioned in lists such as [1].

Can you spot a bug in the following CPP macro? Specifically, when is its behaviour ill-defined?

#define FROBNICATE(__frobptr) do {              
    struct frobnicable *fptr = (__frobptr);     
    assert(fptr && "Can't frobnicate NULL!");   
  } while(0)

One interesting thing I discovered about C while debugging this macro is that variable initializations like the following are valid:

  int foo = foo;
  /* foo now has an unspecified value. */

which means the following piece of code won’t do what is expected of it:

  struct frobnicable *fptr = malloc(sizeof(struct frobnicable));
  fptr->x = 42;

The macro will expand to

do {
  struct frobnicable *fptr = (fptr);
  /* ... and so on ...  `fptr` is undefined in the rest of the
   * macro expansion. */
} while(0);

and the implications won’t be pleasant.

I haven’t yet discovered an elegant and effective way to deal with this issue. The struct frobnicable *fptr = (__frobptr) is needed to prevent multiple evaluations of __fptr, so the only way I see of preventing this bug is by using an extremely unusual name for the fptr variable (or following a convention of prefixing such variable with some previously defined string). But that is neither elegant nor foolproof.


This entry was posted in Computers and tagged , , , . Bookmark the permalink.

3 Responses to An Interesting CPP Bug

  1. Barry Kelly says:

    Have you only just discovered the reason for hygienic macros in Lisps etc.? Or is there more to this?

  2. Sanjoy says:

    Before I was bit by this, I used to think in a block like this

    int a = 5;
      int a = a + 5;

    the `a` in the assignment’s RHS would refer to the `a` in the outer scope and that I’d get a compile-time error if there wasn’t any such `a` defined in an outer scope.

  3. Assuming you’re not trying to hurt yourself with a knife you can do the following.

    #define A(b)
    int *b##__LINE__ = &b;
    printf(“*b: %dn”, *b##__LINE__);

    int b = 1;

    Obviously you should not use the macro twice on one line, but this way allows you to create variable names based on the line number, which should be unique enough (if that’s not the case, then you’re still doing something wrong.)

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s