Por que '&&' e não '&'?

135

Por que é &&preferível &e ||preferível |?

Perguntei a alguém que programa há anos e sua explicação foi:

Por exemplo, em if (bool1 && bool2 && bool3) { /*DoSomething*/ }, bool1deve ser verdadeiro para testar o bool2que deve ser verdadeiro antes de passar para bool3etc. Se eu tivesse usado um único, &não há ordem para o teste, mesmo que todos eles tenham que ser verdadeiros. progredir para a próxima linha, então por que isso importa?

Nota: gostaria de salientar que sou o equivalente de programação de uma criança pequena e essa não é uma pergunta séria ou urgente. É mais uma questão de entender por que as coisas devem ser feitas de uma certa maneira e não de outra.

Dani
fonte
55
& e && | e || são completamente diferentes operadores
Felice Pollano
3
Retagged, pois isso não se aplica apenas para C #
Jonathan Dickinson
12
Revertida, porque as respostas já são específicas para C # e o funcionamento interno pode diferir um pouco em outros idiomas que geralmente têm o mesmo conceito.
Daniel Hilgarth
8
@ Felice: Eles são diferentes, mas dificilmente completamente diferentes. Eles são de fato muito semelhantes: x & ye x && ysempre avaliarão o mesmo resultado se xey forem expressões do tipo booleano. De fato, a única diferença nesse caso parece ser aquela em x & yy sempre é avaliada.
Joren
1
@slawekin: sugiro ler as respostas. Alguns escrevem extensivamente sobre as diferenças de desempenho. A resposta pode surpreendê-lo embora.
Abel

Respostas:

183

Na maioria dos casos, &&e ||são preferidos aos demais &e |porque os primeiros estão em curto-circuito, o que significa que a avaliação é cancelada assim que o resultado é claro.

Exemplo:

if(CanExecute() && CanSave())
{
}

Se CanExecuteretornar false, a expressão completa será false, independentemente do valor de retorno de CanSave. Por isso, CanSavenão é executado.

Isso é muito útil nas seguintes circunstâncias:

string value;
if(dict.TryGetValue(key, out value) && value.Contains("test"))
{
    // Do Something
}

TryGetValueretorna falsese a chave fornecida não for encontrada no dicionário. Devido à natureza de curto-circuito de &&, value.Contains("test")só é executado quando TryGetValueretorna truee, portanto, valuenão é null. Se você usasse o operador AND bit a bit& , obteria um NullReferenceExceptionse a chave não for encontrada no dicionário, porque a segunda parte da expressão é executada em qualquer caso.

Um exemplo semelhante, porém mais simples, é o seguinte código (conforme mencionado por TJHeuvel):

if(op != null && op.CanExecute())
{
    // Do Something
}

CanExecutesó é executado se opnão estiver null. Se opfor null, a primeira parte da expressão ( op != null) é avaliada falsee a avaliação do resto ( op.CanExecute()) é ignorada.

Além disso, tecnicamente, eles são diferentes, também:
&&e ||só pode ser usado em boolenquanto &e |pode ser usado em qualquer tipo integral ( bool, int, long, sbyte, ...), porque eles são operadores bit a bit. &é o operador AND bit a bit e |é o operador OR bit a bit .

Para ser muito exato, em C #, esses operadores ( &, |[e ^]) são chamados de "Operadores lógicos" (consulte a especificação de C # , capítulo 7.11). Existem várias implementações desses operadores:

  1. Para inteiros ( int, uint, longe ulong, no capítulo 7.11.1):
    Eles são implementados para calcular o resultado bit a bit dos operandos e o operador, ou seja, &é implementar para calcular a lógica bit a bit ANDetc.
  2. Para enumerações (capítulo 7.11.2):
    Elas são implementadas para executar a operação lógica do tipo subjacente da enumeração.
  3. Para bools e bools anuláveis ​​(capítulos 7.11.3 e 7.11.4):
    O resultado não é calculado usando cálculos bit a bit. O resultado é basicamente pesquisado com base nos valores dos dois operandos, porque o número de possibilidades é muito pequeno.
    Como os dois valores são usados ​​para a pesquisa, essa implementação não está em curto-circuito.
