C - Побитовая манипуляция - PullRequest
0 голосов
/ 27 августа 2018

Я очень плохо знаком с C и пытаюсь понять, что побитовые операторы в CI нашли этот код передо мной (для преобразования 2 в 37)

int main(void)
{
    int x = 2;
    x = (x<<x<<x) | (x<<x<<x) | (x << !!x) | !!x ;
    printf("%d\n" , x );  // prints 37 
}

Теперь я впервыеувидеть что-то вроде этого (x<<x<<x) и я не понимаю, что он делает.Кто-нибудь может объяснить, пожалуйста, вторую строку в коде подробно?

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

Это известно как «запутывание»: написание ненужного сложного кода, чтобы сделать что-то более продвинутым, чем оно есть.

Глядя на подвыражение x<<x<<x, это простые логические сдвиги влево. оператор ассоциативности операторов сдвига слева направо, поэтому выражение равно (x<<x)<<x.

Мы вышли из смены 2 на 2 и получим 8. 8 << 2, сдвиг влево на 8 и получим 32:

x = 32 | 32 | (x << !!x) | !!x ;

Тогда для любого выражения с побитовым ИЛИ 32 | 32, где операнды 32 идентичны, это то же самое, что просто написать 32 без ИЛИ. Так что это эквивалентно:

x = 32 | (x << !!x) | !!x ;

!! - довольно распространенный, хотя и неясный прием в C для преобразования любого целого числа в логическое значение 0 или 1. !! - это не один оператор, а два раза логический оператор not !. Сначала у нас есть !2, что 0. Тогда !0, что дает 1. Нам осталось это:

x = 32 | (2 << 1) | 1;

2 << 1 равно 4, поэтому:

x = 32 | 4 | 1;

Запишите это в двоичном виде:

   0010 0000
OR 0000 0100
OR 0000 0001
------------
   0010 0101 = 0x25 hex = 37 dec
0 голосов
/ 27 августа 2018

Я бы порекомендовал вам разбить эту длинную строку на несколько маленьких кусочков (иногда более эффективно пробовать такие вещи; т. Е. Только чтение документации недостаточно):

int x = 2;
printf("%d\n", x); // prints 2
printf("%d\n", x << x); // prints 8
printf("%d\n", x << x << x); // prints 32
printf("%d\n", !!x); // prints 1
printf("%d\n", x << !!x); // prints 4

printf("%d\n", x); // prints 2 (just to become sure that x was not changed)

Итак,Вы знаете, что начальная длинная строка равна x = (32 | 32 | 1 | 4).Но это 32 + 4 + 1 = 37.

Давайте посмотрим в деталях:

Что такое << и >>?

Операторы сдвига по битам сдвигают значение слева на количество битов справа:

  • << сдвиг влево и добавление нулей на правом конце.
  • >>сдвиг вправо и добавление либо 0, если значение является типом без знака, либо расширение верхнего бита (для сохранения знака), если это тип со знаком.

Также в стандарте C говорится о E1 >> E2: " Если E1 имеет тип со знаком и отрицательное значение, результирующее значение определяется реализацией. " Арифметический сдвиг не гарантируется.

Поскольку << является ассоциативным, x<<x<<x оценивается как (x<<x)<<x.

См. также: Что такое операторы побитового сдвига (битового сдвига) и как они работают?

Что такое !!x?

Это унарное НЕ и унарное НЕ.!!x может использоваться как сокращение для (x != 0 ? 1 : 0).

...