Por que eu sempre devo ativar avisos do compilador?

302

Costumo ouvir que, ao compilar programas em C e C ++, eu deveria "sempre ativar avisos do compilador". Por que isso é necessário? Como faço isso?

Às vezes, também ouço dizer que devo "tratar os avisos como erros". Eu devo? Como faço isso?

n. 'pronomes' m.
fonte

Respostas:

341

Por que ativar avisos?

Os compiladores C e C ++ são notoriamente ruins em relatar alguns erros comuns do programador por padrão , como:

  • esquecendo de inicializar uma variável
  • esquecendo-se de returnum valor de uma função
  • argumentos printfe scanffamílias que não correspondem à string de formato
  • uma função é usada sem ser declarada previamente (somente C)

Eles podem ser detectados e relatados, mas geralmente não são por padrão; esse recurso deve ser solicitado explicitamente por meio de opções do compilador.

Como habilitar avisos?

Isso depende do seu compilador.

Microsoft C e compiladores C ++ compreender interruptores gosto /W1, /W2, /W3, /W4e /Wall. Use pelo menos /W3. /W4e /Wallpode emitir avisos espúrios para arquivos de cabeçalho do sistema, mas se o seu projeto compilar corretamente com uma dessas opções, siga-o. Essas opções são mutuamente exclusivas.

A maioria dos outros compiladores entende opções como -Wall, -Wpedantice -Wextra. -Wallé essencial e todo o resto é recomendado (observe que, apesar do nome, -Wallhabilita apenas os avisos mais importantes, nem todos ). Essas opções podem ser usadas separadamente ou todas juntas.

Seu IDE pode ter uma maneira de habilitá-los na interface do usuário.

Por que tratar os avisos como erros? São apenas avisos!

Um aviso do compilador sinaliza um problema potencialmente sério no seu código. Os problemas listados acima são quase sempre fatais; outros podem ou não, mas você deseja que a compilação falhe, mesmo que seja um alarme falso. Investigue cada aviso, encontre a causa raiz e corrija-a. No caso de um alarme falso, contorne-o, ou seja, use um recurso ou construção de idioma diferente para que o aviso não seja mais acionado. Se isso for muito difícil, desative esse aviso específico caso a caso.

Você não deseja apenas deixar avisos como avisos, mesmo que todos sejam falsos alarmes. Pode ser bom para projetos muito pequenos em que o número total de avisos emitidos seja menor que 7. Mais alguma coisa, e é fácil que um novo aviso se perca em uma enxurrada de velhos e familiares. Não permita isso. Apenas faça com que todo o seu projeto seja compilado de maneira limpa.

Observe que isso se aplica ao desenvolvimento do programa. Se você estiver lançando seu projeto para o mundo no formulário de origem, pode ser uma boa idéia não fornecer -Werrorou equivalente no script de construção liberado . As pessoas podem tentar criar seu projeto com uma versão diferente do compilador ou com um compilador diferente, o que pode ter um conjunto diferente de avisos habilitados. Você pode querer que a construção deles seja bem-sucedida. Ainda é uma boa ideia manter os avisos ativados, para que as pessoas que vêem mensagens de aviso possam enviar relatórios ou correções de bugs.

Como tratar os avisos como erros?

Isso é feito novamente com opções de compilador. /WXé para a Microsoft, a maioria dos outros usa -Werror. Em ambos os casos, a compilação falhará se houver algum aviso produzido.

n. 'pronomes' m.
fonte
107
Postei estas perguntas e respostas porque estou cansado de dizer às pessoas para ativar avisos. Agora eu posso apenas apontá-los aqui (ou, se eu estiver de mau humor, encerrar a pergunta deles como um idiota). Você pode melhorar esta resposta ou adicionar sua própria!
n. 'pronomes' m.
16
Você também pode usar de clang -Weverything
pmg
9
O único modificador que eu acrescentaria é que alguns avisos podem não ser úteis para o seu aplicativo. (Vi avisos de que o compilador adicionou 2 bytes de preenchimento entre os elementos em uma estrutura. O aplicativo era para prototipagem, para que um pouco de memória desperdiçada não nos incomodasse.) Trate todos os avisos como erros e desative um aviso apenas se você sabe por que esse aviso não irá ajudá-lo.
Kyle Um
20
A desvantagem de tratar os avisos como erros para as pessoas que seguem as instruções de compilação padrão é que o código apodrece quando os compiladores adicionam novos avisos. Os usuários que baixarem seu código e tentarem construí-lo no futuro poderão não conseguir, porque o compilador é muito novo e emite um aviso sobre alguns parênteses extras ou algo que o compilador não se importou. O usuário que encontrar o erro não é responsável pelo seu código ou sistema de construção e não tem idéia de como desativar o tratamento de avisos como erros e, na verdade, criar seu projeto.
interfect 9/09/19
15
@interfect Sim, isso aconteceu comigo algumas vezes. Nada demais. Se você optou por criar um software não mantido usando um compilador com o qual nunca foi testado, é melhor você estar preparado para fazer alguma manutenção.
n. 'pronomes' m.
94

