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?
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:
I compilatori C e C ++ sono notoriamente cattivi nel segnalare alcuni errori comuni del programmatore per impostazione predefinita , come:
return
un valore da una funzioneprintf
e scanf
famiglie non corrispondenti alla stringa di formatoQuesti possono essere rilevati e segnalati, di solito non di default; questa funzione deve essere esplicitamente richiesta tramite le opzioni del compilatore.
Questo dipende dal tuo compilatore.
Compilatori C ++ di Microsoft C e capire interruttori piace /W1
, /W2
, /W3
, /W4
e /Wall
. Usa almeno /W3
. /W4
e /Wall
può 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
, -Wpedantic
e -Wextra
. -Wall
è essenziale e si raccomanda tutto il resto (notare che, nonostante il suo nome, -Wall
abilita 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.
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 -Werror
o 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.
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.
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 b
a a
e 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.
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 ..."
if (returnCodeFromFoo = foo(bar))
e significare, catturare e testare il codice in un posto (supponiamo che l' unico scopo foo
sia avere effetti collaterali!) Il fatto che un programmatore veramente esperto possa sapere che questo non è un il buon stile di programmazione è a parte;)
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).
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.
equals
/ hashCode
), ed è un problema di qualità dell'implementazione che viene segnalato.
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.
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.
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 i
e str
dentro quella quantità colossale di codice laggiù ...
Fino a quando, un giorno, si sono trovati nella situazione in cui hanno scoperto che idx
viene 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!
str[idx]
" non è "Okay, dove sono str
e idx
definiti?"
idx
verificato 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.
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 Werror
fornite, 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.
clippy
strumento per la lanugine di Rust avverte effettivamente della costante "3.14". In realtà è un esempio nei documenti . Ma come puoi immaginare dal nome, è clippy
orgoglioso di essere aggressivamente utile.
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 -Wall
e -Wextra
e persino -Wshadow
sono diventati vitali. Non ho intenzione di affrontare ogni singolo pericolo, ma ne elencherò alcuni che sono emersi per aiutare a mostrare problemi di codice.
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 c
non è 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)
Questo un po 'difficile e non si è presentato fino a quando non è -Wshadow
stato 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
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.
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)
sizeof
è senza segno, ma il tipo intero predefinito è firmato. Il sizeof
tipo di risultato, in size_t
genere, 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 " int
se non diversamente richiesto". Considerando quante persone vengono così insegnate a usare int
per scorrere i loro contenitori (rispetto int
a size_t
), rendendolo un errore si spezzerebbe all'incirca tutto. ; P
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.
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.
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 -Wextra
al 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 -Werror
al compilatore.
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.
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.
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.
#pragma warning
non 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.
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 ...
-Wall
e tu usi -Wall -Wextra
.
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.
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.
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 false
la stessa esecuzione della funzione. Quale può essere vero o no, ma come può dirlo il compilatore?
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.
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.
-Wall and -Werror was designed by code-refactoring maniacs for themselves.
[citazione necessaria]
-Wall
e -Werror
, sta solo chiedendo se è una buona idea. Che, dall'ultima frase, sembra che tu lo stia dicendo.