Ir ao conteúdo
  • Cadastre-se

C Pulando o comando printf


MarianaN

Posts recomendados


#include<stdio.h>
#include<stdlib.h>


int main (){
  
  float notas[3];
int c=0;

for(int c=0;c<3;c++){
printf("Digite a notaa %d :",c+1);
scanf("%.1f",&notas[c]);

}

for(int c=0;c<3;c++){
printf("NOTA %d : %.1f " ,c+1,notas[c]);
}

}

Ao executar o código,  ele solicita a primeira  nota porém após  isso ele pula todas etapas até  o final do código.

  • Amei 1
Link para o comentário
Compartilhar em outros sites

@MarianaN    você colou caracteres inválidos no scanf , e não está pulando o printf , não mas sim  o scanf ;

// scanf("%.1f",&notas[c]);   na leitura não pode especificar a qtd de casas decimais
                           // apenas na escrita "printf("%5.2f\n", notas[c]);"
scanf( "%f" , & notas[c]);

outra forma de validar os dados inseridos pelo usuário seria usar uma string :
#include <ctype.h>
#include <string.h>
#include <stdlib.h>  
char str[20];
l1:
scanf("%s",str); // Aqui não precisa do "&" de endereco pois já o tem
for(int i=0; str[i] != 0; i++)
    if( ! isdigit(str[i]) && str[i] !='.' ) goto l1;
notas[c] = atof(str);

 

  • Curtir 2
Link para o comentário
Compartilhar em outros sites

Procure alinhar seu código para ficar mais legível.

 

Algo assim seria comum:

 

#include <stdio.h>
#include <stdlib.h>

int main()
{
    float notas[3];
    int   c = 0;

    for (int c = 0; c < 3; c++)
    {
        printf("Digite a notaa %d :", c + 1);
        scanf("%.1f", &notas[c]);
    }

    for (int c = 0; c < 3; c++)
    {
        printf("NOTA %d : %.1f ", c + 1, notas[c]);
    }
}

 

TESTE o retorno de scanf() como descrito no manual. É ingênuo seguir sem testar. 

 

 

  • Curtir 2
  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

@MarianaN Você pode fazer o seguinte, se quer limitar a quantidade de caracteres lidos no formato de float:

int ch = 0;
// Limitado a ler 3 caracteres <d.d>
scanf("%3f", &notas[c]);
// Lendo o que ha de resto
while((ch = fgetc(stdin)) != '\n' && ch != EOF);

Isto é, se tem certeza de que a nota é composta por somente um dígito antes da vírgula, e nesse caso não vão ocorrer arredondamentos:
image.png.8d1801c1dc550c6e37b036fea9b8eb10.png

Em geral não tem muito sentido em cobrar a nota dessa forma, acho que vale mais a pena ler a nota do jeito que o usuário inserir e arredondar na hora de imprimir, como você já fez:

10 horas atrás, MarianaN disse:
printf("NOTA %d : %.1f " ,c+1,notas[c]);

 

Se quer testar o retorno da scanf(), pode usar um loop:

while(scanf("%3f", &notas[c]) < 1)
{
    // Lendo o que ha de resto
    while((ch = fgetc(stdin)) != '\n' && ch != EOF);
    // Informando o usuario
    printf("Entrada invalida!\nDigite a nota %d: ", c + 1);
}

A scanf() retorna a quantidade de itens lidos e gravados, então nesse caso deveria ser 1.

Se for 0, não gravou nada.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

1 hora atrás, Lucca Rodrigues disse:
scanf("%3f", &notas[c]);

 

É ingênuo escrever assim. Se o cara bater o dedo no 'w' ao invés de '2' já era. E tem o problema do ponto decimal. Se o usuário digitar ponto ao invés de vírgula ou vice-versa?

 

Nem para um exemplo num forum de iniciantes, eu acho.

 

1 hora atrás, Lucca Rodrigues disse:
while(scanf("%3f", &notas[c]) < 1)

 

:) aí sim.

 

1 hora atrás, Lucca Rodrigues disse:

A scanf() retorna a quantidade de itens lidos e gravados, então nesse caso deveria ser 1.

Se for 0, não gravou nada

 

Pode retornar -1 em caso de erro ou o total de especificadores atendidos. Aquelas coisas que começam por % e não por %%. No caso aqui um só --- "%3f"--- , então pode retornar -1, 0 ou 1 

 

 

 

  • Curtir 2
Link para o comentário
Compartilhar em outros sites

29 minutos atrás, arfneto disse:

É ingênuo escrever assim. Se o cara bater o dedo no 'w' ao invés de '2' já era. E tem o problema do ponto decimal. Se o usuário digitar ponto ao invés de vírgula ou vice-versa?

Esse tipo de coisa acontece, daí a gente aplica os mínimos cuidados com a scanf(): verifica o retorno, lê a besteira que o usuário digitou se o retorno não for o esperado e informa que a entrada não é válida. 😁

No mais, é o que foi dito no outro tópico por você:

Citação

O teclado tem mais de 100 teclas e modificadores como control alt e shift. O que se faz é testar o que está sendo digitado.

