0

Would someone please explain me how having the same value VAL1 and VAL2 behave differently? For the first if statement VAL1 is equal to zero? Thanks!

#include <stdio.h>

#define VAL1 (1U << 1) | (1U << 5)
#define VAL2 0x22

int main()
{
    printf("VAL1: %d\r\n", VAL1);
    printf("VAL2: %d\r\n", VAL2);

    if (VAL1 == 0)
    {
        printf("TRUE 1\r\n");
    }

    if (VAL2 == 0)
    {
        printf("TRUE 2\r\n");
    }

    if (VAL1 == VAL2)
    {
        printf("TRUE 3\r\n");
    }
}

Output:

VAL1: 34
VAL2: 34
TRUE 1
TRUE 3
stardust
  • 343
  • 3
  • 17
  • 4
    Mind operator precedence. Fully parenthesize macros. – njuffa Jun 27 '21 at 10:01
  • Indeed, it works. Such a small detail, yet a big difference. But why exactly is it like that? – stardust Jun 27 '21 at 10:04
  • 2
    `#define VAL1 (1U << 1) | (1U << 5)` ==> `#define VAL1 ((1U << 1) | (1U << 5))` To find out why, write down the expansion. – Weather Vane Jun 27 '21 at 10:04
  • 2
    There are two cardinal rules for writing correct expression macros: (1) In the macro body, wrap each argument (if any) in parentheses, and (2) Wrap the entire macro body in parentheses. Failing to do either of these can result in undesired association in the instantiated macro. You forgot (2), and as a result, part of your macro grouped more tightly with a comparison operator, resulting in undesired behavior. – Tom Karzes Jun 27 '21 at 11:04
  • @Tom Karzes while your rules are fine for most expressions, they can cause syntax errors with string literals. Consider #define PREF( a) "pref" a which could be used to prepend "pref" to a string literal as in PREF( "suf") which expands to "pref" "suf" which the compiler converts to "prefsuf". If you apply either of your rules to PREF you get a syntax error on expansion. Admittedly PREF is a bit nasty, but that's macros! – dmuir Jun 27 '21 at 12:43
  • @dmuir That's not an expression macro. An expression macro expands to an *expression*, not a string constant that can be concatenated with other string constants. My wording was very precise. Perhaps if you read it again you'll understand it better. This is well-known C macro style that any experienced C programmer knows. – Tom Karzes Jun 27 '21 at 14:49
  • [The need for parentheses in macros in C (duplicate)](https://stackoverflow.com/q/10820340/995714) – phuclv Jun 27 '21 at 14:56

2 Answers2

2

The numbers are both the same, but when your macro expands in you conditions, it does not behave as you expect due to operator precedence.

In other words, here's what you get when expanded:

(1U << 1) | (1U << 5) == 0
// is equivalent to
(1U << 1) | ((1U << 5) == 0)
Ulysse BN
  • 10,116
  • 7
  • 54
  • 82
1

In if (VAL1 == 0), macro replacement changes the source code to if ((1U << 1) | (1U << 5) == 0).

The expression inside the if is evaluated as (1U << 1) | ((1U << 5) == 0), due to a desire to keep C’s operator precedences consistent with those of the earlier language B.

Thus we have if ((1U << 1) | ((1U << 5) == 0)), in which (1U << 1) evaluates to 2 and ((1U << 5) == 0) evaluates to 0, resulting in if (2 | 0), which becomes if (2), so the statement with the if is executed.

It is customary to define a macro replacement that is an expression with parentheses to ensure it is grouped as one item:

#define VAL1 ((1U << 1) | (1U << 5))
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312