Esistono casi d'uso validi per una valutazione booleana entusiasta?

4

Oggi ho appreso la briosa valutazione booleana. Nell'esempio seguente, Bar() verrà valutata anche quando Foo() è true.

if (Foo() | Bar())

Questa risposta su SO ha un esempio:

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

Il caso d'uso qui è che vuoi riutilizzare i risultati, ma preferisco scriverlo in questo modo:

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

Un altro caso d'uso sarebbe che Bar() ha un effetto collaterale che dovrebbe essere eseguito indipendentemente dal fatto che Foo() sia vero o falso. La domanda è, può essere un buon codice o potrebbe indicare un odore di codice?

    
posta Stijn 19.09.2013 - 14:58
fonte

6 risposte

13

Il più grande sarebbe contrastare crittografico attacchi di temporizzazione eliminando i rami generati dagli operatori di cortocircuito.

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...

    
risposta data 19.09.2013 - 15:02
fonte
8

È carino chiamare questo entusiasta valutazione booleana , ma ciò che gli operatori effettivamente fanno (in linguaggi in stile C) è bitwise valutazione booleana .

Gli esempi che hai citato funzionano come desiderato, ma questo uso non funziona come desiderato in generale. Se il tuo secondo esempio era

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

e foo() ha restituito 1 e bar() ha restituito 2 - entrambi sono diversi da zero e quindi veri fino a if e && sono interessati - quindi il se non sparerà e qualcosa non sarà fatto. Ciò sarà sorprendente per i lettori del tuo codice.

Quindi questo è un uso oscuro e un odore di codice. Se vuoi essere sicuro che gli effetti collaterali di second = /% diBar() avvengano, è meglio scrivere quel desiderio esplicitamente usando istruzioni separate, come hai notato.

    
risposta data 19.09.2013 - 15:46
fonte
3

In circostanze normali, ritengo altamente probabile che sia un errore di battitura e che il logico || o & & era inteso piuttosto che bit a bit | o & operatori.

Ci sono situazioni in cui hai a che fare con l'hardware, dove l'uso di operatori bit a bit è l'implementazione corretta del codice, ma poi, farlo all'interno dell'istruzione if non è l'ideale per la chiarezza del codice e il debug.

Allo stesso modo, se lo scopo era quello di assicurare un timing coerente del codice (come nel contrastare gli attacchi temporali crittografici), è probabile che l'uso di un'istruzione IF sia una scelta sbagliata di implementazione.

    
risposta data 19.09.2013 - 15:45
fonte
2

Un po 'discutibile, ma ho visto il codice che ha eseguito due operazioni di reso booleane non banali, correlate e combinato come il tuo Foo | Esempio di barra. L'unico vero problema è che spesso deve essere inserito un commento, è troppo facile dare un'occhiata e ignorare il singolo operatore quando tutto ciò che è previsto è il confronto booleano regolare.

Se il tuo ambiente utilizza entrambi in un ambiente normale e le persone sono abituate a vederlo, direi che va bene, ma sembra improbabile. In caso di dubbi, scrivi il codice che è più facile da leggere. Sorprendi la sintassi straniera quando stai inseguendo un bug solo tipo di schifo.

    
risposta data 19.09.2013 - 15:25
fonte
1

È generalmente accettato che la valutazione di cortocircuito (al contrario di "desiderosa") sia l'approccio corretto, sia dal punto di vista dell'ottimizzazione che dal punto di vista della comprensione del codice tipico.

Si consideri:

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

Questo tipo di codice è ESTREMAMENTE comune. La valutazione Eager eseguirà il test sull'elemento dell'array anche se l'indice è esplicitamente fuori intervallo. Questa è generalmente una pessima idea.

Se è necessario che entrambe le parti siano eseguite, è possibile scrivere qualcosa come la seguente:

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

Questo utilizza l'operatore virgola C per valutare diverse espressioni e restituire il valore dell'ultima.

    
risposta data 19.09.2013 - 15:37
fonte
1

Sfortunatamente, usando & in sostituzione di & & è piatto fuori sbagliato . Non sono solo le strategie di valutazione, i significati reali dei due operatori sono diversi. Esistono molti valori interi in cui x && y ha un valore diverso da x & y . Prova il seguente codice:

#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;
}

Quasi un terzo delle combinazioni ha risultati diversi quando si confrontano usando & e & & amp ;. Dal momento che non è sicuro assumere che un valore vero da una funzione C sia diverso da zero, stai cercando dei problemi aspettandoti che abbiano lo stesso valore.

    
risposta data 19.09.2013 - 19:03
fonte

Leggi altre domande sui tag