Ajao Jumat
Ajao Jumat

Reputation: 7

Learning C #define statement (MACROS)

I'm currently reading 'Programming in C' by Stephen G. Kochan, and I stumbled upon an exercise (Chapter 13, Ex 5):

Write a macro SHIFT to perform the identical purpose as the shift function of Program 12.3.

Here is program 12.3:

unsigned int shift (unsigned int  value, int  n)
{
    if ( n > 0 )     // left shift 
        value <<= n;
    else             // right shift 
        value >>= -n;

    return value;
}

And here is my current code:

#include <stdio.h>
#define SHIFT(n)  (n) > 0 ? << (n) : >> (-(n))

unsigned int w1 = 0177777u, w2 = 0444u;
printf ("%o\t%o\n" w1 SHIFT(5), w1 << 5);

The condition in the macro should left shift if n is positive, and right shift otherwise.

Thus both arguments to printf should have the same value, but I keep getting a compiler error.

If I change the #define to:

#define SHIFT(n)  << n

... the code works, but I can't right shift with negative signs.

What is wrong with the conditional version of SHIFT?

Upvotes: 0

Views: 278

Answers (3)

Aditya Banerjee
Aditya Banerjee

Reputation: 372

Try this.

#define SHIFT(n) ((n) > 0 ? << (n) : >> (-(n))) 

Upvotes: 0

Jabberwocky
Jabberwocky

Reputation: 50775

Let's assume this code:

#include <stdio.h>

#define SHIFT(n)  (n) > 0 ? << (n) : >> (-(n))
// #define SHIFT(n)  << n

int main(int argc, char *argv[])
{
  unsigned int w1 = 0177777u, w2 = 0444u;
  printf("%o\t%o\n", w1 SHIFT(5), w1 << 5);
}

With #define SHIFT(n) << n your code works as expected because macros are just textual substitutions. So the compiler actually sees this:

  printf("%o\t%o\n", w1 << 5, w1 << 5);

instead of this:

  printf("%o\t%o\n", w1 SHIFT(5), w1 << 5);

But with #define SHIFT(n) (n) > 0 ? << (n) : >> (-(n)) the compiler sees this:

  printf("%o\t%o\n", w1 (5) > 0 ? << (5) : >> (-(5)), w1 << 5);

and this is syntaxically wrong and doesn't make any sense.

You need this:

#include <stdio.h>

#define SHIFT(value, n)  ... left as an exercise....

unsigned int shift(unsigned int  value, int  n)
{
  if (n > 0)     // left shift 
    value <<= n;
  else             // right shift 
    value >>= -n;

  return value;
}    

int main(int argc, char *argv[])
{
  unsigned int w1 = 0177777u, w2 = 0444u;
  printf("%o\t%o\n", SHIFT(w1, 5), shift(w1, 5));    // test case for positive shift value
  printf("%o\t%o\n", SHIFT(w1, -5), shift(w1, -5));  // test case for negative shift value
}

Upvotes: 3

Lundin
Lundin

Reputation: 213720

First of all, please note that this function in itself is artificial for the purpose of learning - you would never hide basic stuff like bit shifts behind a function or macro. Doing so would just make the program less readable.

Step one is to fix the function so that it handles the case where n is zero. Mainly we don't want to risk the compiler going haywire when presented with the expression value >> -0. (Less importantly, this also gives portability to exotic/fictional one's complement and sign & magnitude systems.)

inline unsigned int shift (unsigned int value, int n)
{
    if (n == 0)           // don't shift
        ;
    else if ( n > 0 )     // left shift 
        value <<= n;
    else /* n < 0 */      // right shift 
        value >>= -n;

    return value;
}

(inline is a micro-optimization hint to the compiler not to generate any overhead function-call code. Probably not needed on modern compilers.)

Then simply implement the macro just like the function. In any function-like macro, you need to use parenthesis around every macro parameter as well as the macro itself.

Since macros don't have return statement you have to ensure that it always evaluates to the desired value. This will in require that we use the conditional operator instead of if-else. Since ?: doesn't give the option of if - else if - else, this will have to be a nested ?: expression.

#define SHIFT(value, n) ( (n)==0 ? (value) :                  \
                                   ((n)>0 ? (value) <<= (n) : \
                                            (value) >>= -(n)) )

This would be one of the main reasons to avoid function-like macros. Instead of a clear, readable function, we have this nearly unreadable mess. Crappy programmers might even get tempted to write it as a horrible single line:

#define SHIFT(value, n) ((n)==0?(value):((n)>0?(value)<<=(n):(value)>>=-(n))) // BAD CODE

Upvotes: 0

Related Questions