0
#define SWAP(a,b) { a^=b ; b^=a ; a^=b; }

if (x < 0)
         SWAP(x,y);
else
         SWAP(y,x);    

My code given above doesn't work. It gives the following error while compiling.

trial.c:3:1: error: expected identifier or '(' before 'if'
trial.c:5:1: error: expected identifier or '(' before 'else'

I am trying to figure out the exact reason why it is not working. The preprocessor expands the macro as follows

if (x < 0)
         { 
           x^=y ; 
           y^=x ; 
           x^=y ; 
         }; 
else
         { 
           y^=x ; 
           x^=y ; 
           y^=x ; 
         };

I have a suspicion that the semi-colon at the end of curly braces is causing the problem. But I am not sure. Can someone explain?

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
liv2hak
  • 14,472
  • 53
  • 157
  • 270
  • 1
    The `;` after the ending `}` of your `if` and `else` statements shouldn't be there – SimpleJ Aug 26 '14 at 21:48
  • I know how to do it :) I want an explanation on why the above would not work. – liv2hak Aug 26 '14 at 21:49
  • see if..else..'s syntax. – BLUEPIXY Aug 26 '14 at 21:50
  • 1
    Do you expect `SWAP(x,y)` and `SWAP(y,x)` to do different things? – Keith Thompson Aug 26 '14 at 21:50
  • Possible duplicate of [C multi-line macro: do/while(0) vs scope block](http://stackoverflow.com/questions/1067226/c-multi-line-macro-do-while0-vs-scope-block). "Now, if your macro is defined in accordance with the second approach (just '{' and '}') the code will no longer compile, because the 'true' branch of 'if' is now represented by a compound statement. And when you put a ';' after this compound statement, you finished the whole 'if' statement, thus orphaning the 'else' branch (hence the compilation error)." – John Kugelman Aug 26 '14 at 21:51
  • @KeithThompson - No it doesn't but that is beside the point. :) – liv2hak Aug 26 '14 at 21:52
  • Beware `SWAP(A[i++],B[j++])` – Roddy Aug 26 '14 at 22:04

3 Answers3

5

If you want a macro that expands to a statement, the do/while(0) trick is the way to go.

But it's more flexible, when possible, to have a macro that expands to an expression. If you want to use it in a statement context, just add a semicolon:

#define SWAP(a, b) ( (a)^=(b), (b)^=(a), (a)^=(b) )

The problem with your original macro, or rather with the way you were using it, is that the added semicolon creates an extra statement. The syntax of an if-else statement is

if (expression ) statement else statement

By providing both a block { ... } and a semicolon, you have two statements between the if and the else.

The do/while(0) trick solves the semicolon problem. Writing a macro that expands to an expression (which is not always possible) avoids it altogether.

Some more observations:

You seem to be expecting SWAP(x, y) and SWAP(y, x) to do different things. They should behave identically.

The xor hack does let you swap to variables without using a temporary, but 99% of the time it makes more sense just to use a temporary. One problem with using a temporary is that you have to declare it, which means you have to know the type; that's not always possible. A drawback of the xor hack is that if the two operands are the same object (say, arr[i] and arr[j], where i ==j), it doesn't work. And it only works for integers.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
2

I have a suspicion that the semi-colon at the end of curly braces is causing the problem

That's exactly right! If you put a semicolon after a block with curly braces, that would be a new, empty, statement after the if. The compiler will parse it like this:

if (x < 0) // The "if"
         { // The body of the "if"
           x^=y ; 
           y^=x ; 
           x^=y ; 
         }
;    // <<== An empty statement after the one-sided "if"
else // <<== A "stray" else (syntax error)

This is the common trap with macros: if you want to use curly braces in your macro, use the do / while(0) trick (explained here):

#define SWAP(a,b) do { a^=b ; b^=a ; a^=b; } while (0)

Note: I hope this is a learning exercise, because there is no difference between swapping A with B and swapping B with A.

Community
  • 1
  • 1
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
1

Yes, a semicolon is not legal at this point. if(condition) { ... }; is not a well-formed if statement, which is either if(condition) { ... } or if(condition) statement;

A classical trick is to wrap the macro contents as a do { contents } while(0). In this case, a semicolon is expected directly following a macro invocation, which thus feels like a function.

It then expands to

if (x < 0)
    do { a^=b ; b^=a ; a^=b; } while (0);

Which is a valid form of an if expression: if(condition) statement;

Alexander Gessler
  • 45,603
  • 7
  • 82
  • 122