Operador lógico ( ||
e &&
) vs. operador bit a bit ( |
e &
).
A diferença mais crucial entre um operador lógico e um operador bit a bit é que um operador lógico pega dois booleanos e produz um booleano, enquanto um operador bit a bit pega dois números inteiros e produz um número inteiro (nota: inteiros significa qualquer tipo de dados integral, não apenas int).
Para ser pedante, um operador bit a bit usa um padrão de bits (por exemplo, 01101011) e executa AND / OR em cada bit. Portanto, por exemplo, se você tiver dois números inteiros de 8 bits:
a = 00110010 (in decimal: 32+16+2 = 50)
b = 01010011 (in decimal: 64+ 16+2+1 = 83)
----------------
a & b = 00010010 (in decimal: 16+2 = 18)
a | b = 01110011 (in decimal: 64+32+16+2+1 = 115)
enquanto um operador lógico trabalha apenas em bool
:
a = true
b = false
--------------
a && b = false
a || b = true
Segundo, muitas vezes é possível usar um operador bit a bit no bool, pois true e false são equivalentes a 1 e 0, respectivamente, e acontece que se você traduzir true para 1 e false para 0, faça a operação bit a bit e converta diferente de zero para verdadeiro e zero para falso; acontece que o resultado será o mesmo se você tivesse acabado de usar o operador lógico (verifique isso no exercício).
Outra distinção importante é também que um operador lógico está em curto-circuito . Assim, em alguns círculos [1], você costuma ver pessoas fazendo algo assim:
if (person && person.punch()) {
person.doVictoryDance()
}
que se traduz em: "se a pessoa existe (ou seja, não é nula), tente dar um soco nela e, se o soco for bem-sucedido (ou seja, retornar verdadeiro), faça uma dança da vitória" .
Se você tivesse usado um operador bit a bit, isso:
if (person & person.punch()) {
person.doVictoryDance()
}
irá traduzir para: "se a pessoa existe (ou seja, não é nula) e o soco é bem-sucedido (ou seja, retorna verdadeiro), faça uma dança da vitória" .
Observe que no operador lógico em curto-circuito, o person.punch()
código pode não ser executado se person
for nulo. De fato, nesse caso específico, o segundo código produziria um erro de referência nulo se person
for nulo, pois ele tenta chamar person.punch()
, independentemente de a pessoa ser nula ou não. Esse comportamento de não avaliar o operando certo é chamado de curto-circuito .
[1] Alguns programadores se recusam a colocar uma chamada de função que tenha um efeito colateral dentro de uma if
expressão, enquanto para outros é um idioma comum e muito útil.
Como um operador bit a bit trabalha em 32 bits por vez (se você estiver em uma máquina de 32 bits), pode levar a um código mais elegante e mais rápido se você precisar comparar um grande número de condições, por exemplo,
int CAN_PUNCH = 1 << 0, CAN_KICK = 1 << 1, CAN_DRINK = 1 << 2, CAN_SIT = 1 << 3,
CAN_SHOOT_GUNS = 1 << 4, CAN_TALK = 1 << 5, CAN_SHOOT_CANNONS = 1 << 6;
Person person;
person.abilities = CAN_PUNCH | CAN_KICK | CAN_DRINK | CAN_SIT | CAN_SHOOT_GUNS;
Place bar;
bar.rules = CAN_DRINK | CAN_SIT | CAN_TALK;
Place military;
military.rules = CAN_SHOOT_CANNONS | CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT;
CurrentLocation cloc1, cloc2;
cloc1.usable_abilities = person_abilities & bar_rules;
cloc2.usable_abilities = person_abilities & military_rules;
// cloc1.usable_abilities will contain the bit pattern that matches `CAN_DRINK | CAN_SIT`
// while cloc2.usable_abilities will contain the bit pattern that matches `CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT`
Fazer o mesmo com operadores lógicos exigiria uma quantidade incômoda de comparações:
Person person;
person.can_punch = person.can_kick = person.can_drink = person.can_sit = person.can_shoot_guns = true;
person.can_shoot_cannons = false;
Place bar;
bar.rules.can_drink = bar.rules.can_sit = bar.rules.can_talk = true;
bar.rules.can_punch = bar.rules.can_kick = bar.rules.can_shoot_guns = bar.rules.can_shoot_cannons = false;
Place military;
military.rules.can_punch = military.rules.can_kick = military.rules.can_shoot_guns = military.rules.can_shoot_cannons = military.rules.can_sit = true;
military.rules.can_drink = military.rules.can_talk = false;
CurrentLocation cloc1;
bool cloc1.usable_abilities.can_punch = bar.rules.can_punch && person.can_punch,
cloc1.usable_abilities.can_kick = bar.rules.can_kick && person.can_kick,
cloc1.usable_abilities.can_drink = bar.rules.can_drink && person.can_drink,
cloc1.usable_abilities.can_sit = bar.rules.can_sit && person.can_sit,
cloc1.usable_abilities.can_shoot_guns = bar.rules.can_shoot_guns && person.can_shoot_guns,
cloc1.usable_abilities.can_shoot_cannons = bar.rules.can_shoot_cannons && person.can_shoot_cannons
cloc1.usable_abilities.can_talk = bar.rules.can_talk && person.can_talk;
bool cloc2.usable_abilities.can_punch = military.rules.can_punch && person.can_punch,
cloc2.usable_abilities.can_kick = military.rules.can_kick && person.can_kick,
cloc2.usable_abilities.can_drink = military.rules.can_drink && person.can_drink,
cloc2.usable_abilities.can_sit = military.rules.can_sit && person.can_sit,
cloc2.usable_abilities.can_shoot_guns = military.rules.can_shoot_guns && person.can_shoot_guns,
cloc2.usable_abilities.can_talk = military.rules.can_talk && person.can_talk,
cloc2.usable_abilities.can_shoot_cannons = military.rules.can_shoot_cannons && person.can_shoot_cannons;
Um exemplo clássico em que padrões de bits e operador bit a bit são usados está nas permissões do sistema de arquivos Unix / Linux.