Daniel Hilgarth
fonte
31
Isso também pode ser útil para verificar se algo é nulo. Por exemplo: if(op != null && op.CanExecute()). Como a segunda causa não é avaliada quando a primeira não é verdadeira, isso é válido.
TJHeuvel
2
@TJHeuvel: Este é basicamente o mesmo uso que descrevi no meu TryGetValueexemplo. Mas sim, é outro bom exemplo disso.
Daniel Hilgarth
4
Boa resposta. Talvez você deva também adicionar um exemplo de como &ou |é usado com argumentos não-booleanos (isto é, o que os operadores fazem) para o benefício de todas as pessoas novas.
Zabba
81

Para explicar muito claramente o que isso significa (mesmo que as outras respostas indiquem isso - mas provavelmente use terminologia que você não entende).

O código a seguir:

if (a && b)
{
   Foo();
}

É realmente compilado para isso:

if (a)
{
    if (b)
    {
        Foo();
    }
}

Onde o código a seguir é compilado exatamente como está representado:

if (a & b)
{
   Foo();
}

Isso é chamado de curto-circuito. Em geral, você deve sempre usar &&e ||em suas condições.

Marcas de bônus: Há um cenário em que você não deveria. Se você estiver em uma situação onde o desempenho é crucial (e este é nano-segundos cruciais ) usar apenas um curto-circuito quando você deve (por exemplo, nullcontrolo) - como um curto-circuito é um ramo / salto; o que pode resultar em erro de ramificação na sua CPU; um &é muito mais barato que &&. Há também um cenário em que o curto-circuito pode realmente quebrar a lógica - dê uma olhada nesta minha resposta .

Diatribe / Monólogo : Quanto ao ramo de mis-previsão de que mais alegremente ignorar. Citando Andy Firth (que trabalha com jogos há 13 anos): "Esse pode ser um nível mais baixo que as pessoas pensam que precisam seguir ... mas elas estariam erradas. Entender como o hardware que você está programando para tratar ramos pode afeta o desempenho em um nível ENORME ... muito mais do que a maioria dos programadores pode apreciar a re-morte por mil cortes. "

  • Os desenvolvedores de jogos (e outros que trabalham em condições extremas de tempo real) chegam a reestruturar sua lógica para se adequar melhor ao preditor. Também há evidências disso no código mscorlib descompilado.
  • Só porque o .NET o protege desse tipo de coisa não significa que não é importante. Uma má previsão de ramificação é terrivelmente cara a 60 Hz; ou a 10.000 solicitações / segundo.
  • A Intel não teria ferramentas para identificar a localização de previsões errôneas, nem o Windows teria um contador de desempenho para isso, nem haveria uma palavra para descrevê-lo, caso não fosse um problema.
  • A ignorância sobre os níveis mais baixos e a arquitetura não faz com que alguém que esteja ciente deles esteja errado.
  • Sempre tente entender as limitações do hardware em que você está trabalhando.

Aqui está uma referência para os não-crentes. É melhor executar o processo no RealTime / High para reduzir o efeito do agendador: https://gist.github.com/1200737

Jonathan Dickinson
fonte
7
Sobre as "marcas de bônus": todos sabemos o que resulta da otimização prematura. :)
um CVn
6
@ Michael - é por isso que 'nanossegundos cruciais' está em negrito :). Os desenvolvedores de jogos AAA geralmente se preocupam com coisas assim - e você nunca sabe quem lerá as respostas; portanto, é sempre melhor documentar até os casos extremos / limitados.
Jonathan Dickinson
1
Essa marca de bônus é válida para C #? Eu pensaria que não, como o MSIL é interpretado, a menos que a expressão seja compilada até o código da máquina.
Jeremy McGee
7
@ Jeremy MSIL não é interpretado.
9788 Jonathan
2
@ TheD verificar novamente - eu adicionei um monólogo sobre por que você deve estar preocupado com isso. E, para sua informação, (x && y)traduz para LOAD x; BRANCH_FALSE; LOAD y; BRANCH_FALSE;onde (x & y)traduz LOAD x; LOAD y; AND; BRANCH_FALSE;. Um ramo versus dois.
11137 Jonathan Dickinson
68

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 personfor nulo. De fato, nesse caso específico, o segundo código produziria um erro de referência nulo se personfor 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 ifexpressã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.

