Precedência do && over ||

Como sei operador lógico && tem precedência mais alta que || . Na execução do código:

 #include  int main() { int i = 1, j =1, k = 1; printf("%d\n",++i || ++j && ++k); printf("%d %d %d",i,j,k); return 0; } 

está dando a saída:

 1 2 1 1 

que só é possível quando ++i || ++j && ++k ++i || ++j && ++k é avaliado assim:

 (++i) || (++j && ++k) 

Mas, de acordo com a regra de precedência do operador, ela deve ser avaliada como:

 (++i || ++j) && (++k) 

e, portanto, a saída deve ser:

 1 2 1 2 

O que está errado com isso?

NOTA: De acordo com o meu entendimento, acho que um operador de maior precedência avaliado da seguinte forma ( se for deixado associativo ):
1. Avalie sua expressão esquerda
2. Em seguida, avalie sua expressão correta (se necessário)
Estou errado?

Você diz:

que só é possível quando ++i || ++j && ++k ++i || ++j && ++k é avaliado assim:

 (++i) || (++j && ++k) 

Mas, de acordo com a regra de precedência do operador, ela deve ser avaliada como:

 (++i || ++j) && (++k) 

O primeiro agrupamento está correto porque a precedência de && é maior que a precedência de || . Então a expressão como um todo avalia o LHS do || , com o efeito colateral de incrementar i , que é avaliado como verdadeiro. Isso significa que o RHS do || (a expressão && ) não é avaliada de todo porque não é necessária para determinar a verdade da expressão geral.

Então, o compilador está correto; você entendeu mal a precedência de alguma forma.


Por que o primeiro agrupamento está correto? De acordo com o primeiro agrupamento || tem precedência mais alta que && . O que está errado comigo?

Você não entende a precedência, parece, ou não entende a interação de precedência com a ordem de avaliação. O primeiro agrupamento dá maior precedência ao && .

Se você tem a + b * c , onde * tem uma precedência maior que + , então ele é avaliado como a + (b * c) , não é? Altere + para || e * para && e as expressões são isomórficas e a interpretação é semelhante.

A grande diferença entre a expressão aritmética e a expressão lógica é que os operandos da expressão lógica devem ser avaliados da esquerda para a direita, mas os operandos da expressão aritmética não; o compilador poderia avaliar b * c antes de avaliar a (mas deve avaliar b * c antes de fazer a adição). Em contrapartida, na expressão lógica ( a || b && c ), o compilador deve avaliar a antes de avaliar b && c , e quando se torna verdadeiro, ele não deve avaliar b ou c , muito menos b && c .

O || short-circuits do operador – se o primeiro operando for avaliado como verdadeiro (diferente de zero), ele não avaliará seu segundo operando.

Isso também é verdade para o && , ele não usa seu segundo operando se o primeiro for falso. Essa é uma otimização possível porque qualquer valor booleano OU true é verdadeiro e, da mesma forma, qualquer valor booleano AND false é sempre false.


OK, então você está confundindo precedência com a ordem de avaliação. Nada é contraditório aqui em tudo:

 ++i || ++j && ++k 

é agrupado como

 (++i) || (++j && ++k) 

desde && tem maior precedência. Mas então o LHS da operação OR é verdadeiro, então o RHS inteiro com sua operação AND é descartado, não é avaliado.


Para sua nota na edição: sim, você está errado: a precedência do operador ainda não é a mesma da ordem de avaliação. É apenas agrupar.

Em primeiro lugar, como você mesmo disse, && tem precedência mais alta, o que significa que o agrupamento de operandos deve ser

  (++i) || (++j && ++k) 

Por que você está dizendo que “de acordo com a precedência do operador” deve ser (++i || ++j) && (++k) não está claro para mim. Isso apenas contradiz o que você mesmo disse.

Em segundo lugar, a precedência do operador não tem absolutamente nada a ver com a ordem de avaliação. A precedência do operador determina o agrupamento entre os operadores e seus operandos (isto é, a precedência do operador diz qual operando pertence a qual operador).

Enquanto isso, a ordem de avaliação é uma história completamente diferente. Ele permanece indefinido ou definido por um conjunto de regras completamente diferente. Em caso de || e && operadores, a ordem de avaliação é de fato definida como da esquerda para a direita (com conclusão antecipada obrigatória sempre que possível).

Assim, as regras de precedência do operador informam que o agrupamento deve ser

  (++i) || ((++j) && (++k)) 

Agora, as regras de ordem de avaliação dizem que primeiro avaliamos ++i , então (se necessário) avaliamos ++j , então (se necessário) avaliamos ++k , então avaliamos && e finalmente avaliamos || .

Já que você está entendendo mal a precedência, vamos tentar esclarecer isso com um exemplo matemático. Multiplicação e divisão têm uma precedência maior que adição e subtração. O que significa que esta expressão:

 a + b * c - d / e 

Pode ser escrito assim:

 a + (b * c) - (d / e) 

Desde que você afirmou corretamente que && tem precedência mais alta que || , esta expressão:

 i || j && k 

pode ser escrito assim:

 i || (j && k) 

Você pode pensar nisso como “a operação com a maior precedência fica primeiro entre parênteses”, se isso ajudar.

(Mas a precedência é diferente da avaliação – se i for verdadeiro, então (j && k) nunca será avaliado.)