Domanda

Lo standard ANSI impone agli operatori logici di essere cortocircuitati, in C o C ++?

Sono confuso perché ricordo il libro di K & amp; R che dice che il tuo codice non dovrebbe dipendere da queste operazioni in corto circuito, perché potrebbero non esserlo. Qualcuno potrebbe indicare dove nello standard si dice che le operazioni logiche sono sempre in corto circuito? Sono principalmente interessato al C ++, una risposta anche per il C sarebbe ottima.

Ricordo anche di aver letto (non ricordo dove) che l'ordine di valutazione non sia strettamente definito, quindi il codice non dovrebbe dipendere o assumere che le funzioni all'interno di un'espressione vengano eseguite in un ordine specifico: entro la fine di un'istruzione saranno state chiamate tutte le funzioni di riferimento, ma il compilatore ha la libertà di selezionare l'ordine più efficiente.

Lo standard indica l'ordine di valutazione di questa espressione?

if( functionA() && functionB() && functionC() ) cout<<"Hello world";
È stato utile?

Soluzione

Sì, per gli operatori || e & amp; & amp; sono richiesti ordini di corto circuito e valutazione in entrambi gli standard C e C ++.

Lo standard C ++ dice (ci dovrebbe essere una clausola equivalente nello standard C):

  

1.9.18

     

Nella valutazione delle seguenti espressioni

a && b
a || b
a ? b : c
a , b
     

usando il significato incorporato degli operatori in queste espressioni, c'è un punto sequenza dopo la valutazione della prima espressione (12).

In C ++ c'è una trappola extra: il cortocircuito si applica NON ai tipi che sovraccaricano gli operatori || e & amp; & amp; .

  

Nota 12: Gli operatori indicati in questo paragrafo sono gli operatori integrati, come descritto nella clausola 5. Quando uno di questi operatori è sovraccarico (clausola 13) in un contesto valido, designando così un utente definito funzione operatore, l'espressione designa una chiamata di funzione e gli operandi formano un elenco di argomenti, senza un punto di sequenza implicito tra di loro.

Di solito non è consigliabile sovraccaricare questi operatori in C ++ a meno che tu non abbia un requisito molto specifico. Puoi farlo, ma potrebbe interrompere il comportamento previsto nel codice di altre persone, soprattutto se questi operatori vengono utilizzati indirettamente tramite modelli di istanza con il tipo che sovraccarica questi operatori.

Altri suggerimenti

La valutazione del corto circuito e l'ordine di valutazione sono uno standard semantico obbligatorio in C e C ++.

In caso contrario, un codice del genere non sarebbe un linguaggio comune

   char* pChar = 0;
   // some actions which may or may not set pChar to something
   if ((pChar != 0) && (*pChar != '\0')) {
      // do something useful

   }

Sezione 6.5.13 Operatore AND logico della specifica C99 (collegamento PDF) dice

  

(4). A differenza del binario bit a bit & amp; operatore, il & amp; & amp; garanzie dell'operatore   valutazione da sinistra a destra; c'è un   punto di sequenza dopo la valutazione di   il primo operando. Se il primo   l'operando confronta uguale a 0, il   il secondo operando non viene valutato.

Allo stesso modo, la sezione 6.5.14 Operatore OR logico dice

  

(4) A differenza del bit per bit | operatore, il ||   l'operatore garantisce da sinistra a destra   valutazione; c'è un punto sequenza   dopo la valutazione del primo   operando. Se il primo operando viene confrontato   diverso da 0, il secondo operando è   non valutato.

Formulazioni simili sono disponibili negli standard C ++, check sezione 5.14 di questo progetto di copia . Come notano le pedine in un'altra risposta, se sostituisci & amp; & amp; o ||, quindi entrambi gli operandi devono essere valutati in quanto diventa una normale chiamata di funzione.