Lie Ryan
fonte
3
+1 para o lado afeta o problema. Surpreendeu não foi mencionado anteriormente
Conrad Frix
3
o exemplo parece um pouco violento, mas parece que outras respostas focaram demais em curto-circuito e não o suficiente na diferença entre operar em números inteiros e em booleanos.
R0MANARMY
A função deve ser entendida antes que os detalhes da implementação (curto-circuito / efeitos colaterais) entrem em jogo. Que bom que você esclareceu a diferença principal: lógica booleana versus lógica inteira, não curto-circuito.
Abel
@ROMANARMY - violento, adoro a ironia dada ao seu monkiker. Bom trabalho
brumScouse
8

No caso de:

if (obj != null && obj.Property == true) { }

funcionaria como esperado.

Mas:

if (obj != null & obj.Property == true) { }

potencialmente poderia lançar uma exceção de referência nula.

gordinho
fonte
2

Curto e simples:

1 && 2= verdadeiro
porque
1 = verdadeiro (diferente de zero) em C
2 = verdadeiro (diferente de zero) em C

trueANDS logicamente com truepara dar true.

Mas

1 & 2= 0 = falso
porque
1 = 0001 no binário
2 = 0010 no binário

0001 ANDs bit a bit com 0010 para fornecer 0000 = 0 em decimal.

O mesmo para || e | operadores também ...!

Shrey
fonte
2
-1: Estamos falando de c # aqui ... 1 && 2é ilegal em c # #
Daniel Hilgarth 14/09
Mas este é um exemplo extremamente importante que explica por que você não pode simplesmente trocar & & & & (o que muitas pessoas parecem pensar).
bobobobo
1

&&é a versão de curto-circuito do &.

Se estamos avaliando false & true, já sabemos, olhando para o primeiro argumento, que o resultado será falso. A &&versão do operador retornará um resultado o mais rápido possível, em vez de avaliar toda a expressão. Há também uma versão semelhante do |operador ||,.

mdm
fonte
1
if (list.Count() > 14 && list[14] == "foo")

é seguro

if (list.Count() > 14 & list[14] == "foo")

falharia se a lista não tivesse o tamanho certo.

jalf
fonte
Não consigo imaginar que alguém possa escrever "if (list.Count ()> 14 & list [14] ==" foo ")" em vez de "if (list.Count ()> 14 && list [14] ==" foo ")". & simplesmente e naturalmente não pode ser usado para && neste caso, mesmo que & 's seja certamente seguro (lista [1] por exemplo).
Tien Do
1

Operadores C # devem explicar o porquê:

Essencialmente, ter dois &ou dois |significa que é mais um condicional do que um lógico, para que você possa ver a diferença entre os dois.

& Operador tem um exemplo de uso de um &.

Stuart Thomson
fonte
Ambos os links estão (efetivamente) quebrados (redireciona para "Documentação aposentada do Visual Studio 2005" ).
Peter Mortensen
1

OK, no valor nominal

    Boolean a = true;
    Boolean b = false;

    Console.WriteLine("a({0}) && b({1}) =  {2}", a, b, a && b);
    Console.WriteLine("a({0}) || b({1}) =  {2}", a, b, a || b);
    Console.WriteLine("a({0}) == b({1}) =  {2}", a, b, a == b);

    Console.WriteLine("a({0}) & b({1}) =  {2}", a, b, a & b);
    Console.WriteLine("a({0}) | b({1}) =  {2}", a, b, a | b);
    Console.WriteLine("a({0}) = b({1}) =  {2}", a, b, a = b);

produz a mesma resposta. No entanto, como você mostrou, se você tiver uma pergunta mais complexa, faça o seguinte:

if (a and b and c and d) ..

Se anão é verdade e talvez bseja uma função em que ela deve ocorrer , conecte-se a algo, faça isso, faça aquilo, tome uma decisão .. por que se preocupar? Perda de tempo, você sabe que já falhou. Por que fazer a máquina desligar e fazer um trabalho extra inútil?

