Jump to content
Salem Ribeiro

scanf e gets meu inferno

 Read less than a minute

Recommended Posts

Posted (edited)
 Read less than a minute

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

Edited by Salem Ribeiro
Melhora na visualização do código

Share this post


Link to post
Share on other sites
 Read less than a minute

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.

  • Curtir 1

Share this post


Link to post
Share on other sites
 Read less than a minute

Obrigado mario

Eu encontrei essa solução nas internet. Mas é difícil acreditar que isso seja a melhor forma de resolver isso.

De qualquer forma muito obrigado por responder.

Share this post


Link to post
Share on other sites
 Read less than a minute

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!

Share this post


Link to post
Share on other sites
 Read less than a minute

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!

Share this post


Link to post
Share on other sites
Posted (edited)
 Read less than a minute

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.

 

Edited by Salem Ribeiro
  • Curtir 1

Share this post


Link to post
Share on other sites
 Read less than a minute

@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.
 

  • Curtir 1
  • l33t 1

Share this post


Link to post
Share on other sites
Posted (edited)
 Read less than a minute

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.

Edited by fredericopissarra
  • l33t 1

Share this post


Link to post
Share on other sites
 Read less than a minute

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!

  • Curtir 2

Share this post


Link to post
Share on other sites
 Read less than a minute

Legal pessoal! Eu sempre utilizei a função scanf com um espaço à mais no final, quando queria ler apenas um caractere. Não sei onde aprendi dessa forma, mas a questão é que nunca passou pela minha cabeça o motivo de fazer isso. A explicação foi nota 10! Obrigado!

Abraços.

  • Curtir 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...