Saturday, June 11, 2016

C/C++ #define Directive Not-So-Obvious Mistake

Consider the following code below:
#include <stdio.h>
#define ABS(x) (x>=0 ? x : -x)
int abs (int x) {
   return x >= 0 ? x : -x;
}
int main () {
   printf ("ABS(1) = %d\n", ABS(1));
   printf ("ABS(-1) = %d\n", ABS(-1));
   printf ("ABS(2-1) = %d\n", ABS(2-1));
   printf ("ABS(1-2) = %d\n", ABS(1-2));
   printf ("abs(1) = %d\n", abs(1));
   printf ("abs(-1) = %d\n", abs(-1));
   printf ("abs(2-1) = %d\n", abs(2-1));
   printf ("abs(1-2) = %d\n", abs(1-2));
   return 0;
}

The code defines the absolute value evaluation procedure using both the C directive and a regular subroutine. In the main function, we are printing out four example values to verify if both methods work correct. What do you think the expected output would be?

Guess the answer first, and copy the code run it.
$ gcc ABS_test.c
$ ./a.out

Does your guess agree with the output? If so, then you must be an experienced C/C++ programmer! If not, do not worry. I did not get it the first time either. Both the C directive ABS(x) and C subroutine abs(x) are defined by the same expression, but one is erroneous while the other is fine. 

It turns out that the ABS(x) directive definition is incorrect and requires an extra parenthesis pair around last x:
#define ABS(x) (x>=0 ? x : -(x))

This is because the C directive is simply literal, replacing whatever expression in the parenthesis of ABS(...) into the definition. Below is actual code that is being compiled by the compiler:
ABS(1) ---> 1 >= 0 ? 1 : -1
ABS(-1) ---> -1 >= 0 ? -1 : --1
ABS(2-1) ---> 2-1 >= 0 ? 2-1 : -2-1
ABS(1-2) ---> 1-2 >= 0 ? 1-2 : -2-1

So, it should now be clear to you why the last expression's output was -3! By the way, the subroutine abs(x) is fine because the compiler will evaluate the value of whatever in the parenthesis of abs(...) before actually calling the function.

No comments:

Post a Comment