Trouble With Symbolic Constants

When you define a symbolic constant with a preprocessor directive make sure you understand clearly what you are doing.

Operators in Symbolic Constants

Let's say you define a set of constants like this:

#define FOO 8
#define BAR 4
#define FOOBAR FOO+BAR

There is not actually much wrong with that, except that something like the following could happen:

printf ("The answer is %d\n", 6*FOOBAR);

The result of this code is a program which prints "the answer is 52", but 6 times 12 (FOO plus BAR) is 72, so what went wrong? The problem is that the printf line gets expanded to this:

printf ("The anwser is %d\n", 6*8+4);

The regular rules of precedence in C say that multiplication should be done before addition, so the printf displays 6 time 8 (48) plus 4, which is 52. To get the answer you were probably expecting you should define FOOBAR like this:

#define FOOBAR (FOO+BAR)

If that is done the printed result is the correct number, 72.

Confused Use of Symbolic Constants

Here is some code based on something which I actually saw someone try to use at one point:

struct foo
{
  /* ... some members ... */
};

main ()
{
  struct foo fooStructure;

#define FOOPTR (&fooStructure)

  ClearFooStructure ();

  /* ... other code ... */

  return;
}

ClearFooStructure ()
{
  struct foo* fooStructure;

  fooStructure = (struct foo*) FOOPTR; /* Why is this cast necessary? */

  /* ... code for clearing the structure pointed to by fooStructure ... */
}

The author of the code came to me with a couple of questions. He inserted some debugging printf statements into his program and found that the value given by FOOPTR in main was different from that in the ClearFooStructure function (and in the fooStructure pointer in that function). He also wondered why he needed the cast to a pointer to struct foo on the line with the comment "Why is this cast necessary?"

The problem is that the author was confusing preprocessing, which is an entirely text substitution oriented step, with the actual programming language. If we expand FOOPTR in the main function it gives us a pointer to the variable called fooStructure. In main the variable fooStructure is a valid struct foo variable and FOOPTR is a valid variable. However, the positioning of the define does not magically associate FOOPTR with the variable fooStructure at the point it is defined. The preprocessor just substitutes text. Thus in ClearFooStructure we get something like this:

 struct foo* fooStructure;

 fooStructure = (struct foo*) (&fooStructure);

Now it should be evident (if you know some C) why the cast is necessary and why this doesn't do the right thing. The pointer fooStructure is being set to point at itself. The cast forces the compiler to let you make a pointer to a struct foo point at the space allocated for a pointer to a struct foo (not an actual struct foo). If the author had named the pointer something else there would have been no fooStructure variable at all and the file would have failed to compile.

There are several morals here. If a cast is necessary where you don't think one should be necessary, or if a warning message occurs, the compiler is usually trying to tell you that you are doing something unconventional and probably not what you meant. (It is both a strength and a weakness of C that it lets you tell the compiler that you know what you are doing and force it to do strange things. PASCAL, I hear, just wouldn't allow this sort of thing, which can be frustrating if you actually need to do something strange.) The second moral is that preprocessor symbolic constants are not part of the programming language. Preprocessing should be viewed as something separate from the language itself.