In C e C ++, quali metodi possono impedire l'uso accidentale dell'assegnazione (=) dove è necessaria l'equivalenza (==)?


15

In C e C ++, è molto semplice scrivere il seguente codice con un errore grave.

char responseChar = getchar();
int confirmExit = 'y' == tolower(responseChar);
if (confirmExit = 1)
{
    exit(0);
}

L'errore è che l'istruzione if avrebbe dovuto essere:

if (confirmExit == 1)

Come codificato, uscirà ogni volta, poiché si verifica l'assegnazione della confirmExitvariabile, quindi confirmExitviene utilizzato come risultato dell'espressione.

Ci sono buoni modi per prevenire questo tipo di errore?


39
Sì. Attiva gli avvisi del compilatore. Tratta gli avvisi come errori e non è un problema.
Martin York,


9
Nell'esempio fornito, la soluzione è semplice. Si assegna un valore booleano, quindi usarlo come un valore booleano: if (confirmExit).
Sicuro il

4
Il problema è che l'errore è stato commesso dai "designer" del linguaggio C, quando hanno scelto di usare = per l'operatore di assegnazione e == per il confronto di uguaglianza. ALGOL ha usato: =, perché in particolare voleva usare = per il confronto sull'uguaglianza, e PASCAL e Ada hanno seguito la decisione ALGOL. (Vale la pena notare che, quando DoD ha sollecitato un ingresso a base C nel bake-off DoD1, che alla fine ha ceduto Ada, Bell Labs ha rifiutato, dicendo che "C non era ora e non sarebbe mai stato abbastanza robusto per la missione critica di DoD software. "Si desidera che DoD e gli appaltatori abbiano ascoltato Bell Labs su questo.)
John R. Strohm,

4
@Giovanni, la scelta dei simboli non è il problema, è il fatto che le assegnazioni sono anche un'espressione che restituisce il valore assegnato, consentendo a = bo a == ball'interno di un condizionale.
Karl Bielefeldt,

Risposte:


60

La tecnica migliore è aumentare il livello di avviso del compilatore. Ti avvertirà quindi del potenziale incarico se condizionato.

Assicurati di compilare il codice con zero avvisi (cosa che dovresti comunque fare). Se vuoi essere pedante, imposta il tuo compilatore in modo da considerare gli avvisi come errori.

L'uso dei condizionali Yoda (mettendo la costante sul lato sinistro) era un'altra tecnica che era popolare circa un decennio fa. Ma rendono il codice più difficile da leggere (e quindi mantengono a causa del modo innaturale che leggono (a meno che tu non sia Yoda)) e non offrono alcun vantaggio maggiore rispetto all'aumento del livello di avviso (che ha anche vantaggi extra di più avvisi).

Gli avvisi sono errori logici nel codice e devono essere corretti.


2
Sicuramente segui gli avvertimenti, non i condizionali Yoda: puoi dimenticare di fare prima la costante condizionale con la stessa facilità con cui puoi dimenticare di fare ==.
Michael Kohne,

8
Ho avuto una piacevole sorpresa che la risposta più votata a questa domanda è "trasformare gli avvisi del compilatore in errori". Mi aspettavo l' if (0 == ret)orrore.
James,

2
@James: ... per non parlare del fatto che non funzionerà a == b!!
Emilio Garavaglia,

6
@EmilioGaravaglia: esiste una soluzione semplice per questo: basta scrivere 0==a && 0==b || 1==a && 1==b || 2==a && 2==b || ...(ripetere per tutti i valori possibili). Non dimenticare l' ...|| 22==a && 22==b || 23==a && 24==b || 25==a && 25==b ||errore obbligatorio ... o i programmatori di manutenzione non si divertiranno.
user281377

10

Puoi sempre fare qualcosa di radicale come testare il tuo software. Non intendo nemmeno test unitari automatizzati, solo i test che ogni singolo sviluppatore esperto fa per abitudine eseguendo il suo nuovo codice due volte, una volta confermando l'uscita e una volta no. Questo è il motivo per cui la maggior parte dei programmatori lo considera un problema.


1
Facile da fare quando il comportamento del programma è completamente deterministico.
James,

