Зачем использовать “|=”, “&=” и т.п. в C++ вместо “=”?

124
28 марта 2018, 05:43

Програмлю Arduino и читаю в интернете про PORT. Говорят, что какая-то там стабильность будет, если писать вместо = - |=, или &=. Так я так и не понял, для чего так писать и чем это отличается от обычного "равно"? Вот пример рабочего кода:

int main() {
DDRB = B00100000;
while(1) {
    PORTB |= B00100000;
    delay(1000);
    PORTB &= B00000000;
    delay(1000);
    }
}

Если заменить все эти |=, и &= на =, то всё будет работать точно также (как мне кажется) и даже не изменится размер прошивки (скетча). Так возвращаемся к вопросу: Зачем "|=", "&=" и т.д. в c++?

Answer 1

Если вопрос касается именно c++ - то это операторы.

|= - побитовое или с присвоением. &= - побитовое и с присвоением.

Подробнее про операторы тут

По сути, это упрощенный синтаксис для подобной записи:

a = a | b; // для |=
a = a & b; // для &=

Думаю, стало немного яснее, при чем тут равно. Далее попробуем разобраться, для чего же сами операторы | и &. Они называются побитовое или и побитовое и соответственно.

Оператор | берет побитовое представление операндов, и в результате выполнения этого оператора вы получите число, побитовое представление которого будет содержать единицы на тех позициях, на которых хоть в одном из двух побитовых представлений операндов стояла единица. Например:

4 | 5 = 5;
// или (в побитовом представлении)
100 | 101 = 101;

Заметим, что крайняя слева и справа единицы есть хоть в одном из двоичных представлений чисел, и поэтому после выполнения побитового или у нас есть число 101 (в бинарном представлении) или 5 (в десятичной системе счисления).

Оператор & берет побитовое представления операндов, и в результате выполнения этого оператора вы получите число, побитовое представление которого будет содержать единицы на тех позициях, на которых в обоих побитовых представлениях операндов стояли единицы. Например:

4 & 5 = 4;
// или (в побитовом представлении)
100 | 101 = 100;

Заметим, что осталась лишь одна единица, которая есть и в первом, и в правом операнде.

Ну и совсем вплотную подойдя к вашей задаче - | и & применяются обычно для работы с побитовыми флагами. Это сделано для экономии места. К примеру, у вас есть переменная размером 4 байта или 32 бита. В таком случае в этой переменной можно хранить аж 32 флага.

Чтобы добавить флаг, нужно применить |:

int flags = 0;
// Добавим флаг на третьей справа позиции, ведь бинарное представление 4 - 100
flags |= 0x4; 

Чтобы проверить, есть ли флаг, нужно применить &:

// Бинарное и - по сути маска. И если на этой позиции была 1,
// то результат будет отличен от 0 и условие будет верным
if (flags & 0x4) {
    // сделать что-то
}

Как-то так. Почему же работает с равно? Все просто. С равно вы по сути стираете предыдущее состояние переменной с флагами, и передаете ей только 1 флаг вместе с равно. Надеюсь, понятно объяснил...

UPD кстати, в вашем случае PORTB |= B00100000; добавляет флаг, а потом PORTB &= B00000000; убирает его (что логично, ведь на той позиции, на которой мы поставили флаг, в B00000000 единицы нет, и применив и мы по сути убрали флажок).

UPD2 кстати, в чистом c++ нет литералов вида B00000000, это фишка компилятора arduino. В c++ начиная с версии 14 можно использовать запись вида 0b00000000, чтобы представить бинарный литерал.

Answer 2

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

Но при программировании всяких - особенно вот таких - вещей это очень нужные операции.

Представьте себе, что ваш PORTB отвечает сразу за 8 светодиодов. И вам нужно, не меняя остальные - которые светятся, пусть светятся, которые нет - так и остаются - помигать третьим.

Пусть PORTB был, ну, скажем, B10011010 - четыре светодиода включены

В вашем варианте при присвоении

PORTB = B00000100

вы погасите их все, включив третий. Затем все выключите

PORTB = B00000000

А вот если сделать так:

PORTB = PORTB | B00000100

а затем

PORTB = PORTB & ~B00000100

переменная получит значения B10011110, а затем опять станет B10011010. Т.е. вы таким образом способны работать с отдельными битами.

Ну, а запись

PORTB = PORTB | B00000100

немного короче записывается как

PORTB |= B00000100

а

PORTB = PORTB & ~B00000100

как

PORTB &= ~B00000100

Если и это непонятно, то уж не знаю, что и делать...

Answer 3

Если заменить все эти |=, и &= на =, то всё будет работать точно также

Ничего подобного:

auto x{0x0E};
x &= 0x01;
assert(0 == x); 
auto y{0x0E};
y = 0x01;
assert(0x01 == y);

Заменить на равно можно, но с применением битового оператора:

auto z{0x0E};
z = z & 0x01;
assert(0x00 == z);
Answer 4

Я хотел бы добавить к ранее сказанным простой пример. Есть массив чисел, но не числа мне нужны, а нужен результат: В какой позиции нечетные числа?

 int n[] = {1, 12, 33, 44, 5};
    for (int i = 0; i < 5; ++i) {
        n[i] &= 1;  // нечетным элементам присваиваем 1           
    }
  • Тогда нечетным присвоится 1, а четным !1, т.е. 0
  • А если вместо оператора &= использовать |=, тогда всем нечетным числам прибавится 1, и будет массив выглядеть так 1, 13, 33, 45, 5
  • Если же просто присвоить, тогда все элементы будут единицами (естественно)...
READ ALSO
Работа с файлами. Выделение предложения

Работа с файлами. Выделение предложения

Всем здравствуйтеС помощью текстового редактора Блокнот создать файл

166
Что происходит при истечении SO_KEEPALIVE?

Что происходит при истечении SO_KEEPALIVE?

Что происходит c TCP-соединением при истечении таймаута SO_KEEPALIVE?

140
Освобождение памяти vector C++

Освобождение памяти vector C++

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

137