Perché dovrei sempre abilitare gli avvisi del compilatore?


302

Sento spesso che durante la compilazione di programmi C e C ++ dovrei "abilitare sempre gli avvisi del compilatore". Perché è necessario? Come lo faccio?

A volte sento anche che dovrei "trattare gli avvisi come errori". Dovrei? Come lo faccio?

Risposte:


341

Perché abilitare gli avvisi?

I compilatori C e C ++ sono notoriamente cattivi nel segnalare alcuni errori comuni del programmatore per impostazione predefinita , come:

  • dimenticando di inizializzare una variabile
  • dimenticare returnun valore da una funzione
  • argomenti printfe scanffamiglie non corrispondenti alla stringa di formato
  • una funzione viene utilizzata senza essere dichiarata in anticipo (solo C)

Questi possono essere rilevati e segnalati, di solito non di default; questa funzione deve essere esplicitamente richiesta tramite le opzioni del compilatore.

Come abilitare gli avvisi?

Questo dipende dal tuo compilatore.

Compilatori C ++ di Microsoft C e capire interruttori piace /W1, /W2, /W3, /W4e /Wall. Usa almeno /W3. /W4e /Wallpuò emettere avvisi spuri per i file di intestazione del sistema, ma se il progetto viene compilato in modo pulito con una di queste opzioni, cercalo. Queste opzioni si escludono a vicenda.

La maggior parte degli altri compilatori comprende opzioni come -Wall, -Wpedantice -Wextra. -Wallè essenziale e si raccomanda tutto il resto (notare che, nonostante il suo nome, -Wallabilita solo gli avvisi più importanti, non tutti ). Queste opzioni possono essere utilizzate separatamente o tutte insieme.

Il tuo IDE potrebbe avere un modo per abilitarli dall'interfaccia utente.

Perché trattare gli avvisi come errori? Sono solo avvertimenti!

Un avviso del compilatore segnala un problema potenzialmente grave nel tuo codice. I problemi sopra elencati sono quasi sempre fatali; altri possono o meno esserlo, ma vuoi che la compilazione fallisca anche se si rivela un falso allarme. Esamina ogni avviso, trova la causa principale e correggilo. Nel caso di un falso allarme, aggiralo, ovvero usa una funzione o un costrutto in un'altra lingua in modo che l'avviso non venga più attivato. Se ciò si rivela molto difficile, disabilitare quel particolare avviso caso per caso.

Non si desidera lasciare solo avvisi come avvisi anche se tutti sono falsi allarmi. Potrebbe essere OK per progetti molto piccoli in cui il numero totale di avvisi emessi è inferiore a 7. Qualsiasi cosa in più, ed è facile perdersi un nuovo avviso in un diluvio di vecchi progetti familiari. Non permetterlo. Fai semplicemente compilare in modo pulito tutto il tuo progetto.

Nota che questo vale per lo sviluppo del programma. Se stai rilasciando il tuo progetto nel mondo nella forma sorgente, allora potrebbe essere una buona idea non fornire -Werroro equivalente nel tuo script di build rilasciato . Le persone potrebbero provare a costruire il tuo progetto con una versione diversa del compilatore o con un compilatore completamente diverso, che potrebbe avere un diverso set di avvisi abilitato. Potresti volere che la loro build abbia successo. È comunque una buona idea mantenere gli avvisi abilitati, in modo che le persone che vedono i messaggi di avviso possano inviarti segnalazioni di bug o patch.

Come trattare gli avvisi come errori?

Questo viene fatto di nuovo con gli switch del compilatore. /WXè per Microsoft, molti altri usano -Werror. In entrambi i casi, la compilazione avrà esito negativo se vengono generati avvisi.


107
Ho pubblicato queste domande e risposte perché sono stanco di dire alle persone di abilitare gli avvisi. Ora posso semplicemente indicarli qui (o, se sono di umore particolarmente malvagio, chiudere la loro domanda come un imbroglio). Puoi migliorare questa risposta o aggiungerne una tua!
n. 'pronomi' m.

16
Puoi anche usare Clang's -Weverything
pmg

9
L'unico modificatore che aggiungerei è che alcuni avvisi potrebbero non essere utili per la tua applicazione. (Ho visto degli avvertimenti che il compilatore ha aggiunto 2 byte di riempimento tra gli elementi in una struttura. L'applicazione era per la prototipazione, quindi un po 'di memoria sprecata non ci ha disturbato.) Tratta tutti gli avvisi come errori e quindi disabilita un avviso solo se sai perché quell'avvertimento non ti aiuterà.
Kyle A

20
Il rovescio della medaglia nel trattare gli avvisi come errori per le persone che seguono le istruzioni di compilazione predefinite è che il tuo codice marcisce come compilatori aggiungendo nuovi avvisi. Gli utenti che scaricano il codice e tentano di crearlo in futuro potrebbero non essere in grado di farlo, poiché il loro compilatore è troppo nuovo e genera un avviso su alcune parentesi extra o qualcosa di cui il compilatore non si preoccupava. L'utente che rileva l'errore non è responsabile del codice o del sistema di compilazione e non ha idea di come disattivare il trattamento degli avvisi come errori e di creare effettivamente il progetto.
Interfaccia il

