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
#include <stdio.h>
int main() {
int n = 4000000000;
printf("%d\n", n); /* Imprime -294967296. */
return 0;
}
Neste caso, podemos usar o long:
#include <stdio.h>
int main() {
long n = 4000000000;
printf("%ld\n", n);
return 0;
}
B. Não declarar uma variável antes de usá-la
#include <stdio.h>
int main() {
printf("%d\n", a);
int a;
return 0;
}
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:
#include <stdio.h>
int main() {
printf("%d\n", 10.0); /*Imprime 1490196704*/
return 0;
}
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:
#include <stdio.h>
int main() {
printf("%f\n", 10.0); /* Imprime 10.000000 */
return 0;
}
Solução 2: informar ao C que ele precisa converter o tipo
#include <stdio.h>
int main() {
printf("%d\n", (int)10.0); /* Imprime 10 */
return 0;
}
B. Imprimir dois números na sequência sem dar um espaço ou pular uma linha
Exemplo:
#include <stdio.h>
int main() {
printf("%d", 4);
printf("%d", 2);
return 0;
}
Esse programa imprime:
42
Solução: adicionar um \n para quebrar a linha ou espaço em branco.
#include <stdio.h>
int main() {
printf("%d\n", 4);
printf("%d", 2);
return 0;
}
Esse programa imprime:
4
2
C. Esquecer de colocar um & antes do nome da variável no scanf
#include <stdio.h>
int main() {
int a;
scanf("%d", a);
printf("%d", a);
return 0;
}
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:
#include <stdio.h>
int main() {
int a;
printf("%d", a); /* valor de 'a' não está definido, imprime qualquer número */
return 0;
}
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:
#include <stdio.h>
int main() {
int y;
4*y - 2 = 3; // Esta linha gera o erro de compilação: "error: lvalue required as left operand of assignment"
printf("Valor de y: %d", y);
return 0;
}
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:
#include <stdio.h>
int main() {
int num;
scanf("%d", &num);
if(5 <= num <= 15) {
printf("ok\n");
}
return 0;
}
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:
#include <stdio.h>
int main() {
int x, y;
scanf("%d %d", &x, &y);
if (x = 2) /* Atribui 2 a variavel x, entrando no if, pois 2 e um valor diferente de zero */
printf("Voce digitou 2.");
y == 4; /* Compara y com 4 e descarta o resultado, ja que nao e usado dentro de um if ou while, nem guardado */
return 0;
}
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:
#include <stdio.h>
int main() {
int n;
scanf("%d", &n);
if(n < 10 && n % 2 == 0) /*testa se é menor que 10 e é par*/
printf("O número é menor que 10\n");
printf("O número é par\n");
return 0;
}
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.
#include <stdio.h>
int main() {
int n;
scanf("%d", &n);
if(n < 10 && n % 2 == 0) { /*testa se é menor que 10 e é par*/
printf("O número é menor que 10\n");
printf("O número é par\n");
}
return 0;
}
B. Não agrupar ifs e elses corretamente com as chaves (mesmo quando a identação está correta):
#include <stdio.h>
int main() {
int n;
scanf("%d", &n);
if(n % 2 == 0)
if(n % 4 == 0)
printf("O número é par e divisível por 4\n");
else
printf("O número é impar\n");
return 0;
}
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.
#include <stdio.h>
int main() {
int n;
scanf("%d", &n);
if(n % 2 == 0) {
if(n % 4 == 0)
printf("O número é par e divisível por 4\n");
} else
printf("O número é impar\n");
return 0;
}
C. Esquecer do comando break quando utilizamos um switch
Exemplo:
#include <stdio.h>
int main() {
int op1, op2;
char operador;
printf("Digite a operação\n");
scanf("%c", &operador);
printf("Digite os dois operandos\n");
scanf("%d %d", &op1, &op2);
switch(operador) {
case '+':
printf("%d + %d = %d\n", op1, op2, op1 + op2);
case '-':
printf("%d + %d = %d\n", op1, op2, op1 - op2);
case '*':
printf("%d + %d = %d\n", op1, op2, op1 * op2);
case '/':
printf("%d + %d = %d\n", op1, op2, op1 / op2);
case '%':
printf("%d + %d = %d\n", op1, op2, op1 % op2);
default:
printf("Operação não reconhecida\n");
}
return 0;
}
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.
#include <stdio.h>
int main() {
int op1, op2;
char operador;
printf("Digite a operação\n");
scanf("%c", &operador);
printf("Digite os dois operandos\n");
scanf("%d %d", &op1, &op2);
switch(operador) {
case '+':
printf("%d + %d = %d\n", op1, op2, op1 + op2);
break;
case '-':
printf("%d + %d = %d\n", op1, op2, op1 - op2);
break;
case '*':
printf("%d + %d = %d\n", op1, op2, op1 * op2);
break;
case '/':
printf("%d + %d = %d\n", op1, op2, op1 / op2);
break;
case '%':
printf("%d + %d = %d\n", op1, op2, op1 % op2);
break;
default:
printf("Operação não reconhecida\n");
}
return 0;
}
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:
...
if (a != 0) {
printf("O numero escolhido foi: %d", a); /* Observe que esta linha se repete. */
scanf("%d", &a);
} else {
printf("O numero escolhido foi: %d", a);
printf("Saindo do programa!");
}
...
Solução: Neste caso, basta colocar a linha fora dos blocos.
...
printf("O numero escolhido foi: %d", a); /* Observe a alteração */
if (a != 0) {
scanf("%d", &a);
} else {
printf("Saindo do programa!");
}
...
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:
...
if (a > 0) {
printf("Maior que 0.\n");
} else if (a < 0) { /* Aqui, caso não seja colocado o **else** o programa continua correto, mas, no caso de **a>0** ser verdade, é desnecessário testar se **a<0**, logo, o else gera eficiência */
printf("Menor que 0.\n");
}
...
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.
...
if (atual == anterior+1) {
printf("Seguinte ao anterior.\n");
}
if (atual > anterior) { /* Observe a ausência do else */
printf("Maior que o anterior.\n");
}
...
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:
#include <stdio.h>
int main() {
int i;
for(i = 1; i <= 10; i++); /*Note o ponto-e-virgula nesta linha*/
printf("%d\n", i);
return 0;
}
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:
#include <stdio.h>
int main() {
int a=0;
while(a < 9) {
a++;
printf("%d", a);
}
return 0;
}
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:
#include <stdio.h>
int main() {
int a=0;
while(a < 9) {
a++;
if(a >= 9)
break;
printf("Valor de a: %d", a);
}
return 0;
}
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.
...
scanf("%c", &entrada);
while (c != 's') {
scanf("%c", &entrada); /* Perceba que ao entrar no loop, o valor de 'entrada' é substituido */
switch(entrada) {
... /* Bloco que utiliza devidamente a entrada */
}
}
...
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.