Как `(1U << 1) | (1U <

#c #if-statement #bit-manipulation #bitwise-operators

Вопрос:

Кто-нибудь, пожалуйста, объясните мне, как иметь одинаковую ценность VAL1 и VAL2 вести себя по-другому? Для первого if утверждения VAL1 равно нулю? Спасибо!

 #include <stdio.h>

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

int main()
{
    printf("VAL1: %drn", VAL1);
    printf("VAL2: %drn", VAL2);

    if (VAL1 == 0)
    {
        printf("TRUE 1rn");
    }

    if (VAL2 == 0)
    {
        printf("TRUE 2rn");
    }

    if (VAL1 == VAL2)
    {
        printf("TRUE 3rn");
    }
}
 

Выход:

 VAL1: 34
VAL2: 34
TRUE 1
TRUE 3
 

Комментарии:

1. Приоритет оператора разума. Полностью заключите макросы в скобки.

2. Действительно, это работает. Такая маленькая деталь, но все же большая разница. Но почему именно так обстоит дело?

3. #define VAL1 (1U << 1) | (1U << 5) ==> > #define VAL1 ((1U << 1) | (1U << 5)) Чтобы узнать, почему, запишите расширение.

4. Существует два основных правила для написания правильных макросов выражений: (1) В теле макрокоманды заключите каждый аргумент (если таковой имеется) в круглые скобки и (2) Заключите все тело макрокоманды в круглые скобки. Невыполнение любого из этих действий может привести к нежелательной ассоциации в созданном макросе. Вы забыли (2), и в результате часть вашего макроса более тесно сгруппирована с оператором сравнения, что приводит к нежелательному поведению.

5. @Tom Karzes хотя ваши правила подходят для большинства выражений, они могут вызвать синтаксические ошибки со строковыми литералами. Рассмотрим #define PREF( a) «pref» a, который может использоваться для добавления «pref» к строковому литералу, как в PREF( «suf»), который расширяется до «pref» «suf», который компилятор преобразует в «prefsuf». Если вы примените одно из своих правил к PREF, вы получите синтаксическую ошибку при расширении. По общему признанию, ПРЕФ немного противный, но это макросы!

Ответ №1:

Оба числа одинаковы, но когда ваш макрос расширяется в ваших условиях, он ведет себя не так, как вы ожидаете, из-за приоритета оператора.

Другими словами, вот что вы получаете, когда расширяете:

 (1U << 1) | (1U << 5) == 0
// is equivalent to
(1U << 1) | ((1U << 5) == 0)
 

Комментарии:

1. Решение: #define VAL1 ( (1U << 1) | (1U << 5) )

Ответ №2:

В if (VAL1 == 0) , замена макросов изменяет исходный код на if ((1U << 1) | (1U << 5) == 0) .

Выражение внутри if оценивается как (1U << 1) | ((1U << 5) == 0) , из-за желания сохранить прецеденты операторов C в соответствии с прецедентами более раннего языка B.

Таким образом , мы имеем if ((1U << 1) | ((1U << 5) == 0)) , в котором (1U << 1) значение равно 2 и ((1U << 5) == 0) равно 0 , в результате if (2 | 0) чего получается , что становится if (2) , поэтому if выполняется оператор с.

Принято определять замену макрокоманды, которая представляет собой выражение со скобками, чтобы гарантировать, что оно сгруппировано как один элемент:

 #define VAL1 ((1U << 1) | (1U << 5))