15
@interfect Sì, mi è successo un paio di volte. Nessun problema. Se hai scelto di creare un software non mantenuto utilizzando un compilatore con cui non è mai stato testato, beh, è ​​meglio essere preparati a fare un po 'di manutenzione da soli.
n. 'pronomi' m.

94

C è, notoriamente, un linguaggio di basso livello come vanno gli HLL. Il C ++, sebbene possa sembrare un linguaggio di livello notevolmente superiore rispetto al C, condivide ancora una serie di suoi tratti. E uno di questi tratti è che le lingue sono state progettate dai programmatori, per i programmatori e, nello specifico, dai programmatori che sapevano cosa stavano facendo.

[Per il resto di questa risposta mi concentrerò su C. La maggior parte di ciò che dirò vale anche per C ++, anche se forse non così fortemente. Anche se, come ha detto Bjarne Stroustrup, "C rende più facile spararti al piede; C ++ rende più difficile, ma quando lo fai ti spazza via tutta la gamba". ]

Se sai cosa stai facendo - sai davvero cosa stai facendo - a volte potresti dover "infrangere le regole". Ma la maggior parte delle volte, la maggior parte di noi concorderà sul fatto che le regole ben intenzionate ci tengono tutti fuori dai guai e che infrangere sfrenatamente quelle regole in ogni momento è una cattiva idea.

Ma in C e C ++, ci sono sorprendentemente un gran numero di cose che puoi fare che sono "cattive idee" ma che non sono formalmente "contro le regole". A volte sono una cattiva idea qualche volta (ma potrebbero essere difendibili altre volte); a volte sono una cattiva idea praticamente tutto il tempo. Ma la tradizione è sempre stata quella di non avvertire di queste cose - perché, ancora una volta, il presupposto è che i programmatori sappiano cosa stanno facendo, non farebbero queste cose senza una buona ragione, sarebbero infastiditi da un mucchio di avvertimenti non necessari.

