Existe algum caso de uso válido para avaliação booleana?

4

Hoje aprendi sobre a avaliação booleana. No exemplo a seguir, Bar() será avaliado mesmo quando Foo() for verdadeiro.

if (Foo() | Bar())

Esta resposta em SO tem um exemplo:

if ((first = (i == 7)) & (second = (j == 10))) { //do something }

O caso de uso aqui seria que você deseja reutilizar os resultados, mas eu preferiria escrevê-lo assim:

first = (i == 7);
second = (j == 10);
if (first && second) { //do something }

Outro caso de uso seria que Bar() tem um efeito colateral que deve ser executado independentemente de Foo() ser verdadeiro ou falso. A pergunta é, isso pode ser um bom código ou indicaria um cheiro de código?

    
por Stijn 19.09.2013 / 14:58
fonte

6 respostas

13

O maior deles seria frustrando criptográfico ataques de temporização , eliminando as ramificações geradas pelos operadores de curto-circuito.

a timing attack is a side channel attack in which the attacker attempts to compromise a cryptosystem by analyzing the time taken to execute cryptographic algorithms. Every logical operation in a computer takes time to execute, and the time can differ based on the input; with precise measurements of the time for each operation, an attacker can work backwards to the input.

Information can leak from a system through measurement of the time it takes to respond to certain queries. How much such information can help an attacker depends on many variables: crypto system design, the CPU running the system, the algorithms used, assorted implementation details, timing attack countermeasures, the accuracy of the timing measurements, etc...

    
por 19.09.2013 / 15:02
fonte
8

É bonito chamar isso de avaliação booleana , mas o que os operadores realmente fazem (em linguagens no estilo C) é bitwise avaliação booleana .

Os exemplos que você citou funcionam conforme desejado, mas esse uso não funciona como desejado em geral. Se o seu segundo exemplo foi

if ((first = foo()) & (second = bar())) { /*do something*/ }

e foo() retornaram 1 e bar() retornaram 2 - ambos os quais são diferentes de zero e, portanto, verdadeiros em relação a if e && , então o if não será acionado e algo não será feito. Isso será surpreendente para os leitores do seu código.

Portanto, este é um uso obscuro e um cheiro de código. Se você quiser garantir que os efeitos colaterais de second = / Bar() ocorram, é melhor escrever esse desejo explicitamente usando instruções separadas, como você observou.

    
por 19.09.2013 / 15:46
fonte
3

Em circunstâncias normais, considero altamente provável que seja um erro de digitação, e que a lógica || ou & & foi planejado em vez do bit a bit | ou & operadores.

Existem situações em que você está lidando com hardware, em que o uso de operadores bit a bit é a implementação correta do código, mas fazê-lo dentro da instrução if é menos do que ideal para clareza de código e depuração.

Da mesma forma, se o propósito era garantir um tempo consistente de código (como no caso de ataques cronométricos de cronometragem), o uso de uma instrução IF provavelmente é uma má escolha de implementação.

    
por 19.09.2013 / 15:45
fonte
2

Um pouco questionável, mas vi o código que fez duas operações de retorno booleanas não triviais relacionadas e as combinou como seu Foo | Exemplo de barra. O único problema real é que muitas vezes um comentário deve ser colocado, é muito fácil olhar e ignorar o único operador quando tudo o que é esperado é a comparação booleana regular.

Se o seu ambiente usa ambos em um ambiente regular e as pessoas estão acostumadas a vê-lo, eu diria que está tudo bem, mas isso parece improvável. Em caso de dúvida, escreva o código mais fácil de ler. Surpreender a sintaxe estrangeira quando você está perseguindo um bug é uma droga.

    
por 19.09.2013 / 15:25
fonte
1

É geralmente aceito que a avaliação de curto-circuito (em oposição a "ansiosa") é a abordagem correta, tanto do ponto de vista da otimização quanto do ponto de vista da compreensão do código típico.

Considere:

while ((i < N) && (test(p[i]))) {
  do something
}

Esse tipo de código é EXTREMAMENTE comum. A avaliação ansiosa executará o teste no elemento da matriz, mesmo se o subscrito estiver explicitamente fora do intervalo. Isso geralmente é uma ideia muito ruim.

Se for criticamente necessário que ambas as partes sejam executadas, pode-se escrever algo como o seguinte:

bool part1, part2;
while ((part1 = (i < N)), (part2 = (test(p[i]))), (part1 && part2)) {
  do something
}

Isso usa o operador de vírgulas C para avaliar várias expressões e retornar o valor do último.

    
por 19.09.2013 / 15:37
fonte
1

Infelizmente, usando & como um substituto para & & está completamente errado . Não são apenas as estratégias de avaliação, os significados reais dos dois operadores são diferentes. Existem muitos valores inteiros em que x && y tem um valor diferente de x & y . Experimente o seguinte código:

#include<stdio.h>

int main()
{
        int i,j;
        int a,b;
        for (i = 0; i <= 100; i++) {
                for (j = i; j <= 100; j++) {
                        a = i && j;
                        b = i & j;
                        if (a && !b) {
                                printf("%d\t%d\n", i, j);
                        }

                }
        }

    return 0;
}

Quase um terço das combinações tem resultados diferentes ao comparar usando & e & & Como não é seguro assumir que um valor verdadeiro de uma função C é diferente de zero, você está solicitando problemas esperando que eles tenham o mesmo valor.

    
por 19.09.2013 / 19:03
fonte

Tags