Sì, richiede questo (sia l'ordine di valutazione che il corto circuito). Nel tuo esempio se tutte le funzioni ritornano vere, l'ordine delle chiamate è rigorosamente dalla funzione A, quindi dalla funzione B e quindi dalla funzione C. Usato per questo tipo

if(ptr && ptr->value) { 
    ...
}

Lo stesso per l'operatore virgola:

// calls a, then b and evaluates to the value returned by b
// which is used to initialize c
int c = (a(), b()); 

Uno dice tra l'operando sinistro e destro di & amp; & amp; , || , , e tra il primo e il secondo / terzo l'operando di ?: (operatore condizionale) è un "punto sequenza". Tutti gli effetti collaterali vengono valutati completamente prima di quel punto. Quindi, questo è sicuro:

int a = 0;
int b = (a++, a); // b initialized with 1, and a is 1

Nota che l'operatore virgola non deve essere confuso con la virgola sintattica usata per separare le cose:

// order of calls to a and b is unspecified!
function(a(), b());

Lo standard C ++ dice in 5.14 / 1 :

  

L'& amp; & amp; gruppi di operatori da sinistra a destra. Gli operandi vengono entrambi implicitamente convertiti in tipo bool (clausola 4).   Il risultato è vero se entrambi gli operandi sono veri e falsi altrimenti. A differenza di & amp ;, & amp; & amp; garantisce da sinistra a destra   valutazione: il secondo operando non viene valutato se il primo operando è falso.

E in 5.15 / 1 :

  

Il || gruppi di operatori da sinistra a destra. Gli operandi sono entrambi implicitamente convertiti in bool (clausola 4). Restituisce vero se uno dei suoi operandi è vero e falso altrimenti. A differenza di |, || garantisce la valutazione da sinistra a destra; inoltre, il secondo operando non viene valutato se il primo operando viene valutato come vero.

Dice per entrambi accanto a quelli:

  

Il risultato è un bool. Tutti gli effetti collaterali della prima espressione tranne la distruzione dei provvisori (12.2) si verificano prima che la seconda espressione venga valutata.

Inoltre, 1.9 / 18 dice

  

Nella valutazione di ciascuna delle espressioni

     
      
  • a & amp; & amp; b
  •   
  • a || b
  •   
  • a? b: C
  •   
  • a, b
  •   
     

usando il significato incorporato degli operatori in queste espressioni (5.14, 5.15, 5.16, 5.18), c'è un punto sequenza dopo la valutazione della prima espressione.

Direttamente dal buon vecchio K & amp; R:

  

C garantisce che & amp; & amp; e || sono valutati da sinistra a destra & # 8212; vedremo presto casi in cui è importante.

Stai molto molto attento.

Per i tipi fondamentali si tratta di operatori di scelta rapida.

Ma se si definiscono questi operatori per la propria classe o tipo di enumerazione, questi non sono collegamenti. A causa di questa differenza semantica nel loro utilizzo in queste diverse circostanze, si consiglia di non definire questi operatori.

Per l'operatore & amp; & amp; e operator || per i tipi fondamentali l'ordine di valutazione è da sinistra a destra (altrimenti il ??taglio corto sarebbe difficile :-) Ma per operatori sovraccarichi che definisci, questi sono fondamentalmente zucchero sintattico per definire un metodo e quindi l'ordine di valutazione dei parametri non è definito.

Se ti fidi di Wikipedia:

  

[ & amp; & amp; e || ] sono semanticamente distinti dagli operatori bit-saggi & amp; e | perché non valuteranno mai l'operando giusto se il risultato può essere determinato solo da sinistra

http://en.wikipedia.org/wiki/C_(programming_language) #Characteristics

La tua domanda si riduce a precedenza dell'operatore C ++ e associatività. Fondamentalmente, nelle espressioni con più operatori e senza parentesi, il compilatore costruisce l'albero delle espressioni seguendo queste regole.

Per precedenza, quando hai qualcosa come A op1 B op2 C , puoi raggruppare le cose come (A op1 B) op2 C o A op1 (B op2 C) . Se op1 ha una precedenza maggiore di op2 , otterrai la prima espressione. Altrimenti, otterrai il secondo.

Per associatività, quando hai qualcosa come A op B op C , puoi di nuovo raggruppare questi come (A op B) op C o A op (B op C) . Se op ha lasciato l'associatività, si finisce con la prima espressione. Se ha la giusta associatività, finiamo con la seconda. Questo funziona anche per gli operatori allo stesso livello di precedenza.

In questo caso particolare, & amp; & amp; ha una precedenza maggiore rispetto a || , quindi l'espressione verrà valutata come (a! = " " ; & amp; & amp; it == seqMap.end ()) || isEven .

L'ordine stesso è "da sinistra a destra" nella forma dell'albero delle espressioni. Quindi valuteremo prima a! = & Quot; " & Amp; & amp; it == seqMap.end () . Se è vero, l'intera espressione è vera, altrimenti andiamo a isEven . Ovviamente la procedura si ripete in modo ricorsivo all'interno della sottoespressione di sinistra.


Idee interessanti, ma il concetto di precedenza ha le sue radici nella notazione matematica. La stessa cosa accade in a * b + c , dove * ha una precedenza maggiore di + .

Ancora più interessante / oscuro, per un'espressione senza paragoni A1 op1 A2 op2 ... opn-1 Un , in cui tutti gli operatori hanno la stessa precedenza, il numero di alberi di espressioni binarie che potremmo formare è dato dal cosiddetto numeri catalani . Per n di grandi dimensioni, questi crescono estremamente velocemente. d

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top