scanf
tem uma série de problemas e muitos são contra-intuitivos.
O principal é que nem sempre ele lê a quebra de linha (que equivale ao ENTER). Então o que acontece é:
- vc digita um número (um ou mais dígitos) e dá ENTER
- o buffer de entrada recebe os dígitos e o
\n
(caractere de quebra de linha, que equivale ao ENTER)
-
scanf("%d", &numero);
lê somente os dígitos e deixa a quebra de linha lá
- a próxima chamada (
scanf("%c", &operacao);
) vai ler um caractere, então ele pega o próximo que está disponível no buffer (que no caso, é a quebra de linha)
Ou seja, ele leu a quebra de linha, sem nem esperar você digitar. Você pode alterar seu programa para imprimir o valor da operação, só pra ver:
default:
printf("\ncheguei aqui\n");
printf("Operacao invalida: [%c] [%d]", operacao, operacao);
O resultado será (no teste abaixo, digitei 1
no número):
Digite o numero: 1
______________________________
Digite a operacao: 1
cheguei aqui
Operacao invalida: [
] [10]
_______
Repare que ele imprimiu [
, pulou a linha e depois ]
. Isso porque o caractere corresponde à quebra de linha, que ao ser impressa faz isso (pula para a próxima linha). Depois imprimi também como inteiro, que mostra o respectivo código da tabela ASCII (e 10 é o new line).
Existem vários “malabarismos” que dá para fazer com scanf
, mas acaba ficando mais confuso ainda.
Uma solução melhor, porém que dá um pouco mais de trabalho, é usar fgets
. O chato é que ela inclui a quebra de linha na string (então você mesmo tem que retirar), e depois ainda precisa converter para número, quando necessário. E depois de ler, você ainda pode limpar o buffer, ou seja, ir lendo e descartando os caracteres restantes que não te interessam (e faça isso até consumir a quebra de linha).
Mas em C não tem jeito, é uma linguagem mais “bruta” e exige que você faça tudo na mão. Uma forma de fazer seria:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// limpa o buffer (lê tudo até a quebra de linha, assim a próxima leitura começa "zerada")
void clearBuffer(FILE *fp) {
int c;
while ((c = fgetc(fp)) != EOF && c != '\n');
}
// usa fgets para ler o que o usuário digitou, e remove o \n (ENTER) do final
char *fgets_wrapper(char *buffer, size_t buflen, FILE *fp) {
if (fgets(buffer, buflen, fp) != 0) {
size_t len = strlen(buffer);
if (len > 0 && buffer[len - 1] == '\n') {
// se tem \n, remove
buffer[len - 1] = '\0';
} else {
// se não tem, é porque digitou coisas a mais, então limpa o buffer pra remover esse "lixo"
clearBuffer(fp);
}
return buffer;
}
return 0;
}
/*
* Lê um número e coloca o valor em "n"
* maxLen indica o máximo de caracteres a serem lidos (incluindo o hífen para números negativos) - os excedentes serão descartados
*
* Retorna 1 se deu certo, 0 se deu erro
*/
int read_int(const char *prompt, int *n, int maxLen) {
printf("%s", prompt);
int maxBuffer = maxLen + 1; // +1 porque precisa de espaço para o terminador de string
char *buffer = malloc(maxBuffer);
// primeiro lê como string
if (! fgets_wrapper(buffer, maxBuffer, stdin)) {
printf("Erro ao ler\n");
free(buffer);
return 0;
}
// depois converte para número
if (sscanf(buffer, "%d", n) != 1) {
printf("Não é número: %s\n", buffer);
free(buffer);
return 0;
}
free(buffer);
return 1;
}
/*
* Lê a operação (apenas um caractere) e guarda em "operacao"
* retorna 1 se conseguiu ler, 0 caso contrário
*/
int ler_operacao(char *operacao, int numero) {
printf("Digite a operação: %d ", numero);
// 2 porque precisa do terminador de string
if (! fgets_wrapper(operacao, 2, stdin)) {
return 0;
}
return 1;
}
int main(void) {
int numerodeop;
int numero;
if( ! read_int("Digite o numero: ", &numero, 10)) {
printf("Erro ao ler\n");
return -1;
}
char operacao[2];
while (1) {
if (! ler_operacao(operacao, numero)) {
printf("Erro ao ler\n");
return -1;
}
if (*operacao == 'o')
break;
switch (*operacao) {
case '+':
if( ! read_int("Digite o numero: ", &numerodeop, 10)) {
printf("Erro ao ler\n");
return -1;
}
numero += numerodeop;
break;
case '-':
if( ! read_int("Digite o numero: ", &numerodeop, 10)) {
printf("Erro ao ler\n");
return -1;
}
numero -= numerodeop;
break;
case 'x':
if( ! read_int("Digite o numero: ", &numerodeop, 10)) {
printf("Erro ao ler\n");
return -1;
}
numero *= numerodeop;
break;
case '/':
if( ! read_int("Digite o numero: ", &numerodeop, 10)) {
printf("Erro ao ler\n");
return -1;
}
numero /= numerodeop;
break;
default:
printf("\ncheguei aqui\n");
printf("Operacao invalida: [%s]\n", operacao);
break;
}
}
printf("O resultado de todas as operações feitas foi: ");
return 0;
}