Eu sempre usei &&porque coloco o mais provável de falhar primeiro, logo, menos cálculos antes de prosseguir quando não há sentido. Se não houver maneira de prever escolhas menos prováveis, como você tem um booleano para limitar a saída de dados, algo como:

if (limit && !MyDictionary.ContainsKey("name")) 
    continue;

Caso contrário limit, não se preocupe em verificar a chave, o que pode levar mais tempo.

BugFinder
fonte
1

Quando usado em uma expressão lógica, como uma instrução if, é &&preferível, pois interromperá a avaliação de expressões assim que o primeiro resultado falso for encontrado. Isso é possível porque um valor falso fará com que toda a expressão seja falsa. Da mesma forma (e novamente em expressões lógicas) ||é preferível, pois interromperá a avaliação de expressões assim que encontrar uma expressão verdadeira, pois qualquer valor verdadeiro fará com que toda a expressão seja verdadeira.

Se, no entanto, as expressões sendo editadas ou editadas juntas tiverem efeitos colaterais e você desejar que tudo isso ocorra como resultado de sua expressão (independentemente do resultado da expressão lógica), então &e |poderá ser usado. Por outro lado, o &&e|| operadores podem ser úteis como proteção contra efeitos colaterais indesejados (como um ponteiro nulo, causando a exceção).

Os operadores &e |também podem ser usados ​​com números inteiros e, nesse caso, eles produzem um resultado inteiro que são os dois operandos e-ed ou-ed juntos no nível de bit. Isso pode ser útil quando os bits binários de um valor inteiro são usados ​​como uma matriz de valores verdadeiros e falsos. Para testar se um determinado bit está ativado ou desativado, uma máscara de bit é bit a bit e ed com o valor Para ativar um pouco, a mesma máscara pode ser dividida em bits ou com o valor. Finalmente, para desativar um pouco, o complemento bit a bit (usando ~) de uma máscara é bit a bit e ed com o valor

int a = 0; // 0 means all bits off
a = a | 4; // set a to binary 100
if ((a & 4) != 0) {
    // will do something
}
a = a & (~4) // turn bit off again, a is now 000

Em idiomas diferentes de C #, é necessário ter cuidado com os modos lógico versus bit a bit de & e |. No código acima, a ifexpressão condicional da instrução (a & 4) != 0é uma maneira segura de expressar essa condição, mas em muitos idiomas C, as instruções condicionais podem simplesmente tratar zero valores inteiros como falsos e valores inteiros diferentes de zero como verdadeiros. (O motivo disso está relacionado às instruções condicionais do processador de ramificação disponíveis e a relação delas com o sinalizador zero que é atualizado após cada operação inteira.) Portanto, o ìfteste da instrução para zero pode ser removido e a condição pode ser reduzida para (a & 4).

Isso pode causar confusão e talvez até problemas quando expressões combinadas usando o operador bit a bit e o operador retornam valores que não possuem bits alinhados. Considere o seguinte exemplo em que os efeitos colaterais de duas funções são desejados, antes de verificar se ambos foram bem-sucedidos (conforme definido por eles retornando um valor diferente de zero):

if (foo() & bar()) {
    // do something
}

Em C, se foo()retornar 1 e bar()retornar 2, o "algo" não será feito porque 1 & 2é zero.

O C # requer instruções condicionais, como ifter uma oeprand booleana, e o idioma não permite que um valor inteiro seja convertido em um valor booleano. Portanto, o código acima geraria erros do compilador. Seria mais corretamente expresso da seguinte maneira:

if (foo() != 0 & bar() != 0) {
    // do something
}
graza
fonte
1

Se você é um programador C antigo, tenha cuidado . C # realmente me surpreendeu.

O MSDN diz para o |operador:

Binário operadores são predefinidos para os tipos integrais e bool . Para tipos integrais, | calcula o OR bit a bit de seus operandos. Para operandos booleanos, | calcula o OR lógico de seus operandos; isto é, o resultado é falso se e somente se ambos os seus operandos forem falsos.