C é, notoriamente, uma linguagem de nível bastante baixo conforme os HLLs. O C ++, embora pareça ser uma linguagem de nível consideravelmente mais alto que o C, ainda compartilha algumas de suas características. E uma dessas características é que as linguagens foram projetadas por programadores, para programadores - e, especificamente, programadores que sabiam o que estavam fazendo.

[No restante desta resposta, vou focar em C. A maior parte do que direi também se aplica ao C ++, embora talvez não tão fortemente. Embora, como disse Bjarne Stroustrup, "C facilite o tiro no pé; o C ++ torna mais difícil, mas quando você o faz explodir toda a perna". ]

Se você sabe o que está fazendo - realmente sabe o que está fazendo -, às vezes você pode ter que "quebrar as regras". Mas na maioria das vezes, a maioria de nós concorda que regras bem-intencionadas nos mantêm longe de problemas, e que quebrar voluntariamente essas regras o tempo todo é uma má idéia.

Mas em C e C ++, há um número surpreendentemente grande de coisas que você pode fazer que são "más idéias", mas que não são formalmente "contrárias às regras". Às vezes, são uma péssima ideia algumas vezes (mas podem ser defensáveis ​​outras vezes); às vezes, são uma má ideia praticamente o tempo todo. Mas a tradição sempre foi não avisar sobre essas coisas - porque, novamente, a suposição é que os programadores sabem o que estão fazendo, eles não fariam essas coisas sem uma boa razão, ficariam aborrecidos por muitos de avisos desnecessários.

Mas é claro que nem todos os programadores realmente sabem o que estão fazendo. E, em particular, todo programador C (não importa o quão experiente) passa por uma fase de ser um programador iniciante em C. E mesmo programadores C experientes podem se descuidar e cometer erros.

Finalmente, a experiência mostrou não apenas que os programadores cometem erros, mas que esses erros podem ter consequências reais e sérias. Se você cometer um erro, e o compilador não avisar sobre isso, e de alguma forma o programa não travar imediatamente ou fizer algo obviamente errado por causa disso, o erro pode estar lá, escondido, às vezes por anos, até causar um realmente grande problema.

Portanto, na maioria das vezes, os avisos são uma boa ideia, afinal. Até os programadores experientes aprenderam (na verdade, é " especialmente os programadores experientes aprenderam") que, no geral, os avisos tendem a fazer mais bem do que prejudicar. Sempre que você fez algo errado deliberadamente e o aviso foi um incômodo, provavelmente há pelo menos dez vezes que você fez algo errado por acidente e o aviso o salvou de mais problemas. E a maioria dos avisos pode ser desativada ou contornada nas poucas vezes em que você realmente deseja fazer a coisa "errada".

(Um exemplo clássico de como um "erro" é o teste if(a = b)Na maioria das vezes, isso é um erro, por isso a maioria dos compiladores estes dias advertir sobre isso -.. Alguns até mesmo por omissão, mas se você realmente queria tanto Atribuir ba ae teste o resultado, você pode desativar o aviso digitando if((a = b)).)

A segunda pergunta é: por que você deseja solicitar ao compilador que trate os avisos como erros? Eu diria que é por causa da natureza humana, especificamente, a reação muito fácil de dizer "Oh, isso é apenas um aviso, isso não é tão importante, eu vou limpar isso mais tarde". Mas se você é um procrastinador (e eu não o conheço, mas sou um péssimo procrastinador), é fácil adiar a limpeza necessariamente para sempre - e se você tem o hábito de ignorar os avisos, fica cada vez mais fácil perder uma importante mensagem de aviso que fica parada, despercebida, no meio de todas as que você está ignorando.

Portanto, pedir ao compilador que trate os avisos como erros é um pequeno truque que você pode usar para contornar esse inimigo humano.

Pessoalmente, não sou tão insistente em tratar avisos como erros. (De fato, se eu for honesto, posso dizer que praticamente nunca habilito essa opção na minha programação "pessoal".) Mas você pode ter certeza de que habilitei essa opção no trabalho, onde nosso guia de estilo (que eu escreveu) exige seu uso. E eu diria - suspeito que a maioria dos programadores profissionais diria - que qualquer loja que não trate avisos como erros em C está se comportando de maneira irresponsável, não está aderindo às melhores práticas do setor comumente aceitas.

