Ir para conteúdo

scanf e gets meu inferno


Salem Ribeiro

Posts Recomendados

Alguém consegue me explicar de maneira clara pq meu programa passa reto pela função fgets?
 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
/*----------------------------------------------------------------------------*/
/*                           Data Structs                                     */
/*----------------------------------------------------------------------------*/
struct DataNode
{
    int id;
    char *name;
    struct DataNode *node;
};
struct DataMove
{
    struct DataNode *previous;
    struct DataNode *next;
};
struct HeadNode
{
    int count;
    struct DataNode *first_node;
};

/*----------------------------------------------------------------------------*/
/*                           Functions                                        */
/*----------------------------------------------------------------------------*/
struct    DataNode *create_list()
{
    struct DataNode *node;
    
    node = (struct DataNode*)malloc(sizeof(struct DataNode));
    if (node)
        return(node);
    else
        return(NULL);
}

struct    DataNode *add(int i, char *name, struct DataNode *item)
{
    item->id = i;
    item->name = name;
    item->node = create_list();
    if (item->node)
        return (item->node);
    else
        return (NULL);
}

/*                               MAIN                                         */
/*----------------------------------------------------------------------------*/

int main(void)
{
    struct HeadNode head;
    struct DataMove node;
    char value_menu;
    char name[50];

    head.count            = 0;
    head.first_node        = create_list();
    node.next            = head.first_node;
    if (head.first_node)
    {
        value_menu = '0';
        do{
            printf("\e[H\e[2J");
            printf("ESTUDO DE LISTAS ENCADEADAS\n");
            printf("1)\tAdicionar\n");
            printf("2)\tProcurar\n");
            printf("3)\tListar\n");
            printf("4)\tOrdenar\n");
            printf("5)\tExcluir\n");
            printf("0)\tSair\n");
            printf("MENU: ");
            scanf("%c", &value_menu);
            switch (value_menu)
            {
                case '1':
                    printf("\nDigite o nome: ");
                    fgets(name, 50, stdin);
                    head.count ++;
                    node.previous = node.next;
                    node.next = add(head.count, name, node.next);
                    printf("\n\nCadastro realizado com sucesso!!!");
                    
                break;
            }
        }while (value_menu != '0');
    }
    else
    {
        printf("Não foi possível criar lista");
        return (1);
    }
    return(0);
}

Debugando percebi que fgets acaba recebendo um \n e não sei como resolver esse problema ridículo

 

estudo_list.c

Link para o comentário
Compartilhar em outros sites

Yep, como o @Marioh falou, é preciso consumir o buffer (no caso, o stdin, que é o buffer que você tá usando). Acontece que ao digitar 1 e pressionar [enter], você insere dois bytes no buffer: o '1' e '\n'. Aí você tá consumindo só o primeiro com o seu scanf(), deixando o '\n' lá. Como a fgets() lê até encontrar um \n, ela o encontra logo de cara não copia mais nada pra char nome[]. Ou seja, o comportamento é o esperado.

Tem outras soluções além da do Marioh. No final vão meio que dar no mesmo:

1. Usando a fflush():

scanf("%c", &value_menu);
fflush(stdin);

2. Lendo o \n (que é o segundo byte) pra outra variável (meio feio):

char tmp;
scanf("%c%c", &value_menu, &tmp);

No caso acima você não usaria o char tmp nunca mais, mas se olhar a documentação da scanf, vai ver que tem isso ó:

CONVERSIONS
     Following the % character introducing a conversion, there may be a number of flag characters, as follows:

     *        Suppresses assignment.  The conversion that follows occurs as usual, but no pointer is used; the result of the conversion is simply discarded.

3. Ou seja, podes usar o *:

scanf("%c%*c", &value_menu);

Assim não vai precisar da variável auxiliar.

Independente do que escolher (e provavelmente há soluções além dessas), o importante é você entender o que se passa. ?

Abraço!

Link para o comentário
Compartilhar em outros sites

Eita, não sabia disso. Você tem razão: a função é preparada para limpar buffers de saída, não de entrada. Tenho a sensação de já ter usado com buffer de entrada, então deve ter bastante código bugado meu por aí! kkkk

Existe a fpurge também, mas não sei se é padrão. Nunca usei.

Obrigado por alertar, @Marioh! ?

Abraço!

Link para o comentário
Compartilhar em outros sites

Bom dia pessoal

Eu fiz oq o grande @Mariohrecomendou e o problema foi solucionado, já tinha percebido via lldb a questão do '\n' sendo consumido pela variável name e imaginei que uma outra função resolvesse esse problema. Tentei o getchar, getc, getch e fflush e só apanhei.

Vou estudar melhor essa questão do stdin, voltarei aqui se consegui avançar e encontrar algo mais clean.

Muito obrigado @Marioh @Fernando Mercês

10 horas atrás, Marioh disse:

Se eu nao me engano tu tem que esvaziar os stdin.

tenta:


	char clear;
	while((clear = getchar()) != '\n' && clear != EOF);

depois do seu primeiro scan

espero que ajude.

 

Link para o comentário
Compartilhar em outros sites

@Fernando Mercês Parece que o fflush pode funcionar dessa maneira só não é garantido, li em algum lugar que os kernels atuais suportam, mas não sei ao certo tô com
o 5.5.13 e comigo não funciona. Já o __fpurge da stdio_ext.h funciona examento como esperado.

Li também em algum lugar (acho que foi no reddit) que realmente nao faz sentido dar um "flush" no stdin, afinal pra onde ele vai ser flushado ?
Lá na manpage do fflush ele fala que se a stream for de output ele força um write nos dados em buffer que ultimamente tem um destino.
 

Link para o comentário
Compartilhar em outros sites

Sorry... só vi agora. Fiquei internado num hospital na última semana (não... não foi o coronga!)...

O detalhe é que scanf varre um stream em busca do formato desejado, mas deixa para trás o restante, uma vez atendido o formato. Ao usar "%c" você pede a conversão de um único caractere, todos os demais que o seguem, "ficam para trás"...  Acontece que stdin normalmente precisa de um '\n', quando o redirecionamento é feito para o teclado e '\n' é o final de linha interpretado pelo fgets() também...

Não... usar fflush com streams de entrada não resolve, uma vez que, como @Marioh apontou corretamente, isso é undefined behavior. De fato, o comportamento, em alguns OSs, é simplesmente ignorar a ordem de "despejo".

Você pode tentar um scanf() diferente:

scanf("%c ", &value_menu);

Note o espaço depois do "%c". Isso faz com que quaisquer caracteres "separadores" sejam varridos, mas ignorados.

Link para o comentário
Compartilhar em outros sites

Exemplo:

#include <stdio.h>
#include <string.h>

int main( void )
{
  char v, *p;
  char buffer[16];

  // Lê 1 char de stdin, mas ignora todos os "separadores" (isspace()) subsequentes...
  scanf( "%c ", &v );

  // Lê 16 chars à partir do primeiro caracter válido do stream stdin.
  fgets( buffer, sizeof buffer, stdin );

  // Livra-se do '\n' no final da string, se houver.
  if ( p = strchr( buffer, '\n' ) )
    *p = '\0';

  printf( "'%c' : \"%s\"\n", v, buffer );
}

Testando:

Untitled.png.5f1bf3a7575b85e98d05b56e153d3e31.png

Ahhhhhhhhhh... NÃO use gets()... é uma função obsoleta!

Link para o comentário
Compartilhar em outros sites

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

  • Quem Está Navegando   0 membros estão online

    • Nenhum usuário registrado visualizando esta página.
×
×
  • Criar Novo...