(A ênfase é minha.) Os tipos booleanos são manipulados especialmente e, nesse contexto, a pergunta começa a fazer sentido, e a diferença é que outros já foram esperados em suas respostas:

&&e ||estão em curto-circuito. &e |avaliar os dois operandos.

e o que é preferível depende de vários fatores, como efeitos colaterais, desempenho e legibilidade do código, mas geralmente os operadores de curto-circuito também são preferíveis porque são mais bem compreendidos por pessoas com experiência semelhante à minha.

O motivo é: eu argumentaria assim: Como não existe um tipo booleano real em C, você pode usar o operador bit a bit |e ter seu resultado avaliado como verdadeiro ou falso em uma condição if. Mas essa é a atitude errada para C #, porque já existe um caso especial para tipos booleanos.

nalply
fonte
0

É importante, porque se o custo da avaliação do bool2 (por exemplo) for alto, mas o bool1 for falso, você economizou um pouco de computação usando && over &

gastador
fonte
0

Porque &&e ||são usados ​​para controle de fluxo como if/elsesão. Nem sempre é sobre condicionais. É perfeitamente razoável escrever como uma declaração, não como ifou whilecondicional, o seguinte:

 a() && b() && c() && d();

ou mesmo

 w() || x() || y() || z();

Não é apenas o fato de serem mais fáceis de digitar do que as if/elseversões equivalentes ; eles também são muito mais fáceis de ler e entender.

tchrist
fonte
0

&& e & significam duas coisas muito diferentes e oferecem duas respostas diferentes.

1 && 2produz 1 ("verdadeiro")
1 & 2gera 0 ("falso")

&&é um operador lógico - significa "verdadeiro se ambos os operandos forem verdadeiros"
&é uma comparação bit a bit. Significa "me diga qual dos bits está definido nos dois operandos"

tylerl
fonte
2
A questão é sobre c #. No C #, não há como converter um número em um booleano; portanto, 0 não é 'falso' e diferente de zero não é 'verdadeiro'; simplesmente não há equivalência.
Nate CK
Para converter número em bool, de maneira que 1 significa verdadeiro e 0 significa falso, diga "n! = 0" (presumo ... não estou familiarizado com C #). Na verdade, eu queria retirar esse comentário, pois ele não é bem pesquisado e não acho que seja útil ou realmente relevante para o comentário anterior, agora que penso mais a respeito, mas pressionei enter acidentalmente e agora acho que não posso cancelá-lo por isso aqui vamos nós, para o que vale a pena :-)
Don escotilha
1 && 2dá um erro de compilador: "O erro 4 do operador '&&' não pode ser aplicado a operandos do tipo 'int' e 'int'"
Peter Mortensen
0

A maneira mais rápida (e um pouco confusa) de explicar isso para as pessoas que NÃO PRECISAM conhecer as operações exatas do código ao fazer isso é

&& está verificando cada uma dessas condições até encontrar um falso e retornar o resultado inteiro como falso

|| está verificando cada uma dessas condições até encontrar um verdadeiro e retornar o resultado inteiro como verdadeiro.

& está fazendo MATHS com APONTA / TODAS as condições e lidando com o resultado.

| está fazendo MATHS baseado em ambas / todas as condições e lidando com o resultado.

Nunca me deparei com um ponto em que precisei usar & ou | dentro de uma instrução if. Eu o uso principalmente para cortar valores hexadecimais em suas cores de componentes usando deslocamento bit a bit.

POR EXEMPLO:

r = fullvalue >> 0xFF & 0xFF;
g = fullvalue >> 0xF & 0xFF;
b = fullvalue & 0xFF;

Dentro desta operação "& 0xFF" está forçando a olhar apenas para o valor binário. Eu pessoalmente não encontrei um uso para | ainda assim.

WORMSS
fonte
0

Simplesmente,

if exp1 && exp2

se exp1 for flase , não verifique exp2

mas

if exp1 & exp2

se exp1 for falseOu true verifique exp2

e raramente as pessoas usam &porque raramente querem verificar exp2 se exp1 forfalse

Fady
fonte