Tem muitas maneiras. A mais flexível é ler tecla a tecla sem mostrar e só mostrar depois de testar. O mais simples é ler e testar em um loop, mas pode zoar a tela.

Acho que é o mais adequado, mas foram poucas as vezes que vi alguém aplicar em uma atividade acadêmica ou coisa do tipo.

Eu tinha feito isso há um tempo, só não sei se o comportamento da peek() ou da wait() muda entre os compiladores. Testei com o GCC:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

char LerChar(void);
int LerInt(int minval, int maxval);
float LerFloat(float minval, float maxval);

int main()
{
    printf("Digite algum numero inteiro entre -300 e 300: ");
    int x = LerInt(-300, 300);
    printf("\n\nDigite algum numero real entre -1.2 e 5.6: ");
    float y = LerFloat(-1.2f, 5.6f);

    printf("\n\nValor inteiro: %d\nValor real: %f\n", x, y);

    return 0;
}

char LerChar(void)
{
	INPUT_RECORD record;
	HANDLE console = GetStdHandle(STD_INPUT_HANDLE);
	int total = 0;
	// Tecla
	char c = 0;

	do {
		FlushConsoleInputBuffer(console);
		WaitForSingleObject(console, INFINITE);

		/* Se PeekConsoleInput() for bem-sucedida,
	    o valor retornado será diferente de zero */
		if(!PeekConsoleInput(console, &record, 1, (LPDWORD)&total))
            return 1;
		if(total > 0)
            if(record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown)
            {
                c = record.Event.KeyEvent.uChar.AsciiChar;
                FlushConsoleInputBuffer(console);
                if(c == '\r')
                    c = '\n';
                return c;
            }
	} while(1);
}

int LerInt(int minval, int maxval)
{
	char str[12];
	int x = 0;
	int i = 0;
    int ch;

	while((ch = LerChar()) != EOF && ch != '\n')
    {
        if(i == 0 && ch == '-')
        {
            // '-' no início
            putchar(ch);
            str[i++] = ch;
        }
        else if(ch == '\b' && i > 0)
        {
            // Backspace
            printf("\b \b");
            str[--i] = '\0';
        }
        else if(ch >= '0' && ch <= '9')
        {
            // Entre '0' e '9'
            str[i++] = ch;
            str[i] = '\0';
            /* Caso o primeiro número seja 0 e haja outro
            em sequência, o mesmo é retirado da string */
            if((str[0] == '0' && str[1] == '0') || (str[0] == '-' && str[1] == '0' && str[2] == '0'))
                str[--i] = '\0';
            else if(atoll(str) <= (long long)maxval && atoll(str) >= (long long)minval)
                putchar(ch);
            else
                str[--i] = '\0';
        }
    }

    str[i] = '\0';
    // Caso algum valor tenha sido digitado
    if(str[0] >= '0' || (str[0] == '-' && str[1] >= '0'))
        x = atoi(str);

    return x;
}

float LerFloat(float minval, float maxval)
{
    int aloc = 101;
	char* str = (char*)malloc(aloc);
	float x = 0.0f;
	int pnt = -1;
	int i = 0;
    int ch;

	while((ch = LerChar()) != EOF && ch != '\n')
    {
        if(i == (aloc - 1))
        {
            aloc += 100;
            str = (char*)realloc(str, aloc);
        }

        if(i == 0 && ch == '-')
        {
            // '-' no início
            putchar(ch);
            str[i++] = ch;
        }
        else if(pnt == -1 && ch == '.')
        {
            // '.'
            pnt = i;
            putchar(ch);
            str[i++] = ch;
        }
        else if(ch == '\b' && i > 0)
        {
            // Backspace
            printf("\b \b");
            str[--i] = '\0';
            if(i == pnt)
                pnt = -1;
        }
        else if(ch >= '0' && ch <= '9')
        {
            // Entre '0' e '9'
            str[i++] = ch;
            str[i] = '\0';
            /* Caso o primeiro número seja 0 e haja outro
            em sequência, o mesmo é retirado da string */
            if((str[0] == '0' && str[1] == '0') || (str[0] == '-' && str[1] == '0' && str[2] == '0'))
                str[--i] = '\0';
            else if(strtold(str, NULL) <= (long double)maxval && strtold(str, NULL) >= (long double)minval)
                putchar(ch);
            else
                str[--i] = '\0';
        }
    }

    str[i] = '\0';
    // Caso algum valor tenha sido digitado
    if(str[0] >= '0' || (str[0] == '-' && str[1] >= '0') || (str[0] == '-' && str[1] == '.' && str[2] >= '0'))
        x = strtof(str, NULL);

    return x;
}

Desse jeito o usuário não consegue digitar um valor fora do intervalo, nem zoar o programa digitando letras ou coisa do tipo.

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

23 minutos atrás, Lucca Rodrigues disse:

Acho que é o mais adequado, mas foram poucas as vezes que vi alguém aplicar em uma atividade acadêmica ou coisa do tipo.

