Erros comuns em C
Essa página é uma coleção de erros comuns (com exemplos de código) que ocorrem quando estamos aprendendo a programar em C. A página foi feita em conjunto com Tales Lelo da Aparecida.
1. Variáveis, Atribuições e Estrutura Básica de um Programa
A. Armazenar um valor muito grande em um int
Neste caso, podemos usar o long:
B. Não declarar uma variável antes de usá-la
Neste caso, ocorre um erro de compilação:
exemplo1.c: In function 'main':
exemplo1.c:4:18: error: 'a' undeclared (first use in this function)
printf("%d\n", a);
^
exemplo1.c:4:18: note: each undeclared identifier is reported only once for each function it appears in
2. Escrita, Leitura e Operações Aritméticas
A. Imprimir uma informação usando printf com o tipo errado
Exemplo:
Esse programa gera exibe a seguinte mensagem ao ser compilado com a opção -Wall:
main.c: In function'main':
main.c:4:14: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat=]
printf("%d\n", 10.0);
Solução 1: imprimir como float:
Solução 2: informar ao C que ele precisa converter o tipo
B. Imprimir dois números na sequência sem dar um espaço ou pular uma linha
Exemplo:
Esse programa imprime:
42
Solução: adicionar um \n para quebrar a linha ou espaço em branco.
Esse programa imprime:
4
2
C. Esquecer de colocar um & antes do nome da variável no scanf
Neste caso, o programa compila, mas quando o executamos, o erro segmentation fault ocorre.
D. Usar o valor de uma variável sem antes ter definido esse valor no código ou à partir do scanf
Exemplo:
E. Colocar uma expressão do lado esquerdo do sinal de =
Devido ao hábito criado com a sintaxe matemática, quando se inicia a programar pode surgir esta confusão. O operador = é utilizado para definir um novo valor a uma variável. A linguagem não é capaz de resolver cálculos algébricos com dois lados de uma igualdade, isto é, o exemplo abaixo é inválido:
Assim, para não sofrer com este problema basta organizar o código de modo que todo cálculo fique do lado direito de =, resultando, no exemplo, em y = (3+2)/4
3. Expressões Relacionais, Lógicas e Comandos Condicionais
A. Usar expressões do tipo a <= x <= b
Exemplo:
Esse programa apresenta os seguintes warnings quando compilamos:
exemplo.c: In function 'main':
exemplo.c:6:17: warning: comparison of constant '15' with boolean expression is always true [-Wbool-compare]
if(5 <= num <= 15) {
^
exemplo.c:6:8: warning: comparisons like 'X<=Y<=Z' do not have their mathematical meaning [-Wparentheses]
if(5 <= num <= 15) {
^
Ou seja, nós somos avisados que comparações deste tipo não têm o mesmo significado da matemática.
Se rodarmos esse programa, ok será impresso independentemente do valor digitado. Isso porque o C avalia a expressão 5 <= num <= 15 como (5 <= num) <= 15 e a expressão 5 <= num vale 0 ou 1, dependendo do valor da variável num. Assim, a expressão (5 <= num) <= 15 é sempre verdadeira, já que tanto 0 quanto 1 são menores ou iguais a 15.
B. Confusão entre = e ==
Em C temos, assim como muitas linguagens, temos símbolos que são reaproveitados, devido ao número limitado de caracteres que o teclado nos proporciona. Assim, possuímos duas utilidades para o =: uma na atribuição, como em x = 2; e uma na comparação, como (x == 2).
É importante entender a diferença entre esses dois usos, pois ao confundí-los pode ocorrer erros como:
Este programa gera a seguinte saída, quando compilado com -Wall:
main.c:6:2: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
if (x = 2) /* Atribui 2 a variavel x, entrando no if, pois 2 e um valor diferente de zero */
^
main.c:9:2: warning: statement with no effect [-Wunused-value]
y == 4; /* Compara y com 4 e descarta o resultado, ja que nao e usado dentro de um if ou while, nem guardado */
^
Para não enfrentar tais problemas basta utilizar o operador = para atribuições e o == para comparações.
4. Comandos Condicionais
A. Não usar chaves no comando if quando temos mais de uma linha a ser executada se a condição for verdadeira:
Quando digitamos 8, a saída é:
O número é menor que 10
O número é par
Mas quando digitamos 13, a saída é:
O número é par
Solução: Usar as chaves corretamente, já que não basta a identação.
B. Não agrupar ifs e elses corretamente com as chaves (mesmo quando a identação está correta):
Quando a digitamos 12, a saída é:
O número é par e divisível por 4
Mas quando digitamos 6, a saída é:
O número é impar
O erro ocorre porque o else está associado ao segundo if e não ao primeiro, independentemente da identação.
Solução: Usar corretamente as chaves para dar o significado que você deseja.
C. Esquecer do comando break quando utilizamos um switch
Exemplo:
Se no programa acima digitamos o caracter ’+’ e os números 2 e 6, então será impresso:
2 + 4 = 6
2 + 4 = -2
2 + 4 = 8
2 + 4 = 0
2 + 4 = 2
Operação não reconhecida
Isso se deve a não colocar o comando break no final de cada case. Neste caso, o programa continua a execução do programa seguindo para os próximos cases até terminar o bloco ou até encontrar um break.
Solução: Usar break corretamente para indicar que o programa não deve continuar executando os próximos comandos.
D. Repetir trechos em blocos mutuamente exclusivos
Se algum trecho se repete, por exemplo, em um if e seu else correspondente, muitas vezes é possível reescrever o código sem repetição.
Exemplo:
Solução: Neste caso, basta colocar a linha fora dos blocos.
E. Não fazer o else if de condições mutuamente exclusivas
Sempre que possível utilize else if pois esta estrutura evite que o computador avalie um if que só pode ser falso, afinal um if de condição oposta foi verdadeiro.
Exemplo:
Além de otimizar o código, há casos onde isto é necessário, como, por exemplo, quando as condições não são mutuamente exclusivas, mas só um bloco é desejado para executar. Por exemplo, um programa que avalia entre dois números se são uma PA de razão 1 ou simplesmente uma sequência crescente.
Neste exemplo, como não há else antes do segundo if, caso o número atual seja seguinte ao anterior, vai ser exibido:
Seguinte ao anterior.
Maior que o anterior.
Assim, neste caso, é necessário para o funcionamento correto do programa a estrutura else if.
5. Comandos Repetitivos
A. Usar ponto-e-vírgula incorretamente no for e no while
Exemplo:
Esse código imprime 11 ao invés de imprimir os números entre 1 e 10.
Isto acontece por causa do ; no final da linha do for. Neste caso, o comando for executa um comando vazio enquanto i for menor ou igual a 10. Quando o for termina de ser executado, a variável i vale 11 e o programa segue para a próxima linha, que imprime o valor de i.
Para corrigir, basta remover o ; no final da linha do for.
B. Esperar que o loop seja quebrado na mudança da variável condicional
É importante entender como funciona o while e os demais loops.
Exemplo:
Quando o código acima é executado, a saída é: 0123456789. Pode parecer trivial, mas é importante notar que ao exibir o algarismo 9, a variável a já possui um valor que faz a condição do while ser falsa, contudo, até que a condição seja atingida, que é após o printf, o loop vai continuar a rodar. Caso o comportamento desejado seja sair abruptamente do loop, apesar de não recomendado, é utilizado a comando break. Em nosso caso poderíamos fazer:
Assim, a nova saída seria 012345678.
C. Sobreescrever a última leitura
Certas vezes precisamos ler dados em um loop. E em casos mais específicos podemos desejar ler um dado fora, antes dos demais.
Solução: Existem diversas maneiras de contornar este problema. Neste nosso trecho é possível usar o do/while, que permitiria que a leitura de dados aparecesse somente dentro do loop, onde o caso (entrada == ‘s’) seria tratado no switch. Mas, em outros casos, é possível passar o scanf para a última linha do while, e mantendo a leitura externa.
Dicas relacionadas
1. Funcionamento do comando diff
Ao submeter no susy(ou outro sistema de submissão de algoritmos), é executado o diff, um comando que pode ser executado no terminal da seguinte maneira:
diff [arquivo1] [arquivo2]
Ele ira analisar ambos arquivos e exibir suas diferenças seguindo um padrão para cada trecho de diferenças. Na 1ª linha é exibido o <Número><Letra><Número>. Os números são as linhas do primeiro e segundo arquivo, respectivamente, onde começa o bloco de diferenças. A letra será a em caso de adição de conteúdo, d para deleções e c para modificações.
A partir daí, as linhas com < marcam o que está no arquivo 1; > o que está no 2 e entre os dois há uma linha que os separa, para facilitar a leitura, apenas. Pode aparecer um =, que significará que a linha está igual em ambos arquivos.
Quando não há diferença entre as saídas, nem mesmo caracteres maiúsculos em um e minúsculos em outro, mas ainda aparecem ”>” ou ”<”, pode ser o caso de existir caracteres não legíveis, como o espaço ou o \n em um dos arquivos.
Agora, quando os arquivos realmente são identicos, ao executar o comando diff, não será exibido texto algum.