Return to index
Function-like Macros

Function-like Macros

Take a second. Try to call to mind the use we've made of the C preprocesssor. What have we done:

Now, let's think for a moment about functions. Functions are great. They give us a way to reuse code -- we can write a function once and use them over-and-over again. This gets us much bang for the buck -- and makes the code maintainble by ensuring that corrections or changes need only be made in once place. And, it helps to make code meaningful by identifying segments of code with meaningful and descriptive names.

But, these benefits come at a cost. Later thise semester, we'll learn a little bit about the mechanics of a function call. For now, let's just recall something we've said before -- there is overhead associated with making a function call and in returning from a function. It takes time.

Normally, for most functions, this small amount of overhead is no big deal. But, for really small functions, it can take longer to make the call and to return than to actually do the work. And, in certain essoteric types of programming, it can, under certain circumstances, be diifuclt or impossible to make function calls, because function calls change the stack.

We can get around this by using #define to create a function-like macro. The basic idea is that we define something that looks like a function within a #define. And then, after that, we can basically use it like a function. But, there are three big differences:

Let's take a quick look at the following function-like macro and its use. We see that the macro si replaced and explanded by the preprocessor.

  #define multiply(x,y) (x * y)

  ...

  int c =  multiply(10,5); /* int c = (10 * 5);
  

Now, let's ocnsider the example below. It is broken. Do you see why? The extra space breaks the preprocessor -- the first space acts to separate the macro from its definition.

  #define multiply (x,y) (x * y)
  /*              ^            */
  /*              |            */
  /*          Evil space       */

  

Let's take a look at another interesting case. Here is an example that probably doesn't work as you'd expect:

  #define multiply(x,y) (x * y)

  ...

  int c =  multiply(10+5,5); /* int c = (10+5 * 5);

  

Take a second look at the code above. Notice that it seems that we are asking to evaluate "((10+5)*5) = 75". But, it is expanded without regard to the grouping, so the order of oeprations is controlling -- and multiply beats add: "Please excuse my dear aunt sally". So, we get "(10+5 * 5) = (10 + (5*5)) = 250"

To fix this, when defining a macro, we always ()-parenthesize each-and-every use of the macro's arguments as follows. Notice how the parentheses force the right grouping:

  #define multiply(x,y) ((x) * (y))

  ...

  int c =  multiply(10+5,5); /* int c = ((10+5) * (5));
  

Having said that, I'd like to reiterate and amplify the above: Always parenthesize each and every use of a paramter within a macro -- whether you think you need it or not. We've seen one alligator -- there are more in the swamp. Do go looking for them -- just stay safe in the boat. This is another example of defensive programming.