Steve Summit
fonte
9
"programadores que sabiam o que estavam fazendo" - LOL; há uma falácia "nenhum verdadeiro Scotsman" se alguma vez eu vi um :)
Dancrumb
3
@Dancrumb LOL de volta a você. Eu nunca tenho certeza de que entendi a verdadeira falácia do escocês , mas eu gosto, então isso será um bom exercício para mim. Eu acho que a aplicação aqui é assim: "Nenhum programador de C jamais escreveria if(a = b), portanto, não precisamos avisar." (Então, alguém produz uma lista de 10 bugs críticos em 10 produtos lançados esse resultado deste erro particular.) "Ok, não experiente programador C jamais iria escrever isso ..."
Steve Summit
3
@SteveSummit, mas um programador C realmente experiente pode escrever if (returnCodeFromFoo = foo(bar))e dizer a verdade, para capturar e testar o código em um só lugar (suponha que o único objetivo fooseja ter efeitos colaterais!) O fato de um programador realmente experiente saber que isso não é um bom estilo de codificação é irrelevante;)
alephzero
5
O problema é que os programadores mais experientes habilitam a maioria, se não todos, os avisos. Se eles querem usar algo parecido if (returnCodeFromFoo = foo(bar)), colocam um comentário e desativam o aviso (para que, quando o programador de manutenção o analise quatro anos depois, ele perceberá que o código é intencional. com alguém que (no país de Microsoft C ++) insistiu que combinar / Wall com tratar avisos como erros era o caminho a seguir.Uh, não é (a menos que você queira colocar muitos comentários de supressão).
Flydog57
3
Como alguém que não escreve código diariamente, mas quando o faço tende a ser bare metal (geralmente em um quadro de meu próprio projeto), acho os avisos inestimáveis. Ao inserir valores em registros internos (para um local do descritor de DMA é um exemplo), o aviso sobre conversão em ponteiro significa que eu faço uma conversão para limpar o aviso. Não seria um erro, mas se alguém (ou eu mesmo) pegar esse código em alguns meses, pode ser confuso. Além disso, aplico a regra de não apresentar avisos aos resultados das minhas ferramentas CAD também.
Peter Smith
39

Os avisos consistem nos melhores conselhos que alguns dos desenvolvedores de C ++ mais qualificados poderiam usar em um aplicativo. Vale a pena ficar por aqui.

O C ++, sendo uma linguagem completa de Turing, tem muitos casos em que o compilador deve simplesmente confiar que você sabia o que está fazendo. No entanto, existem muitos casos em que o compilador pode perceber que você provavelmente não pretendia escrever o que escreveu. Um exemplo clássico é printf () códigos que não correspondem aos argumentos, ou std :: cordas passados para printf (não que isso sempre acontece comigo!). Nesses casos, o código que você escreveu não é um erro. É uma expressão válida do C ++ com uma interpretação válida para a ação do compilador. Mas o compilador tem um forte palpite de que você simplesmente ignorou algo que é fácil para um compilador moderno detectar. Estes são avisos. São coisas óbvias para um compilador, usando todas as regras estritas do C ++ à sua disposição, que você pode ter esquecido.

Desativar avisos ou ignorá-los é como optar por ignorar o aconselhamento gratuito daqueles mais habilidosos que você. É uma lição de huberis que termina quando você voa muito perto do sol e suas asas derretem, ou ocorre um erro de corrupção de memória. Entre os dois, eu vou cair do céu qualquer dia!

"Tratar avisos como erros" é a versão extrema dessa filosofia. A idéia aqui é que você resolva todos os avisos que o compilador lhe der - ouça todos os conselhos gratuitos e aja de acordo. Se este é um bom modelo de desenvolvimento para você, depende da equipe e de que tipo de produto você está trabalhando. É a abordagem ascética que um monge pode ter. Para alguns, funciona muito bem. Para outros, não.

Em muitos dos meus aplicativos, não tratamos os avisos como erros. Fazemos isso porque esses aplicativos específicos precisam compilar em várias plataformas com vários compiladores de idades variadas. Às vezes, achamos que é realmente impossível corrigir um aviso de um lado sem que ele se transforme em um aviso em outra plataforma. Portanto, somos apenas cuidadosos. Nós respeitamos os avisos, mas não nos inclinamos para trás.

Cort Ammon
fonte
11
O que o C ++ sendo Turing completo tem a ver com isso. Muitas linguagens estão completas e não confiam em você se você fizer algo errado ...
Adeus SE
3
O @KamiKaze em todas as linguagens terá erros idiomáticos (por exemplo, Java não pode impedir que você escreva um equals/ inconsistente / hashCode), e é um problema de qualidade de implementação quais delas são relatadas.
Caleth 9/09/19
2
@KamiKaze O bit de completude do Turing aparece para mostrar que há casos em que o compilador não pode provar que seu código falhará ao funcionar conforme o planejado. Isso é importante porque os compiladores não podem tornar todos os códigos "errados" um erro. Os erros só podem ser reservados para comportamentos que os designers de linguagem têm certeza de que sempre estarão "errados". (normalmente porque leva a caminhos inconsistentes).
Cort Ammon
2
O que também aponta para o desafio com "todos os avisos são erros". Os avisos são, por design, mais oportunistas, acionando algum código potencialmente correto em troca de acionar código errado com mais frequência. Os avisos como erros levam a que você não consiga exercer todos os recursos do idioma.
Cort Ammon
2
No entanto, em geral, os programadores querem uma linguagem que faça mais do que uma coisa "segura". Queremos uma linguagem que faça o que pensamos que dissemos. Portanto, os avisos permanecem importantes porque a classe real de coisas que queremos que o computador faça é uma classe semântica. O compilador pode escolher definindo "incorreto" ou "inseguro", mas no final você ainda tem uma superclasse dos comportamentos que o programador queria que o programa fizesse. Os avisos ajudam a restringir essa superclasse.
Cort Ammon
19

Não apenas o manuseio dos avisos cria um código melhor, como também o torna um programador melhor. Os avisos lhe dirão sobre coisas que podem parecer pouco para você hoje, mas um dia esse mau hábito voltará e o incomodará.

Use o tipo correto, retorne esse valor, avalie esse valor de retorno. Reserve um tempo e reflita "Esse é realmente o tipo correto neste contexto?" "Eu preciso devolver isso?" E o biggie; "Esse código será portátil pelos próximos 10 anos?"

Adquira o hábito de escrever código sem aviso prévio.

RedSonja
fonte
17

As outras respostas são excelentes e não quero repetir o que disseram.

Um outro aspecto do "por que ativar avisos" que não foi abordado adequadamente é que eles ajudam enormemente na manutenção do código. Quando você escreve um programa de tamanho significativo, torna-se impossível manter tudo na cabeça de uma só vez. Você normalmente tem uma função ou três nas quais está escrevendo e pensando ativamente, e talvez um arquivo ou três na tela que possa se referir, mas a maior parte do programa existe em segundo plano em algum lugar e você deve confiar que ele continua trabalhando.

Ter avisos e tê-los tão enérgicos quanto possível, ajuda a alertá-lo se algo que você mudar causar problemas para algo que você não pode ver.

Tomemos, por exemplo, o aviso de clang -Wswitch-enum. Isso aciona um aviso se você usar uma opção em uma enumeração e perder um dos valores possíveis da enumeração. É algo que você pode pensar que seria um erro improvável de cometer: você provavelmente ao menos olhou para a lista de valores de enumeração ao escrever a instrução switch. Você pode até ter um IDE que gerou as opções de opção para você, sem deixar espaço para erro humano.

Esse aviso realmente ocorre quando, seis meses depois, você adiciona outra entrada possível ao enum. Novamente, se você estiver pensando no código em questão, provavelmente ficará bem. Mas se esse enum é usado para vários propósitos diferentes e é para um daqueles que você precisa da opção extra, é muito fácil esquecer de atualizar uma opção em um arquivo que você não tocou por 6 meses.

Você pode pensar em avisos da mesma maneira que em casos de teste automatizados: eles ajudam a garantir que o código seja sensato e a fazer o que você precisa quando você o escreve, mas ajudam ainda mais a garantir que ele continua fazendo o que você precisa enquanto estimula. A diferença é que os casos de teste funcionam muito estreitamente com os requisitos do seu código e você precisa escrevê-los, enquanto os avisos funcionam amplamente com padrões sensíveis para quase todo o código, e são muito generosamente fornecidos pelos boffins que fazem os compiladores.

Josiah
fonte
9
A outra maneira de ajudar na manutenção é quando você está olhando o código de outra pessoa e não pode dizer se um efeito colateral foi intencional. Com os avisos ativados, você sabe que eles estavam pelo menos cientes do problema.
Robin Bennett
Ou, no meu caso, você importa um arquivo de um sistema incorporado que contém uma declaração de alternância de mais de 3000 linhas em uma enumeração com vários milhares de valores. Os avisos de "falha" (evitados usando goto) mascaravam vários bugs "não manipulados" ... o compilador incorporado não emitiu nenhum deles, mas os bugs eram importantes, no entanto.
Móż 5/10/19
15

Avisos não fixos , mais cedo ou mais tarde, levarão a erros no seu código .


A depuração de uma falha de segmentação, por exemplo, exige que o programador rastreie a raiz (causa) da falha, que geralmente está localizada em um local anterior no seu código do que a linha que eventualmente causou a falha de segmentação.

É muito típico que a causa seja uma linha para a qual o compilador emitiu um aviso que você ignorou e a linha que causou a segmentação falha na linha que acabou gerando o erro.

A correção do aviso leva à correção do problema. Um clássico!

Uma demonstração do acima. Considere o seguinte código:

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

que, quando compilado com o sinalizador "Wextra" passado para o GCC, fornece:

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

que eu poderia ignorar e executar o código de qualquer maneira .. E então eu testemunharia uma "grande" falha de segmentação, como meu professor de epicurus de IP costumava dizer:

falha de segmentação

Para depurar isso em um cenário do mundo real, seria iniciado a partir da linha que causa a falha de segmentação e tentaria rastrear qual é a raiz da causa. Eles teriam que procurar o que aconteceu ie strdentro dessa quantidade colossal de código por lá ...

Até que, um dia, eles se encontraram na situação em que descobrem que idxé usado não inicializado, portanto, ele tem um valor de lixo, o que resulta em indexar a string (muito) além de seus limites, o que leva a uma falha de segmentação.

Se eles não tivessem ignorado o aviso, teriam encontrado o bug imediatamente!

gsamaras
fonte
4
Para o seu título: não necessariamente. Por exemplo, um aviso sugerindo o uso de parênteses em uma fórmula que realmente não precisa deles aponta para um não-problema que nunca causará um erro. As precedências do operador em uma determinada linguagem de programação não são alteradas. Sempre.
Marc van Leeuwen
4
@MarcvanLeeuwen A instância citada pode se transformar em erro, por exemplo, se o programador que não se lembra da precedência do operador modifica corretamente a fórmula um pouco. O aviso informa: "pode ​​não estar claro para alguém em algum momento; adicione alguns parênteses para torná-lo mais claro". Embora se deva concordar que o título da postagem original nem sempre é verdadeiro.
Hubert Jasieniecki
^ Tudo pode ser transformado em erro. É tão fácil introduzir um bug no código parcialmente entre parênteses quanto no código totalmente entre parênteses.
Jim Balter
... Como um aparte, eu tenho perguntas para o depurador cuja primeira reação a "Oh, ele se separou de str[idx]" não é "Ok, onde estão stre idxdefinidas?`
Justin Time - Restabelece Monica
2
Você tem sorte se tiver uma falha de segmentação. Se você tiver menos sorte, pode, por acaso, ter idxo valor esperado em seu teste (não muito improvável se o valor esperado for 0) e apontar para alguns dados confidenciais que nunca devem ser impressos quando implantados.
Celtschk
14

Tratar avisos como erros é apenas um meio de autodisciplina: você estava compilando um programa para testar esse novo recurso brilhante, mas não pode até consertar as partes desleixadas. Não há informações adicionais Werror, apenas as prioridades são definidas com muita clareza:

Não adicione novo código até corrigir os problemas no código existente

É realmente a mentalidade que é importante, não as ferramentas. A saída de diagnóstico do compilador é uma ferramenta. MISRA (para C incorporado) é outra ferramenta. Não importa qual você usa, mas sem dúvida os avisos do compilador são a ferramenta mais fácil que você pode obter (é apenas um sinalizador para definir) e a relação sinal / ruído é muito alta. Portanto, não há razão para não usá-lo.

Nenhuma ferramenta é infalível. Se você escrever const float pi = 3.14;, a maioria das ferramentas não informará que você definiu π com uma precisão ruim, o que pode levar a problemas futuros. A maioria das ferramentas não abre uma sobrancelha if(tmp < 42), mesmo que seja conhecido que dar nomes sem sentido às variáveis ​​e usar números mágicos é uma maneira de desastre em grandes projetos. Você precisa entender que qualquer código de "teste rápido" que você escreve é ​​apenas isso: um teste, e você precisa corrigi-lo antes de prosseguir para outras tarefas, enquanto ainda vê suas deficiências. Se você deixar esses códigos como estão, a depuração se você passar dois meses adicionando novos recursos será significativamente mais difícil.

Depois de entrar na mentalidade certa, não há sentido em usá-lo Werror. Ter avisos como avisos permitirá que você tome uma decisão informada se ainda faz sentido executar a sessão de depuração que você estava prestes a iniciar ou abortá-la e corrigir os avisos primeiro.

Dmitry Grigoryev
fonte
3
Para o bem ou para o mal, a clippyferramenta de fiapos para o Rust realmente alertará sobre a constante "3,14". Na verdade, é um exemplo nos documentos . Mas, como você pode imaginar, clippytem orgulho em ser agressivamente útil.
emk
1
@emk Obrigado por este exemplo, talvez eu deva reformular minha resposta de uma maneira que nunca diga "nunca" . Não quis dizer que é impossível verificar valores imprecisos π, mas apenas livrar-se dos avisos não garante uma qualidade decente do código.
Dmitry Grigoryev
Uma coisa que os avisos indicam como erros é que as construções automatizadas falharão, alertando assim que algo deu errado. Construções automatizadas também permitem automatizar fiapos (a explosão é de 3 ... 2 ... 1 .. :) #
05519
8

Como alguém que trabalha com código C incorporado herdado, a ativação de avisos do compilador ajudou a mostrar muitas fraquezas e áreas a serem investigadas ao propor correções. Em gcc utilizando -Walle -Wextrae até mesmo -Wshadowtornaram-se vital. Não vou correr todos os riscos, mas vou listar alguns que apareceram que ajudaram a mostrar problemas de código.

Variáveis ​​sendo deixadas para trás

Este pode facilmente apontar para trabalhos inacabados e áreas que podem não estar utilizando todas as variáveis ​​passadas, o que pode ser um problema. Vejamos uma função simples que pode desencadear isso:

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

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

Apenas compilar isso sem -Wall ou -Wextra não gera problemas. -Wall lhe dirá que isso cnunca é usado:

foo.c: Na função 'foo':

foo.c: 9: 20: aviso: variável não utilizada 'c' [-Wunused-variable]

-Wextra também dirá que seu parâmetro b não faz nada:

foo.c: Na função 'foo':

foo.c: 9: 20: aviso: variável não utilizada 'c' [-Wunused-variable]

foo.c: 7: 20: aviso: parâmetro não utilizado 'b' [-Wunused-parameter] int foo (int a, int b)

Global Variable Shadowing

Este foi um pouco difícil e não apareceu até que -Wshadowfoi usado. Vamos modificar o exemplo acima para adicionar apenas, mas acontece que existe um global com o mesmo nome de um local que causa muita confusão ao tentar usar os dois.

int c = 7;

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

Quando -Wshadow foi ativado, é fácil identificar esse problema.

foo.c: 11: 9: aviso: a declaração de 'c' sombreia uma declaração global [-Wshadow]

foo.c: 1: 5: note: a declaração sombreada está aqui

Formatar strings

Isso não requer nenhum sinalizador extra no gcc, mas ainda tem sido a fonte de problemas no passado. Uma função simples que tenta imprimir dados, mas tem um erro de formatação, pode se parecer com o seguinte:

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

Isso não imprime a string, pois o sinalizador de formatação está errado e o gcc dirá com satisfação que provavelmente não é o que você queria:

foo.c: Na função 'foo':

foo.c: 10: 12: aviso: o formato '% d' espera o argumento do tipo 'int', mas o argumento 2 tem o tipo 'const char *' [-Wformat =]


Essas são apenas três das muitas coisas que o compilador pode verificar para você. Existem muitas outras como usar uma variável não inicializada que outras pessoas apontaram.

Dom
fonte
7
No mundo incorporado, os avisos que mais me preocupam são os avisos " possible loss of precision" e " comparison between signed and unsigned". Acho difícil entender quantos "programadores" os ignoram (na verdade, não sei ao certo por que não são erros)
Mawg diz que restabelece Monica em 10/09/1919
3
No último caso, @Mawg, acredito que a principal razão pela qual não é um erro é que o resultado de sizeofnão está assinado, mas o tipo inteiro padrão é assinado. O sizeoftipo de resultado,, size_tgeralmente é usado para qualquer coisa relacionada ao tamanho do tipo, como, por exemplo, alinhamento ou contagem de elementos de matriz / contêiner, enquanto números inteiros em geral devem ser usados ​​como "a intmenos que seja necessário". Considerando quantas pessoas são ensinadas a usar intpara iterar seus contêineres (em comparação intcom size_t), cometer um erro quebraria praticamente tudo. ; P
Justin Time - Reinstate Monica
6

Essa é uma resposta específica para C e por que isso é muito mais importante para C do que para qualquer outra coisa.

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

Esse código é compilado com um aviso . O que são e devem haver erros em praticamente todos os outros idiomas do planeta (exceto linguagem assembly) são avisos em C. Os avisos em C são quase sempre erros disfarçados. Os avisos devem ser corrigidos, não suprimidos.

Com gcc, fazemos isso como gcc -Wall -Werror.

Esse também foi o motivo da alta segurança de alguns avisos de API não seguros da MS. A maioria das pessoas que programa C aprendeu a maneira mais difícil de tratar os avisos como erros, e essas coisas pareciam não ser o mesmo tipo de coisa e queriam correções não portáteis.

Joshua
fonte
5

AVISOS DO COMPILADOR SÃO SEU AMIGO (sem gritar, em maiúsculas para enfatizar).

Eu trabalho em sistemas Fortran-77 legados. O compilador me diz coisas valiosas: o tipo de dados do argumento não corresponde a uma chamada de sub-rotina, usando uma variável local antes que um valor tenha sido definido na variável, se eu tiver um argumento de variável ou sub-rotina que não seja usado. Estes são quase sempre erros.

Evitando uma postagem longa: quando meu código é compilado corretamente, 97% funciona. O outro cara com quem trabalho compila com todos os avisos desativados, passa horas ou dias no depurador e depois me pede ajuda. Acabei de compilar o código com os avisos e dizer o que corrigir.

Mark Diaz
fonte
5

Você sempre deve habilitar os avisos do compilador, pois o compilador geralmente pode lhe dizer o que há de errado com seu código. Para fazer isso, você passa -Wall -Wextrapara o compilador.

Normalmente, você deve tratar os avisos como erros, porque geralmente significam que há algo errado com o seu código. No entanto, geralmente é muito fácil ignorar esses erros. Portanto, tratá-los como erros fará com que a compilação falhe, portanto você não poderá ignorar os erros. Para tratar avisos como erros, passe -Werrorpara o compilador.

SS Anne
fonte
4

Os avisos do compilador em C ++ são muito úteis por alguns motivos.

1 - Permite mostrar onde você pode ter cometido um erro que pode impactar o resultado final de suas operações. Por exemplo, se você não inicializou uma variável ou se colocou "=" em vez de "==" (existem apenas exemplos)

2 - Permite também mostrar onde seu código não está em conformidade com o padrão do c ++. É útil porque, se o código estiver em conformidade com o padrão real, será fácil movê-lo para outra forma de placa, por exemplo.

Em geral, os avisos são muito úteis para mostrar onde há erros no seu código que podem afetar o resultado do seu algoritmo ou evitar algum erro quando o usuário usar o seu programa.

Fizik26
fonte
4

Ignorar avisos significa que você deixou um código incorreto que não só poderia causar problemas no futuro para outra pessoa, mas também tornaria importantes as mensagens de compilação menos notadas por você. Quanto mais saída do compilador, menos alguém notará ou se incomodará. Limpador, melhor. Isso também significa que você sabe o que está fazendo. Os avisos são muito pouco profissionais, descuidados e arriscados.

Kirk Augustin
fonte
4

Certa vez, trabalhei para uma grande empresa (Fortune 50) que fabricava equipamentos de teste eletrônico.

O produto principal do meu grupo foi um programa MFC que, ao longo dos anos, passou a gerar literalmente centenas de avisos. Que foram ignorados em quase todos os casos.

Este é um pesadelo quando ocorrem erros.

Após essa posição, tive a sorte de ser contratado como o primeiro desenvolvedor de uma nova startup.

Incentivei uma política de 'sem aviso' para todas as compilações, com os níveis de aviso do compilador configurados para serem bastante barulhentos.

Nossa prática era usar #pragma warning - push / disable / pop para o código que o desenvolvedor tinha certeza de que estava realmente bem, junto com uma declaração de log no nível de depuração, apenas por precaução.

Essa prática funcionou bem para nós.

Jim In Texas
fonte
1
Destacado. #pragma warningnão apenas suprime avisos, serve para a dupla finalidade de comunicar rapidamente a outros programadores que algo é intencional e não acidental e atua como uma tag de pesquisa para localizar rapidamente áreas potencialmente problemáticas quando algo quebra, mas corrigir os erros / avisos não consertá-lo.
Justin Time - Restabelece Monica
Você está certo Justin, que é exatamente como eu visto #pragma aviso
Jim No Texas
3

Um aviso é um erro esperando para acontecer. Portanto, você deve ativar os avisos do compilador e organizar seu código para remover qualquer aviso.

josyb
fonte
3

Há apenas um problema em tratar os avisos como erros: quando você usa código proveniente de outras fontes (por exemplo, bibliotecas micro $ ** t, projetos de código aberto), eles não fazem seu trabalho corretamente e compilar seu código gera toneladas de avisos.

Eu sempre escrevo meu código para que não gere avisos ou erros e o limpo até compilar sem gerar ruído estranho. O lixo com o qual tenho que trabalhar me assusta, e me surpreendo quando preciso criar um grande projeto e observar um fluxo de avisos, onde a compilação deve apenas anunciar quais arquivos foram processados.

Também documento meu código porque sei que o custo real do software é proveniente principalmente da manutenção, não de escrevê-lo inicialmente, mas essa é uma história diferente ...

FKEinternet
fonte
Não perca tempo, há um bom dinheiro em trabalhos de consultoria para pessoas que podem ler avisos do compilador em voz alta para os clientes.
Móż 5/10/19
O código de outras fontes que geram avisos não significa necessariamente que os autores foram desleixados. Também pode significar que eles compilaram o código com um compilador diferente que gerou um conjunto diferente de avisos. O código pode compilar sem avisos em um compilador e gerar avisos em outro. Ou talvez seja apenas um conjunto diferente de opções de aviso; por exemplo, eles usaram -Walle você usa -Wall -Wextra.
Celtschk 19/12/19
2

Algum aviso pode significar possível erro semântico no código ou possível UB. Por exemplo , ;depois if(), variável não utilizada, variável global mascarada por local ou comparação de assinado e não assinado. Muitos avisos estão relacionados ao analisador de código estático no compilador ou a violações do padrão ISO detectável em tempo de compilação, que "exigem diagnósticos". Embora essas ocorrências possam ser legais em um caso específico, elas resultariam de problemas de design na maioria das vezes.

Alguns compiladores, por exemplo, gcc, têm uma opção de linha de comando para ativar o modo "avisos como erros"; é uma ferramenta legal, se cruel, para educar codificadores iniciantes.

Swift - torta de sexta-feira
fonte
1

O fato de que os compiladores C ++ aceitar compilar o código que, obviamente, resulta em um comportamento indefinido em tudo é uma grande falha nos compiladores. A razão pela qual eles não corrigem isso é porque isso provavelmente quebraria algumas construções utilizáveis.

A maioria dos avisos deve ser erros fatais que impedem a conclusão da compilação. Os padrões para exibir apenas erros e fazer a compilação de qualquer maneira estão errados e se você não os substituir para tratar os avisos como erros e deixar alguns avisos, provavelmente você terminará com o programa travando e fazendo coisas aleatórias.

Jason Livesay
fonte
Ironicamente, um monte de comportamento indefinido não causa avisos, mas silenciosamente compila perfeitamente uma bomba-relógio desagradável. ; P
Justin Time - Reinstate Monica
1
O problema é que, se o padrão exigir uma mensagem de erro, essa mensagem de erro deverá ser emitida em todos os casos em que o problema ocorrer, mas nunca se o problema não ocorrer. Mas em casos como comportamento indefinido, pode ser impossível decidir. Por exemplo, considere o seguinte código: int i; if (fun1()) i=2; if (fun2()) i=3; char s="abcde"[i];Este código exibe um comportamento indefinido se e somente se ambos fun1()e fun2()pode retornar falsena mesma execução de função. O que pode ou não ser verdade, mas como o compilador deve dizer?
Celtschk
-2

Definitivamente, você deve habilitar avisos do compilador, pois alguns compiladores são ruins em relatar alguns erros comuns de programação, incluindo o seguinte:

-> inicializa uma variável é esquecida -> retorna um valor de uma função - - os argumentos simples nas famílias printf e scanf que não correspondem à sequência de formatação em que uma função é usada sem ser declarada anteriormente, embora aconteça apenas em c

Portanto, como essas funções podem ser detectadas e relatadas, geralmente não é o padrão; portanto, esse recurso deve ser solicitado explicitamente por meio de opções do compilador.

Hasan Raza
fonte
-32

Acalme-se: você não precisa, não é necessário. -Wall e -Werror foram projetados por maníacos de refatoração de código para eles mesmos: foram inventados pelos desenvolvedores de compiladores para evitar a quebra de construções existentes após atualizações do compilador no lado do usuário . O recurso não é nada, mas tudo sobre a decisão de interromper ou não interromper a compilação.

É totalmente da sua preferência usá-lo ou não. Eu o uso o tempo todo porque me ajuda a corrigir meus erros.

sqr163
fonte
19
Embora não seja obrigatório , é altamente recomendado usá-los
Spikatrix
18
-Wall and -Werror was designed by code-refactoring maniacs for themselves.[citação necessário]
YSC
22
Parece que você está se contradizendo. Se você "usa o tempo todo porque ajuda a corrigir seus erros", não vale a pena ensinar aos programadores mais novos para que eles façam isso em qualquer lugar desde o início? Eu não acho que essa pergunta esteja perguntando se é possível compilar sem -Walle -Werror, mas apenas perguntando se é uma boa ideia. Que, da sua última frase, parece que você está dizendo que sim.
scohe001
12
Quando você obtiver mais experiência em manter o código não escrito por você, revise esta resposta.
Thorbjørn Ravn Andersen
Esta não é uma resposta útil. Existem 4 pontos de interrogação na pergunta dos POs. Quantas respostas esta resposta?
Anders