3
Questo particolare problema può essere difficile da testare. Ho visto persone morse da rc=MethodThatRarelyFails(); if(rc = SUCCESS){più di una volta, soprattutto se il metodo fallisce solo in condizioni difficili da testare.
Gort il robot il

3
@StevenBurnap: ecco a cosa servono gli oggetti finti. Il test corretto include il test delle modalità di errore.
Jan Hudec,

Questa è la risposta corretta
Lightness Races con Monica

6

Un modo tradizionale per impedire l'uso errato delle assegnazioni all'interno dell'espressione consiste nel posizionare la costante a sinistra e la variabile a destra.

if (confirmExit = 1)  // Unsafe

if (1=confirmExit)    // Safe and detected at compile time.

Il compilatore segnalerà un errore per l'assegnazione illegale a una costante simile alla seguente.

.\confirmExit\main.cpp:15: error: C2106: '=' : left operand must be l-value

La condizione riveduta sarebbe:

  if (1==confirmExit)    

Come mostrato dai commenti qui sotto, questo è considerato da molti un metodo inappropriato.



13
Va notato che fare le cose in questo modo ti renderà abbastanza impopolare.
riwalk

11
Si prega di non raccomandare questa pratica.
James,

5
Inoltre, non funziona se è necessario confrontare due variabili tra loro.
dan04,

Alcune lingue in realtà consentono di assegnare 1 a un nuovo valore (sì, so che la Q è un attacco c / c ++)
Matsemann

4

Sono d'accordo con tutti quelli che dicono "avvisi del compilatore" ma voglio aggiungere un'altra tecnica: le revisioni del codice. Se hai una politica di revisione di tutto il codice che viene eseguito il commit, preferibilmente prima che venga eseguito il commit, è probabile che questo tipo di cose vengano rilevate durante la revisione.


Non sono d'accordo. Soprattutto = invece di == può facilmente scorrere esaminando il codice.
Simon,

2

Innanzitutto, aumentare i livelli di avviso non fa mai male.

Se non vuoi che il tuo condizionale verifichi il risultato di un'assegnazione all'interno dell'istruzione if stessa, dopo aver lavorato con molti programmatori C e C ++ nel corso degli anni e non aver mai sentito che confrontare la costante prima if(1 == val)era una cosa negativa, tu potrebbe provare quel costrutto.

Se il tuo capo progetto approva che tu lo faccia, non preoccuparti di cosa pensano gli altri. La vera prova è se tu o qualcun altro puoi dare un senso al tuo codice tra mesi e anni.

Se hai intenzione di testare il risultato di un compito, tuttavia, l'uso di avvertenze più elevate potrebbe [probabilmente avrebbe] catturato il compito in una costante.


Sollevo un sopracciglio scettico nei confronti di qualsiasi programmatore non principiante che vede un condizionale Yoda e non lo capisce immediatamente. È solo un capovolgimento, non è difficile da leggere, e certamente non è così male come sostengono alcuni commenti.
underscore_d

@underscore_d Alla maggior parte dei miei datori di lavoro, i compiti all'interno di un condizionale erano disapprovati. Il pensiero era che era meglio separare l'incarico dal condizionale. Il motivo per essere più chiari, anche a scapito di un'altra riga di codice, era il fattore ingegneristico sostenitore. Ho lavorato in luoghi con basi di codice di grandi dimensioni e molta manutenzione backlogata. Capisco perfettamente che qualcuno potrebbe voler assegnare un valore e si ramifica sulla condizione risultante dall'assegnazione. Vedo che viene fatto più spesso in Perl e, se l'intento è chiaro, seguirò il modello di progettazione dell'autore.
octopusgrabbus

Stavo pensando ai soli condizionali Yoda (come la tua demo qui), non con il compito (come nel PO). Non mi importa il primo, ma non mi piace neanche il secondo. L'unica forma che uso deliberatamente è if ( auto myPtr = dynamic_cast<some_ptr>(testPtr) ) {quella di evitare di mantenere una nullptrportata inutile se il cast fallisce, il che è presumibilmente il motivo per cui C ++ ha questa limitata capacità di assegnazione in un condizionale. Per il resto, sì, una definizione dovrebbe avere una propria linea, direi: molto più facile da vedere a colpo d'occhio e meno incline a distorsioni della mente.
underscore_d

@underscore_d Risposta modificata in base ai tuoi commenti. Buon punto.
octopusgrabbus

1

In ritardo per la festa come sempre, ma l'analisi del codice statico è la chiave qui

La maggior parte degli IDE ora fornisce SCA oltre al controllo sintattico del compilatore e sono disponibili altri strumenti, inclusi quelli che implementano le linee guida MISRA (*) e / o CERT-C.

Dichiarazione: faccio parte del gruppo di lavoro MISRA C, ma sto postando a titolo personale. Sono anche indipendente da qualsiasi fornitore di utensili


-2

Usa semplicemente l'assegnazione della mano sinistra, gli avvisi del compilatore possono essere d'aiuto, ma devi assicurarti di raggiungere il livello giusto, altrimenti verrai sommerso da avvisi inutili o non ti verrà detto che vuoi vedere.


4
Sareste sorpresi da quanti pochi avvertimenti generano i moderni compilatori. Potresti non pensare che un avviso sia importante, ma nella maggior parte dei casi ti sbagli di grosso.
Kristof Provost,

Dai un'occhiata al libro "Scrivere codice solido" amazon.com/Writing-Solid-Microsoft-Programming-Series/dp/… . Inizia con una grande discussione sui compilatori e su quanti benefici potremmo ottenere dai messaggi di avviso e da un'analisi statica ancora più ampia.
Sviluppatore:

@KristofProvost Sono riuscito a ottenere Visual Studio per darmi 10 copie dello stesso identico avvertimento per la stessa riga di codice, quindi quando questo problema è stato "risolto" si sono verificati 10 avvisi identici sulla stessa riga di codice dicendo che il il metodo originale era migliore.
Lama invertita l'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.