Ma ovviamente non tutti i programmatori sanno davvero cosa stanno facendo. E, in particolare, ogni programmatore C (indipendentemente dall'esperienza) passa attraverso una fase di essere un programmatore C. E anche i programmatori C esperti possono diventare incuranti e fare errori.

Infine, l'esperienza ha dimostrato non solo che i programmatori commettono errori, ma che questi errori possono avere conseguenze reali e serie. Se si commette un errore e il compilatore non ti avvisa a riguardo, e in qualche modo il programma non si blocca immediatamente o non fa qualcosa di ovviamente sbagliato a causa sua, l'errore può nascondersi lì, nascosto, a volte per anni, fino a quando non provoca un davvero grosso problema.

Quindi si scopre che, la maggior parte delle volte, gli avvisi sono una buona idea, dopo tutto. Perfino i programmatori esperti hanno imparato (in realtà, è " soprattutto i programmatori esperti hanno imparato") che, a conti fatti, gli avvertimenti tendono a fare più bene che male. Per ogni volta che hai fatto deliberatamente qualcosa di sbagliato e l'avvertimento è stato un fastidio, ci sono probabilmente almeno dieci volte in cui hai fatto qualcosa di sbagliato per caso e l'avvertimento ti ha salvato da ulteriori problemi. E la maggior parte degli avvisi può essere disabilitata o risolta per quelle poche volte in cui si vuole davvero fare la cosa "sbagliata".

(Un classico esempio di come un "errore" è il test if(a = b)più delle volte, questo è un errore, così la maggior parte dei compilatori in questi giorni mettono in guardia su di esso -.. Alcuni addirittura per difetto Ma se davvero voleva sia assegnare ba ae il test il risultato, è possibile disabilitare l'avviso digitando if((a = b)).)

La seconda domanda è: perché dovresti chiedere al compilatore di trattare gli avvisi come errori? Direi che è a causa della natura umana, in particolare, la reazione fin troppo facile di dire "Oh, è solo un avvertimento, non è così importante, lo ripulirò più tardi." Ma se sei un procrastinatore (e non ti conosco, ma sono un terribile procrastinatore) è facile rimandare la pulizia necessariamente per sempre - e se prendi l'abitudine di ignorare gli avvisi, è diventa sempre più facile perdere un importante messaggio di avviso che è seduto lì, inosservato, in mezzo a tutti quelli che stai ignorando.

Quindi chiedere al compilatore di trattare gli avvertimenti come errori è un piccolo trucco che puoi giocare su te stesso per aggirare questa creatura umana.

Personalmente, non insisto tanto sul trattamento degli avvisi quanto sugli errori. (In effetti, se sono onesto, posso dire che praticamente non abilito mai quell'opzione nella mia programmazione "personale".) Ma puoi essere sicuro di avere quell'opzione abilitata al lavoro, dove la nostra guida di stile (che io ha scritto) ne impone l'uso. E direi - sospetto che la maggior parte dei programmatori professionisti direbbe - che qualsiasi negozio che non tratta gli avvisi come errori in C si sta comportando in modo irresponsabile, non aderisce alle migliori pratiche del settore comunemente accettate.


9
"programmatori che sapevano cosa stavano facendo" - LOL; c'è un errore "no true Scotsman" se mai ne vedessi uno :)
Dancrumb,

3
@Dancrumb LOL back atcha. Non sono mai del tutto sicuro di capire l' errore di No true Scotsman , ma mi piace, quindi questo sarà un buon esercizio per me. Immagino che l'applicazione qui sia così: "Nessun programmatore C scriverà mai if(a = b), quindi non è necessario avvisarlo." (Quindi qualcuno produce un elenco di 10 bug critici in 10 prodotti rilasciati che derivano da questo errore specifico.) "Okay, nessun programmatore C esperto avrebbe mai scritto che ..."
Steve Summit

3
@SteveSummit ma un programmatore C veramente esperto può scrivere if (returnCodeFromFoo = foo(bar))e significare, catturare e testare il codice in un posto (supponiamo che l' unico scopo foosia avere effetti collaterali!) Il fatto che un programmatore veramente esperto possa sapere che questo non è un il buon stile di programmazione è a parte;)
alephzero

5
Il fatto è che i programmatori più esperti abilitano la maggior parte degli avvisi, se non tutti. Se vogliono usare qualcosa del genere if (returnCodeFromFoo = foo(bar)), inseriscono un commento e disattivano l'avviso (in modo che quando il programmatore della manutenzione lo guarda 4 anni dopo, realizzerà che il codice è intenzionale. Detto questo, ho lavorato con qualcuno che in (in Microsoft C ++ land) ha insistito sul fatto che combinare / Wall con il trattamento degli avvisi come errori fosse la strada da percorrere. Uh, non lo è (a meno che tu non voglia inserire molti commenti di soppressione).
Flydog57

3
Come qualcuno che non scrive codice su base giornaliera, ma quando lo faccio tende ad essere bare metal (spesso su una lavagna del mio design) trovo che gli avvertimenti abbiano un valore inestimabile. Quando si inseriscono valori nei registri interni (per un percorso descrittore DMA è un esempio) l'avvertimento sulla conversione in puntatore significa che eseguo un cast per cancellare l'avvertimento. Non sarebbe un errore, ma se qualcun altro (o anche me stesso!) Raccoglierà quel codice in pochi mesi, potrebbe essere fonte di confusione. Inoltre, applico la regola di assenza di avvisi anche agli output dei miei strumenti CAD.
Peter Smith,

39

Gli avvisi sono costituiti dai migliori consigli che alcuni degli sviluppatori C ++ più abili potrebbero inserire in un'applicazione. Vale la pena tenerli in giro.

Il C ++, essendo un linguaggio completo di Turing, ha molti casi in cui il compilatore deve semplicemente fidarsi di sapere cosa stai facendo. Tuttavia, ci sono molti casi in cui il compilatore può rendersi conto che probabilmente non hai intenzione di scrivere ciò che hai scritto. Un classico esempio sono i codici printf () che non corrispondono agli argomenti, o le stringhe std :: strs passate a printf (non quello che mi sia mai successo !). In questi casi, il codice che hai scritto non è un errore. È un'espressione C ++ valida con un'interpretazione valida per il compilatore su cui agire. Ma il compilatore ha un forte sospetto che hai semplicemente trascurato qualcosa che è facile da rilevare per un compilatore moderno. Questi sono avvertimenti. Sono cose che sono ovvie per un compilatore, usando tutte le rigide regole del C ++ a sua disposizione, che potresti aver trascurato.

Disattivare gli avvisi o ignorarli è come scegliere di ignorare i consigli gratuiti di chi è più abile di te. È una lezione di huberis che termina quando si vola troppo vicino al sole e le ali si sciolgono o si verifica un errore di corruzione della memoria. Tra i due, prenderò cadere dal cielo ogni giorno!

"Tratta gli avvisi come errori" è la versione estrema di questa filosofia. L'idea qui è che risolvi ogni avvertimento che ti dà il compilatore: ascolti ogni piccolo consiglio gratuito e agisci su di esso. Se questo è un buon modello di sviluppo per te dipende dal team e dal tipo di prodotto su cui stai lavorando. È l'approccio ascetico che un monaco potrebbe avere. Per alcuni funziona alla grande. Per altri no.

In molte delle mie applicazioni non trattiamo gli avvisi come errori. Lo facciamo perché queste particolari applicazioni devono essere compilate su più piattaforme con diversi compilatori di età diverse. A volte troviamo che in realtà è impossibile correggere un avviso da un lato senza trasformarlo in un avviso su un'altra piattaforma. Quindi stiamo semplicemente attenti. Rispettiamo gli avvisi, ma non ci pieghiamo all'indietro per loro.


11
Cosa c'entra il C ++ con Turing completo. Molte lingue stanno diventando complete e non ti fidi di te se fai qualcosa di sbagliato ....
Arrivederci SE

3
@KamiKaze ogni lingua avrà errori idiomatici (es. Java non può impedirti di scrivere un incoerente equals/ hashCode), ed è un problema di qualità dell'implementazione che viene segnalato.
Caleth,

2
@KamiKaze Il bit di completezza di Turing arriva per mostrare che ci sono casi in cui il compilatore non può provare che il codice non funzionerà come previsto. Questo è importante perché i compilatori non possono rendere un errore tutto il codice "sbagliato". Gli errori possono essere riservati solo a comportamenti che i designer linguistici sono certi saranno sempre "sbagliati". (in genere perché conduce verso percorsi incoerenti).
Cort Ammon,

2
Il che indica anche la sfida con "tutti gli avvisi sono errori". Gli avvisi sono, in base alla progettazione, più opportunistici, attivando un codice potenzialmente corretto in cambio dell'attivazione di un codice errato più spesso. Avvisi come errori ti portano a non essere in grado di esercitare le capacità del linguaggio completo.
Cort Ammon,

2
Tuttavia, in generale, i programmatori vogliono un linguaggio che faccia più di una cosa "sicura". Vogliamo una lingua che faccia la cosa che pensavamo di aver detto di fare. Pertanto, gli avvisi rimangono importanti perché l'attuale classe di cose che vogliamo che il computer faccia è una classe semantica. Il compilatore può eliminarlo definendo "errato" o "non sicuro", ma alla fine hai ancora una superclasse dei comportamenti che il programmatore voleva che il programma facesse. Gli avvisi aiutano a restringere quella superclasse.
Cort Ammon,

19

La gestione degli avvisi non solo rende il codice migliore, ma ti rende un programmatore migliore. Gli avvertimenti ti diranno cose che potrebbero sembrare poco per te oggi, ma un giorno quella cattiva abitudine tornerà e ti morderà la testa.

Utilizzare il tipo corretto, restituire quel valore, valutare quel valore restituito. Prenditi del tempo e rifletti "È davvero il tipo corretto in questo contesto?" "Devo restituire questo?" E il grande; "Questo codice sarà portatile per i prossimi 10 anni?"

Prendi l'abitudine di scrivere codice senza avvertimenti, in primo luogo.


17

Le altre risposte sono eccellenti e non voglio ripetere ciò che hanno detto.

Un altro aspetto del "perché abilitare gli avvisi" che non è stato adeguatamente toccato è che aiutano enormemente con la manutenzione del codice. Quando scrivi un programma di dimensioni significative, diventa impossibile tenere tutto in testa contemporaneamente. Di solito hai una o tre funzioni a cui stai attivamente scrivendo e pensando, e forse un file o tre sullo schermo a cui puoi fare riferimento, ma la maggior parte del programma esiste in background da qualche parte e devi fidarti che continua a funzionare.

Avere degli avvertimenti e averli il più energici e in faccia possibile, ti aiuta a avvisarti se qualcosa che cambi crea problemi a qualcosa che non riesci a vedere.

Prendi ad esempio l'avvertimento del clang -Wswitch-enum. Ciò attiva un avviso se si utilizza un interruttore su un enum e si perde uno dei possibili valori enum. È qualcosa che potresti pensare che sarebbe un errore improbabile da fare: probabilmente hai almeno guardato l'elenco dei valori enum quando hai scritto l'istruzione switch. Potresti anche avere un IDE che ha generato le opzioni di commutazione per te, senza lasciare spazio per errori umani.

Questo avvertimento si presenta davvero quando, sei mesi dopo, aggiungi un'altra possibile voce all'enum. Ancora una volta, se stai pensando al codice in questione, probabilmente starai bene. Ma se questo enum viene utilizzato per molteplici scopi diversi ed è per uno di quelli di cui hai bisogno dell'opzione extra, è molto facile dimenticare di aggiornare un interruttore in un file che non hai toccato per 6 mesi.

Puoi pensare agli avvisi nello stesso modo in cui penseresti ai casi di test automatizzati: ti aiutano a assicurarti che il codice sia sensibile e fanno quello che ti serve quando lo scrivi per la prima volta, ma aiutano ancora di più a assicurarti che continua a fare ciò di cui hai bisogno mentre lo stimoli. La differenza è che i casi di test funzionano in modo molto stretto ai requisiti del tuo codice e devi scriverli, mentre gli avvisi funzionano ampiamente secondo standard sensibili per quasi tutto il codice e sono generosamente forniti dai boffin che creano i compilatori.


9
L'altro modo in cui aiutano con la manutenzione è quando stai guardando il codice di qualcun altro e non puoi dire se un effetto collaterale era intenzionale. Con gli avvisi attivi, sai che erano almeno a conoscenza del problema.
Robin Bennett,

O nel mio caso, importi un file da un sistema incorporato che contiene un'istruzione switch di linea 3000+ su un enum con diverse migliaia di valori. Gli avvisi "fall through" (evitati usando goto) mascheravano una serie di bug "non gestiti" ... il compilatore incorporato non emetteva nessuno dei due, ma i bug erano comunque importanti.
Móż

15

Gli avvisi non fissi , prima o poi, porteranno a errori nel codice .


Il debug di un errore di segmentazione, ad esempio, richiede al programmatore di tracciare la radice (causa) dell'errore, che di solito si trova in una posizione precedente nel codice rispetto alla riga che alla fine ha causato l'errore di segmentazione.

È molto tipico che la causa sia una riga per la quale il compilatore ha emesso un avviso ignorato e la riga che ha causato l'errore di segmentazione è la riga che alla fine ha generato l'errore.

Correggere l'avviso porta a risolvere il problema .. Un classico!

Una dimostrazione di quanto sopra. Considera il seguente codice:

#include <stdio.h>

int main(void) {
  char* str = "Hello world!";
  int idx;

  // Colossal amount of code here, irrelevant to 'idx'

  printf("%c\n", str[idx]);

  return 0;
}

che una volta compilato con il flag "Wextra" passato a GCC, dà:

main.c: In function 'main':
main.c:9:21: warning: 'idx' is used uninitialized in this function [-Wuninitialized]
    9 |   printf("%c\n", str[idx]);
      |                     ^

che potrei ignorare ed eseguire comunque il codice .. E poi sarei testimone di un "grande" errore di segmentazione, come diceva il mio professore di epicurus IP:

Errore di segmentazione

Al fine di eseguire il debug in uno scenario del mondo reale, si dovrebbe partire dalla linea che causa l'errore di segmentazione e tentare di rintracciare qual è la radice della causa .. Dovrebbero cercare ciò che è successo ie strdentro quella quantità colossale di codice laggiù ...

Fino a quando, un giorno, si sono trovati nella situazione in cui hanno scoperto che idxviene utilizzato non inizializzato, quindi ha un valore di immondizia, che si traduce in indicizzazione della stringa (via) oltre i suoi limiti, che porta a un errore di segmentazione.

Se solo non avessero ignorato l'avvertimento, avrebbero trovato immediatamente il bug!


4
Al tuo titolo: non necessariamente. Ad esempio, un avvertimento che suggerisce di usare le parentesi in una formula che in realtà non ne ha bisogno indica un non-problema che non causerà mai un errore. Le precedenza degli operatori in un determinato linguaggio di programmazione non cambiano. Mai.
Marc van Leeuwen,

4
@MarcvanLeeuwen L'istanza che citi può trasformarsi in errore, ad esempio se il programmatore che non ricorda la precedenza dell'operatore modifica leggermente la formula. L'avvertimento ti dice: "potrebbe non essere chiaro a qualcuno ad un certo punto, aggiungere alcune parentesi per renderlo più chiaro". Sebbene si debba concordare sul fatto che il titolo del post originale non è sempre vero.
Hubert Jasieniecki,

^ Tutto può essere trasformato in un errore. È altrettanto facile introdurre un bug nel codice parzialmente tra parentesi come nel codice completamente tra parentesi.
Jim Balter,

... A parte, ho delle domande per il debugger la cui prima reazione a "Oh, è stata separata da str[idx]" non è "Okay, dove sono stre idxdefiniti?"
Justin Time - Reinstate Monica il

2
Sei fortunato se ricevi un errore di segmentazione. Se si è meno fortunati, è possibile che per caso si sia idxverificato il valore previsto per il test (non troppo improbabile se il valore previsto è 0), e in realtà potrebbe capitare di indicare alcuni dati sensibili che non dovrebbero mai essere stampati durante la distribuzione.
Celtschk,

14

Trattare gli avvisi come errori è solo un mezzo di autodisciplina: stavi compilando un programma per testare quella nuova brillante funzionalità, ma non puoi farlo fino a quando non risolvi le parti sciatte. Non ci sono informazioni aggiuntive Werrorfornite, stabilisce semplicemente le priorità in modo molto chiaro:

Non aggiungere nuovo codice fino a quando non risolvi i problemi nel codice esistente

È davvero la mentalità che è importante, non gli strumenti. L'output di diagnostica del compilatore è uno strumento. MISRA (per C incorporato) è un altro strumento. Non importa quale usi, ma probabilmente gli avvisi del compilatore sono lo strumento più semplice che puoi ottenere (è solo un flag da impostare) e il rapporto segnale-rumore è molto alto. Quindi non c'è motivo di non usarlo.

Nessuno strumento è infallibile. Se scrivi const float pi = 3.14;, la maggior parte degli strumenti non ti dirà che hai definito π con una cattiva precisione che potrebbe causare problemi lungo la strada. La maggior parte degli strumenti non solleva un sopracciglio if(tmp < 42), anche se è risaputo che assegnare variabili a nomi insignificanti e usare numeri magici è un modo per il disastro nei grandi progetti. Devi capire che qualsiasi codice di "test rapido" che scrivi è proprio questo: un test, e devi ottenerlo subito prima di passare ad altre attività, mentre vedi ancora i suoi difetti. Se lasci i codici così come sono, il debug se dopo due mesi aggiungerai nuove funzionalità sarà significativamente più difficile.

Una volta che hai preso la giusta mentalità, non ha senso usare Werror. Avere avvisi come avvisi ti consentirà di prendere una decisione informata se ha ancora senso eseguire quella sessione di debug che stavi per iniziare o interromperla e correggere prima gli avvisi.


3
Nel bene e nel male, lo clippystrumento per la lanugine di Rust avverte effettivamente della costante "3.14". In realtà è un esempio nei documenti . Ma come puoi immaginare dal nome, è clippyorgoglioso di essere aggressivamente utile.
emk,

1
@emk Grazie per questo esempio, forse dovrei riformulare la mia risposta in un modo "mai" mai detto . Non intendevo dire che il controllo di valori π imprecisi è impossibile, solo che semplicemente eliminare gli avvisi non garantisce una qualità del codice decente.
Dmitry Grigoryev il

Una cosa che ti avvisa quando gli errori ti danno è che i build automatici falliranno, avvisandoti così che qualcosa è andato storto. Le build automatizzate consentono anche di automatizzare la lanugine (l'esplosione è 3 ... 2 ... 1 .. :)
Móż

8

Come qualcuno che lavora con il codice C incorporato legacy, l'abilitazione degli avvisi del compilatore ha contribuito a mostrare molte debolezze e aree su cui indagare quando si propongono correzioni. In gcc l'utilizzo -Walle -Wextrae persino -Wshadowsono diventati vitali. Non ho intenzione di affrontare ogni singolo pericolo, ma ne elencherò alcuni che sono emersi per aiutare a mostrare problemi di codice.

Le variabili vengono lasciate indietro

Questo può facilmente indicare il lavoro incompiuto e le aree che potrebbero non utilizzare tutte le variabili passate che potrebbero essere un problema. Diamo un'occhiata a una semplice funzione che può innescare questo:

int foo(int a, int b)
{
   int c = 0;

   if (a > 0)
   {
        return a;
   }
   return 0;
}

Basta compilare questo senza -Wall o -Wextra non restituisce problemi. -Wall ti dirà che cnon è mai stato usato:

foo.c: nella funzione 'foo':

foo.c: 9: 20: avviso: variabile inutilizzata 'c' [-Wunused-variabile]

-Wextra ti dirà anche che il tuo parametro b non fa nulla:

foo.c: nella funzione 'foo':

foo.c: 9: 20: avviso: variabile inutilizzata 'c' [-Wunused-variabile]

foo.c: 7: 20: avviso: parametro non utilizzato 'b' [-Wunused-parametro] int foo (int a, int b)

Shadowing variabile globale

Questo un po 'difficile e non si è presentato fino a quando non è -Wshadowstato utilizzato. Modifichiamo l'esempio sopra per aggiungere, ma sembra che ci sia un globale con lo stesso nome di un locale che crea molta confusione quando si tenta di utilizzare entrambi.

int c = 7;

int foo(int a, int b)
{
   int c = a + b;
   return c;
}

Quando è stato attivato -Wshadow, è facile individuare questo problema.

foo.c: 11: 9: avvertimento: dichiarazione di 'c' ombre una dichiarazione globale [-Wshadow]

foo.c: 1: 5: nota: la dichiarazione ombreggiata è qui

Formattare le stringhe

Questo non richiede alcun flag aggiuntivo in gcc, ma è stato ancora fonte di problemi in passato. Una semplice funzione che prova a stampare i dati, ma presenta un errore di formattazione potrebbe apparire così:

void foo(const char * str)
{
    printf("str = %d\n", str);
}

Questo non stampa la stringa poiché il flag di formattazione è sbagliato e gcc ti dirà felicemente che probabilmente non è quello che volevi:

foo.c: nella funzione 'foo':

foo.c: 10: 12: avviso: il formato '% d' prevede un argomento di tipo 'int', ma l'argomento 2 ha il tipo 'const char *' [-Wformat =]


Queste sono solo tre delle molte cose che il compilatore può ricontrollare per te. Ci sono molti altri come usare una variabile non inizializzata che altri hanno sottolineato.


7
Nel mondo incorporato, gli avvisi che mi preoccupano di più sono gli avvisi " possible loss of precision" e " comparison between signed and unsigned". Trovo difficile capire quanti "programmatori" li ignorano (in realtà, non sono proprio sicuro del perché non siano errori)
Mawg dice di ripristinare Monica

3
In quest'ultimo caso, @Mawg, credo che il motivo principale per cui non si tratti di un errore sia che il risultato sizeofè senza segno, ma il tipo intero predefinito è firmato. Il sizeoftipo di risultato, in size_tgenere, viene utilizzato per qualsiasi elemento correlato alla dimensione del tipo, ad esempio allineamento o conteggio degli elementi array / container, mentre gli interi in generale devono essere utilizzati come " intse non diversamente richiesto". Considerando quante persone vengono così insegnate a usare intper scorrere i loro contenitori (rispetto inta size_t), rendendolo un errore si spezzerebbe all'incirca tutto. ; P
Justin Time - Ripristina Monica il

6

Questa è una risposta specifica a C, e perché questo è molto più importante per C che per qualsiasi altra cosa.

#include <stdio.h>
int main()
{
   FILE *fp = "some string";
}

Questo codice viene compilato con un avviso . Quali sono e dovrebbero essere errori in quasi tutte le altre lingue del pianeta (salvo il linguaggio dell'assemblea) sono avvertimenti in C. Gli avvertimenti in C sono quasi sempre errori in incognito. Gli avvisi devono essere riparati, non eliminati.

Con gcc, lo facciamo come gcc -Wall -Werror.

Questo è stato anche il motivo dell'elevata discrepanza di alcuni avvisi API non sicuri di MS. La maggior parte delle persone che programmano C hanno imparato il modo più duro di trattare gli avvisi come errori e queste cose sembravano non essere lo stesso tipo di cose e volevano correzioni non portatili.


5

LE AVVERTENZE DEL COMPILER SONO IL TUO AMICO (non urla, lettere maiuscole per enfasi).

Lavoro su sistemi Fortran-77 legacy. Il compilatore mi dice cose preziose: il tipo di dati argomento non corrisponde a una chiamata di subroutine, utilizzando una variabile locale prima che un valore sia stato impostato nella variabile, se ho un argomento di variabile o subroutine che non viene utilizzato. Questi sono quasi sempre errori.

Evitare un lungo post: quando il mio codice viene compilato in modo pulito, il 97% funziona. L'altro ragazzo con cui lavoro si compila con tutti gli avvisi disattivati, trascorre ore o giorni nel debugger, quindi mi chiede di aiutare. Ho appena compilato il suo codice con gli avvisi e gli ho detto cosa riparare.


5

Dovresti sempre abilitare gli avvisi del compilatore perché il compilatore può spesso dirti cosa c'è che non va nel tuo codice. Per fare ciò, si passa -Wall -Wextraal compilatore.

Di solito dovresti considerare gli avvisi come errori perché gli avvisi di solito indicano che c'è qualcosa di sbagliato nel tuo codice. Tuttavia, spesso è molto facile ignorare questi errori. Pertanto, trattarli come errori provocherà il fallimento della compilazione, quindi non è possibile ignorare gli errori. Per trattare gli avvisi come errori, passare -Werroral compilatore.


4

Gli avvisi del compilatore in C ++ sono molto utili per alcuni motivi.

1 - Permette di mostrarti dove puoi aver commesso un errore che può influire sul risultato finale delle tue operazioni. Ad esempio se non hai inizializzato una variabile o se hai inserito "=" anziché "==" (ci sono solo esempi)

2 - Permette anche di mostrarti dove il tuo codice non è conforme allo standard del c ++. È utile perché se il codice è conforme allo standard attuale, sarà facile spostare il codice in un'altra forma di piastra, ad esempio.

In generale, gli avvisi sono molto utili per mostrarti dove sono presenti errori nel codice che possono influire sul risultato dell'algoritmo o impedire alcuni errori quando l'utente utilizzerà il programma.


4

Ignorare gli avvisi significa che hai lasciato un codice sciatto che non solo potrebbe causare problemi in futuro a qualcun altro, ma renderà anche i messaggi di compilazione importanti meno notati da te. Più output del compilatore, meno qualcuno noterà o si preoccuperà. Più pulito è meglio. Significa anche che sai cosa stai facendo. Gli avvisi sono molto poco professionali, disattenti e rischiosi.


4

Una volta ho lavorato per una grande azienda (Fortune 50) che produceva apparecchiature di collaudo elettroniche.

Il prodotto principale del mio gruppo era un programma MFC che, nel corso degli anni, ha generato letteralmente centinaia di avvisi. Che sono stati ignorati in quasi tutti i casi.

Questo è un incubo spaventoso quando si verificano bug.

Dopo quella posizione, ho avuto la fortuna di essere assunto come primo sviluppatore in una nuova startup.

Ho incoraggiato una politica di "nessun avviso" per tutte le build, con livelli di avviso del compilatore impostati per essere piuttosto rumorosi.

La nostra pratica era usare l'avvertenza #pragma - push / disable / pop per il codice che lo sviluppatore era sicuro che andasse davvero bene, insieme a un'istruzione di registro a livello di debug, per ogni evenienza.

Questa pratica ha funzionato bene per noi.


1
Distaccato. #pragma warningnon sopprime solo gli avvisi, serve al duplice scopo di comunicare rapidamente agli altri programmatori che qualcosa è intenzionale e non accidentale, e funge da tag di ricerca per individuare rapidamente aree potenzialmente problematiche quando qualcosa si rompe ma correggere errori / avvisi non lo fa aggiustalo.
Justin Time - Ripristina Monica il

Hai ragione Justin, è esattamente così che ho visto l'avvertimento #pragma
Jim In Texas,

3

Un avviso è un errore in attesa di accadere. Quindi è necessario abilitare gli avvisi del compilatore e riordinare il codice per rimuovere qualsiasi avviso.


3

C'è solo un problema nel trattare gli avvisi come errori: quando usi codice proveniente da altre fonti (es. Librerie micro $ ** t, progetti open source), non hanno fatto bene il loro lavoro e la compilazione del loro codice genera tonnellate di avvertimenti.

Ho sempre scrivere il mio codice in modo che non genera eventuali avvisi o errori, e pulirlo fino compila senza generare alcun rumore estraneo. La spazzatura con cui devo lavorare mi preoccupa e sono sbalordito quando devo costruire un grande progetto e guardare un flusso di avvertimenti passare dove la compilazione dovrebbe solo annunciare quali file ha elaborato.

Documento anche il mio codice perché so che il costo reale della vita del software deriva principalmente dalla manutenzione, non dalla sua scrittura iniziale, ma è una storia diversa ...


Non bussare, ci sono buoni soldi nel lavoro di consulenza per le persone che possono leggere ad alta voce gli avvisi del compilatore per i clienti.
Móż

Il codice proveniente da altre fonti che generano avvisi non significa necessariamente che gli autori fossero sciatti. Può anche significare che hanno compilato il codice con un compilatore diverso che ha generato un diverso set di avvisi. Il codice può essere compilato senza avvisi su un compilatore e generare avvisi su un altro. O forse è solo un diverso set di opzioni di avviso; ad esempio hanno usato -Walle tu usi -Wall -Wextra.
Celtschk,

2

Un avvertimento può significare un possibile errore semantico nel codice o un possibile UB. Ad esempio ;dopo if(), variabile non utilizzata, variabile globale mascherato da locale o confronto tra firmato e non firmato. Molti avvisi sono correlati all'analizzatore di codice statico nel compilatore o alle violazioni dello standard ISO rilevabili al momento della compilazione, che "richiedono diagnostica". Sebbene tali occorrenze possano essere legali in un caso particolare, sarebbero il risultato di problemi di progettazione per la maggior parte del tempo.

Alcuni compilatori, ad esempio gcc, hanno un'opzione da riga di comando per attivare la modalità "avvisi come errori", è uno strumento utile, anche se crudele, per educare i programmatori principianti.


1

Il fatto che i compilatori C ++ accettare il codice di raccogliere tali ovviamente si traduce in un comportamento indefinito a tutti è un grande difetto nei compilatori. Il motivo per cui non risolvono questo problema è perché probabilmente si romperanno alcune build utilizzabili.

La maggior parte degli avvisi dovrebbe essere errori fatali che impediscono il completamento della compilazione. I valori predefiniti per visualizzare solo gli errori e comunque eseguire la compilazione sono errati e se non li sostituisci per trattare gli avvisi come errori e lasciare alcuni avvisi, probabilmente finirai con il crash del programma e facendo cose casuali.


Ironia della sorte, un sacco di comportamento indefinito in realtà non provoca avvertimenti, ma silenziosamente si compila bene in una brutta bomba a orologeria. ; P
Justin Time - Ripristina Monica il

1
Il problema è che se lo standard richiede un messaggio di errore, quel messaggio di errore deve essere emesso in tutti i casi in cui si verifica il problema, ma mai se il problema non si verifica. Ma in casi come un comportamento indefinito, potrebbe essere impossibile decidere. Ad esempio, considerare il seguente codice: int i; if (fun1()) i=2; if (fun2()) i=3; char s="abcde"[i];Questo codice mostra un comportamento indefinito se e solo se entrambi fun1()e fun2()può restituire falsela stessa esecuzione della funzione. Quale può essere vero o no, ma come può dirlo il compilatore?
Celtschk

-2

Dovresti assolutamente abilitare gli avvisi del compilatore poiché alcuni compilatori non sono in grado di segnalare alcuni errori di programmazione comuni, tra cui:

-> inizializza una variabile viene dimenticata -> restituisce un valore da una funzione perda -> i semplici argomenti nelle famiglie printf e scanf che non corrispondono alla stringa di formato una funzione viene utilizzata senza essere dichiarata in anticipo, sebbene avvenga solo in c

Così come queste funzioni possono essere rilevate e segnalate, di solito non di default; quindi questa funzione deve essere esplicitamente richiesta tramite le opzioni del compilatore.


-32

Vacci piano: non è necessario, non è necessario. -Wall e -Werror sono stati progettati dai maniaci del refactoring del codice per se stessi: è stato inventato dagli sviluppatori del compilatore per evitare di rompere le build esistenti dopo gli aggiornamenti del compilatore sul lato utente . La funzione non è nulla, ma tutto sulla decisione di interrompere o non interrompere la build.

È totalmente tua preferenza utilizzarlo o meno. Lo uso sempre perché mi aiuta a correggere i miei errori.


19
Sebbene non sia obbligatorio , si consiglia vivamente di usarli
Spikatrix

18
-Wall and -Werror was designed by code-refactoring maniacs for themselves.[citazione necessaria]
YSC

22
Sembra che ti stia contraddicendo. Se lo "usi sempre perché aiuta a correggere i [tuoi] errori", non vale la pena insegnare ai programmatori più nuovi in ​​modo che lo facciano ovunque fin dall'inizio? Non penso che questa domanda si stia chiedendo se è possibile compilare o meno senza -Walle -Werror, sta solo chiedendo se è una buona idea. Che, dall'ultima frase, sembra che tu lo stia dicendo.
scohe001,

12
Quando hai più esperienza con il mantenimento del codice non scritto da te, rivisita questa risposta.
Thorbjørn Ravn Andersen,

Questa non è una risposta utile. Ci sono 4 punti interrogativi nella domanda dei PO. A quanti risponde questa risposta?
Anders,
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.