Меняет ли позиция этого побитового оператора поведение? - PullRequest
4 голосов
/ 18 октября 2019

Эти две строки кода эквивалентны?

P1->OUT &= ~(uint8_t)(1<<1);

P1->OUT &= (uint8_t)(~(1<<1));

Ответы [ 2 ]

3 голосов
/ 18 октября 2019

TL; DR - Нет! (Они не всегда одинаковы, а результат зависит от типа P1->OUT.)

I 'м, предполагая нормальный случай арифметики с 2-мя дополнениями. Если вы страдаете от арифметики величины знака или 1-го дополнения, вам придется думать намного сложнее, но вывод, вероятно, тот же (не всегда одинаковый).

Случай 1:

  • (1<<1) - это значение int (0x02).
  • (uint8_t)(1<<1) преобразуется в uint8_t, но значениеstill 0x02.
  • ~(uint8_t)(1<<1) снова преобразует значение uint8_t в int ( обычные арифметические преобразования ) и применяет к результату побитовый оператор инверсии ~.

Предполагая 32-битный тип int, вы получаете 0xFFFFFFFD до & с P1->OUT:

P1->OUT &= 0xFFFFFFFD;

Случай 2:

  • (1<<1) - это значение int (0x02).
  • ~(1<<1) - это значение int - предполагается 32-битное int,значение 0xFFFFFFFD.
  • (uint8_t)~(1<<1) приводится к uint8_t, обнуляя биты в старших байтах.

Предполагая 32-битный тип int,Вы получаете 0x000000FD до & с P1->OUT:

P1->OUT &= 0x000000FD;

Итак, значение в сочетании с P1->OUT отличается, но эффект зависит также от (неуказанного) типа P1->OUT. Если это uint8_t, то нет никакой разницы;если оно равно uint64_t, это может привести к огромным различиям, но оно также зависит от значения в P1->OUT до выполнения составного присваивания.

3 голосов
/ 18 октября 2019

Это зависит от типа P1->OUT и системы. Результат 1 << 1 имеет тип int.

Я рассматриваю общий случай int n; вместо 1 << 1

In

P1->OUT &= ~(uint8_t)(n);

операнд будет снова расширен до int перед ~ (целочисленным повышением), а ~ будет применен к int. В результате будут установлены все старшие биты 8 ... k. Если P1->OUT имеет ширину 8 битов, это нормально, но если у него больше битов, результат не тот, который вы ожидаете.

Этот вариант еще хуже:

P1->OUT &= (uint8_t)(~(n));

Операнд ~ будет снова применен к int и , которые будут преобразованы в uint8_t. теперь, если ~n на самом деле отрицательный (установлен бит знака) - а в случае ~(1 << 1) он будет отрицательным - это будет хорошо в реализациях с двумя дополнениями, но полностью неверно в дополнении 1и реализации со знаком и величиной, потому что битовые представления не будут одинаковыми.


Правильный способ переворота битов - всегда использовать unsigned int или более широкое дополнение к двум. число:

P1->OUT &= ~(1U << n);

или

P1->OUT &= (~(1U << n)) & 0xFF;

, чтобы избежать арифметических преобразований и целочисленных повышений, когда-либо производящих числа со знаком.

...