Eu tinha feito isso há um tempo, só não sei se o comportamento da peek() ou da wait() muda entre os compiladores

 

É verdade. Acho que claramente vem dos livros e instrutores, que nunca apresentam scanf() como uma função que retorna um int, antes de tudo.

 

E esse é um dos erros mais comuns nas tais "atividades acadêmicas". :) 

 

E scanf() não foi escrita para isso, para ler do teclado. A ideia na época era consumir dados tabulares, como arquivos csv. Uma alternativa a usar AWK que era comum naquele tempo. Por isso o nome: scan formatted input.

 

Para ler do teclado sempre foi mais flexível usar fgets() e atoi atod atof e tal.

 

29 minutos atrás, Lucca Rodrigues disse:

Eu tinha feito isso há um tempo, só não sei se o comportamento da peek() ou da wait() muda entre os compiladores

 

Essas funções dependem apenas do Windows.

 

Mas nem precisa disso. Pode simplesmente desligar o eco e o LINE INPUT e ler com fgetc() as letrinhas uma por uma. É o mais flexível. Já te falei sobre isso. Se a letra for uma das que aceita então guarda e mostra. Se não for então pode tocar um bip e seguir lendo, sem mostrar nada.

 

E no linux é igual. Apenas as chamadas são diferentes e se usa ioctl().

 

Uma coisa que sempre dá certo é usar algo como a PICTURE do COBOL, afinal já são 70 anos de COBOL :D Pode ser uma expressão regular nos tempos modernos pós '70.

 

exemplo como no COBOL: "$ZZZ9V99" aceita um número. O Z é um número opcional, o 9 é um número obrigatório e V é a posição assumida para o ponto.  $ vira o símbolo da moeda no país. Então aceitaria 1 como R$ 0,01, 9999 como R$ 99,99 e tal. "A" indica uma letra, "X' aceita qualquer coisa. Acho que deu pra entender.

 

	void* meu_leitor( const char* mascara ); 

 

Então a função recebe uma string com isso e retorna o valor. Só um exemplo. E faz o que eu disse: desliga echo e line input para não mexer na tela, lê lê as letras uma a uma e testa com a máscara de leitura. Deu certo mostra, deu errado não mostra. Na saída restaura as opções do terminal.  Seguro e simples desde os '60. 

 

Pode claro retornar qualquer coisa. Uma função pra ler inteiros podia ser só 

 

	double meu_leitor_d( const char* mascara ); 

 

 E chamada com

 

	double nota = meu_leitor_d("ZZVZ9");

 

para o exemplo aqui. E aceitaria notas de 0 a 99.99. E não seria nada novo nem nos anos 70 :D 

 

 

Em geral isso é tratado com uma máquina de estados que vai acompanhando a string com a mascara e completando o valor conforme a digitação. É rápido de escrever.

  • Curtir 1
  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

35 minutos atrás, arfneto disse:

Mas nem precisa disso. Pode simplesmente desligar o eco e o LINE INPUT e ler com fgetc() as letrinhas uma por uma. É o mais flexível. Já te falei sobre isso. Se a letra for uma das que aceita então guarda e mostra. Se não for então pode tocar um bip e seguir lendo, sem mostrar nada.

Em geral o resultado é o mesmo para o caso, só que não uso fgetc() porque tenho que teclar Enter duas vezes. Dá pra chamar read() com o mesmo esquema, desabilitando echo e line input:

char LerChar(void)
{
    DWORD mode;
    int total = 0;
    int c = 0;

    HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
    if(h == NULL)
    {
        // GetStdHandle() falhou
        return '\0';
    }

    GetConsoleMode(h, &mode);
    SetConsoleMode(h, mode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));

    ReadConsole(h, &c, 1, (LPDWORD)&total, NULL);

    SetConsoleMode(h, mode);

    if(total > 0)
    {
        if(c == '\r')
            c = '\n';
        return (char)c;
    }
    else
    {
        // não leu nada
        return '\0';
    }
}

LerInt() e LerFloat() do código que postei avaliam o caractere digitado que é inserido na string caso o valor resultante após a inserção esteja dentro do intervalo.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

1 minuto atrás, Lucca Rodrigues disse:

Em geral o resultado é o mesmo para o caso, só que não uso fgetc() porque tenho que teclar Enter duas vezes. Dá pra chamar read() com o mesmo esquema, desabilitando echo e line input:

 

LINE INPUT no Windows é que faz isso: esperar o ENTER

 

E usaria fread mesmo, ou fgetc. Pode deixar a questão do sistema operacional resolvida no início e assim usar o mesmo código em Windows MAC ou Linux

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

@MarianaN Olá.

Essa falha e outras são detectáveis, quase senão por todos, os compiladores. Para isso, incremente suas opções de Alerta [consulte o manual do IDE] para todos [-Wall] erros [-Werror]. Com isso será praticamente educado(a) pelo compilador, algo que é quase sempre o suficiente.

 

Veja

Uma imagem da falha

image.png.dccf5265a85a9a98881275b1fff296eb.png

Com esses alerta, além de detecta a falha, exercita o inglês convenientemente!

[:)]  

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...