Ir ao conteúdo

C Código em C só funciona em uma ordem especifica?


Ir à solução Resolvido por devair1010,

Posts recomendados

Postado

Então gente, estou aprendendo linguagem c.

Chegando no capitulo de "Tipos definidos pelo programador" onde aprendemos a utilizar o STRUCT, notei um erro bem incomum (pelo menos para mim, que sou iniciante). Se eu rodar esse programa assim do jeito que está, na hora de fazer a leitura da variável rua (bloco 3) contida dentro de STRUCT, ela simplesmente não lê e pula para o próximo bloco para fazer a leitura do número (bloco 4), vide anexo .png (imagem de "erro"). porém se eu mudar o bloco 3 e coloca-lo entre o bloco 1 e 2 ele funciona perfeitamente. testei tanto no visual studio code quanto no code::blocks e em ambos o erro permanece. Lembrando que no livro a ordem é exatamente essa. Alguém poderia me explicar o por quê disso? Seria algum tipo de ordem de procedência, onde o que for char tem de vir primeiro?

 

Obs: Fiz os teste e, nem o código de exemplo do livro funcionou como deveria.

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

struct cadastro{
    char nome[50];
    int idade;
    char rua[50];
    int numero;
};

int main(){

  	//bloco 1
    struct cadastro cad1;
    printf("Digite seu nome: ");
    fgets(cad1.nome, 50, stdin);

  	//bloco 2
    printf("Digite sua idade: ");
    scanf("%d",&cad1.idade);

  	//bloco 3
    printf("Digite sua rua: ");
    fgets(cad1.rua, 50, stdin);

  	//bloco 4
    printf("Digite o número: ");
    scanf("%d",&cad1.numero);
    

    printf("\n");
    return 0;
}

 

teste.png

  • Obrigado 1
  • Solução
Postado

@beggarjs    esse erro é comum ,  quando usa scanf ,   pois ele não pega o newLine , tecla Enter do teclado , e quando o fgets vai pegar ,  o newLine está lá no buffer do teclado , e ele pega o newLine , e nem espera que seja digitado qq coisa ,

//bloco 2
printf("Digite sua idade: ");
scanf("%d",&cad1.idade);
//bloco 3
printf("Digite sua rua: ");
fgets(cad1.rua, 50, stdin);

e uma solução poderia ser não usar o scanf e apenas o fgets ,  e converter para inteiro usando a função atoi 

//bloco 2
char str[20];
printf("Digite sua idade: ");
/// scanf("%d",&cad1.idade);
fgets(str , sizeof(str) , stdin);
cad1.idade = atoi( str );
//bloco 3
printf("Digite sua rua: ");
fgets(cad1.rua, 50, stdin);

 

  • Obrigado 1
Postado

@devair1010 Muito obrigado.

Estava vasculhando o fórum e vi um problema parecido. Vou fazer do modo como você mencionou e volto para concluir o tópico. 

 

Poderia só me explicar o que seria esse sizeof()?

Posso usar fgets(str, 20, stdin)?

  • Obrigado 1
Postado

@beggarjs @beggarjs  o sizeo();  calcula automaticamente  a qtd de  de caracteres da string  , assim facilita pois o programador não precisa lembrar o tamanho declarado de uma string  , e acontece de mesmo se lembrando , ainda assim , colocar errado   ;    igual nesse seu codigo , voce colocou o valor  50  mas  precisa ser   49  , pois precisa de um espaco para o '\0'  ou  apenas  0 ,  finalizador de string ,   e tambem em outros sistermas diferentes de onde o programa foi criado , o tamanho dos dados serao diferentes , e o sizeof resolve isso  facilmente .     mas tambem pode especificar com o valor , igual voce colocou ,  mas deixando um espaco para finalizar a  string  .

  • Curtir 1
  • Obrigado 1
Postado

@devair1010 Muito obrigado pela explicação.

Acabei testando aqui com a var aux (como auxiliar) e deu tudo certo. primeiro eu fiz sem o uso da variável aux, e fiz direto com cad1.idade e cad1.numero o que não deu certo (vide anexo de como fiz da primeira vez). Agora em sua opinião está certo?

 

Obs: Existe algum modo de pegar esses dados sem o uso de uma variável auxiliar, como eu fiz ali ? 

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

struct cadastro{
    char nome[50];
    int idade;
    char rua[50];
    int numero;
};

int main(){
    char aux[50];

    struct cadastro cad1;
    printf("Digite seu nome: ");
    fgets(cad1.nome, 50, stdin);

    printf("Digite sua idade: ");
    fgets(aux, sizeof(aux), stdin);
    cad1.idade = atoi(aux);

    printf("Digite sua rua: ");
    fgets(cad1.rua, 50, stdin);

    printf("Digite o número: ");
    fgets(aux, sizeof(aux), stdin);
    cad1.numero = atoi(aux);
    

    printf("\n");
    return 0;
}

 

teste1.png

  • Curtir 1
  • Obrigado 1
Postado

@beggarjs    está quase certo ,   e  para usar atoi precisa de uma string mesmo ,  e essa aux é a ideaL  , e nos fgets coloque em a menos para o newLine , e também ao pegar uma string com fgets e na hora que for escrever essa mesma string na tela , vai pular para a linha de baixo ,  e isso não tem nenhum problema , mas causa desorganização no formate das escritas ,  e então é melhor remover esse newLine pego pelo fgets , com esse procedimento

cad1.nome[strlen(cad1.nome) - 1 ] = 0; /// remover o newLine pego pelo fgets

e seu código ficaria assim  :

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
struct cadastro
{
  char nome[50];
  int  idade;
  char rua[50];
  int  numero;
};
int main()
{
  char aux[50];
  struct cadastro cad1;
  printf("Digite seu nome: ");
  fgets(cad1.nome, 49, stdin);           /// deixar um espaco para o "\o"
  cad1.nome[strlen(cad1.nome) - 1 ] = 0; /// remover o newLine pego pelo fgets
  printf("Digite sua idade: ");
  fgets(aux, sizeof(aux), stdin);        /// deixar um espaco para o "\o"
  cad1.idade = atoi(aux);
  printf("Digite sua rua: ");
  fgets(cad1.rua, 49, stdin);            /// deixar um espaco para o "\o"
  cad1.rua[strlen(cad1.rua) - 1 ] = 0;   /// remover o newLine pego pelo fgets
  fgets(aux, sizeof(aux), stdin);        /// deixar um espaco para o "\o"
  cad1.numero = atoi(aux);               /// convet string para int
  printf("\nNome ---> %s\n\
          \rIdade --> %d\n\
          \rRua ----> %s\n\
          \rNumero -> %d\n\
          \r\t Tecle\n\n\n",
          cad1.nome , cad1.idade ,
          cad1.rua , cad1.numero);       /// um printf já resolve e com mais velocidade
  getch();
  return 0;
}

 

  • Curtir 1
  • Obrigado 1
Postado

@beggarjs    ok  ,    e não havia visto o anexo ,  e nele não vai funcionar mesmo , não , porque  "cod1.idade"  é do tipo int , e o fgets só pega string's  ,  que é do tipo char , mas que armazena mais de um caractere ;

  • Obrigado 1
Postado
21 horas atrás, beggarjs disse:

Chegando no capitulo de "Tipos definidos pelo programador" onde aprendemos a utilizar o STRUCT, notei um erro bem incomum (pelo menos para mim, que sou iniciante)


Não é um erro incomum. Ao contrário, é o mais comum dos erros dos iniciantes com scanf(). Os foruns e plantões de dúvida estão sempre vendo questões sobre isso. Aqui por exemplo. "o programa não lê o valor", "o programa passa reto pelo `scanf()`" são temas comuns por exemplo. E sempre vem aquela lista de soluções, como usar "[^\n]" no especificador de `scanf()`, "limpar o buffer" e outros mitos.

 

21 horas atrás, beggarjs disse:

na hora de fazer a leitura da variável rua (bloco 3) contida dentro de STRUCT, ela simplesmente não lê e pula para o próximo bloco para fazer a leitura do número (bloco 4)

 

A nomenclatura "bloco" é curiosa. Essas variáveis nesse caso são chamadas de membros da `struct`.

 

21 horas atrás, beggarjs disse:

Lembrando que no livro a ordem é exatamente essa. Alguém poderia me explicar o por quê disso? Seria algum tipo de ordem de procedência, onde o que for char tem de vir primeiro?

 

A culpa é do livro e -- se for o caso -- de seu professor. Vou tentar explicar e dar uns exemplos a seguir

 

Sobre essa declaração

 

struct cadastro{
    char nome[50];
    int idade;
    char rua[50];
    int numero;
};

 

prefira sempre

typedef struct 
{
    char nome[50];
    int idade;
    char rua[50];
    int numero;
    
}   Cadastro;

 

e em main()

 

    Cadastro um;

 

E mais uma vez vou culpar o livro. Isso não é um Cadastro. É só um item. É um desperdício há décadas livros não tocarem no assunto de composição de dados já na primeira oportunidade.

 

Veja uma diferença óbvia:

 

typedef struct
{
    char nome[50];
    int  idade;
    char rua[50];
    int  numero;

} Item;

typedef struct
{
    unsigned limite;
    unsigned qtd;
    Item     item[50];

} Cadastro;

 

E um exemplo de como usar...

 

    Item     um  = {.nome = "Johnny Cash", .numero = 42};
    Cadastro cad = {.limite = 50, .qtd = 0};
    // um item no inicio
    cad.item[0] = um;
    cad.qtd += 1;

 

Aí sim tem um cadastro de itens, com uma quantidade atual e um limite. É muito mais simples escrever assim.

 

Veja que pode atribui um item direto a uma posição da estrutura por exemplo. E controlar o tamanho é fácil porque tanto o tamanho quanto a  capacidade estão DENTRO do cadastro. Enquanto isso alunos passam o semestre usando contadores e ponteiros por todo o programa 🙂 

 

21 horas atrás, beggarjs disse:

Seria algum tipo de ordem de procedência, onde o que for char tem de vir primeiro?

 

Não. Como @devair1010 explicou, trata-se do uso errado de `scanf()`

 

Porque dá errado?


`scanf()` foi feita para ler entrada formatada --- scan formatted input --- e por isso tem esse nome. Não deveria ser usada para ler do teclado. `scanf()` para de ler assim que conseguir dados suficientes para o que foi pedido, e o resto fica lá para ser lido pelo seu programa ou outro programa. O teclado é pra todo mundo, como o mouse e a tela. Para todos os programas.

 

Exemplo: para idade e rua alguém digitou

 

    25<ENTER>Av. da Liberdade<ENTER>

 

sem esperar pelos prompts mesmo

 

Um `scanf()` com '%d' vai ler o `2` o `5` e ao ver o <ENTER> vai retornar com o valor `25` deixando `<ENTER>Av. da Liberdade` para ser lido. O `fgets()` vem a seguir e de cara vê o <ENTER> e já retorna, porque o tal '\n' marca o fim da string que a função deve ler. Para ser lido fica lá `Av. da Liberdade`.

 

O que fazer?

 

Como eu disse, essa função sequer foi escrita para isso, ler dados do teclado.

É usada em programas de estudo porque ela lê e converte os dados em uma chamada só. Só que os livros não começam por explicar a mecânica das coisas. Apenas consuma os dados da maneira esperada e no caso normal seria ler o tal '\n' que encerrou a leitura do número.

Claro que se o cara digitou `25reerrererereetre` e depois o ENTER vai dar m. depois de todo modo.

Ler do teclado com segurança é um porre e não é para iniciantes. Por sorte os programas de iniciantes não precisam dessa tal segurança 😄 O protótipo de scanf é então apenas tire o '\n' de lá e o programa vai rodar ok para usuários disciplinados.

 

O protótipo de scanf é

 

	int scanf(const char *format, ...)

 

isso quer dizer que ela retorna um `int`.

 

Se não está explicado no seu livro sugiro outro livro.

Se está você deveria ter lido. NUNCA deixe de testar o retorno de scanf(). É ingênuo seguir adiante.

 

fgets()

 

Essa função é mais segura mas apenas lê o texto e transfere. Não converte nada.

 

Eis o protótipo de fgets()

 

	char *fgets(char *str, int n, FILE *stream)

 

E aí tem o que @devair1010 não usou e não explicou. Todo dia programas de iniciantes e profissionais no mundo todo entram em loop por causa disso. Ou cancelam. `fgets` retorna um ponteiro e é isso que você tem que usar. NUNCA use ingenuamente o valor de str, o primeiro argumento

Porque? porque se der erro fgets() vai retornar NULL, mas não vai alterar o valor de str. E se ler algo vai retornar o endereço de str. Ou seja: milhares de programas pelo mundo continuam considerando o valor original de str se fgets falha.

 

Pode ser culpa do seu livro de novo. E do livro que o Devair usou para aprender isso.

 

21 horas atrás, devair1010 disse:

quando usa scanf ,   pois ele não pega o newLine , tecla Enter do teclado , e quando o fgets vai pegar

 

6 horas atrás, devair1010 disse:

e também ao pegar uma string com fgets e na hora que for escrever essa mesma string na tela , vai pular para a linha de baixo ,  e isso não tem nenhum problema , mas causa desorganização no formate das escritas ,  e então é melhor remover esse newLine pego pelo fgets , com esse procedimento

 

já falamos sobre isso aqui: nem sempre vai ter um '\n' no final da string. O programa vai dar errado nesses casos. Não se trata de desorganização mas sim de padrão. Veja esse programa de teste

 

EXEMPLO

 

#include <stdio.h>
int main(void)
{
    char campo1[4];
    char campo2[4];

    printf("Digite campo1: ");
    fgets(campo1, sizeof(campo1), stdin);

    printf("Digite campo2: ");
    fgets(campo2, sizeof(campo2), stdin);

    printf("\n\nCampos:\n[1]\t\"\%s\" (%d)\n[2]\t\"%s\" (%d)\n",
    campo1, sizeof(campo1),
    campo2, sizeof(campo2));
    return 0;
}

 

E veja o que acontece se o cara digita "teste42"

 

Digite campo1: teste42
Digite campo2:

Campos:
[1]     "tes" (4)
[2]     "te4" (4)

 

E entenda porque está errado: fgets() só coloca o '\n' ao final da string se couber. Se não tiver espaço vai passar adiante e deixar o resto pra ser lido. E aí vai acontecer a mesma coisa que acontece com scanf(): Passa reto pela leitura do segundo campo...

 

 

 

  • Curtir 1
  • Obrigado 2
Postado

@arfneto Muito obrigado pelas dicas. Vou tentar me policiar melhor a partir de agora. O problema é que quando estudamos por um livro acreditamos que o autor tá fazendo do melhor modo possível, o que não é o caso kk.

 

Para caso de curiosidade o livro que estou usando é Linguagem C, completa e descomplicada, por André Backes.

  • Curtir 1
Postado
2 horas atrás, beggarjs disse:

Muito obrigado pelas dicas. Vou tentar me policiar melhor a partir de agora. O problema é que quando estudamos por um livro acreditamos que o autor tá fazendo do melhor modo possível, o que não é o caso kk.

 

Para caso de curiosidade o livro que estou usando é Linguagem C, completa e descomplicada, por André Backes.

 

Muitas vezes não é o caso mesmo. Obrigado por citar o livro. Eu estava curioso mesmo. 🙂 

 

Acha que entendeu o que eu disse sobre os protótipos e o que estava errado nos códigos?

 

 

 

 

 

 

  • Curtir 1
Postado

@arfneto  Entender 100% eu não entendi, já estou com certa dificuldade para aprender esse assunto e o modo do qual você me mostrou é um pouco mais complicado para mim (lembrando que esse é meu primeiro contato com a linguagem). mas aos poucos estou tentando aplicar e compreender melhor o que me foi passado, agora mesmo estava fazendo mais exercícios e apliquei o typedef para ver como funciona (o livro também sita esse comando).

 

Não sei se pode por links de outros lugares aqui (se não poder eu apago), mas gostaria de deixar meu github caso você queira dar uma olhada em como anda minha compreensão da linguagem, lá tem um repositório com os exercícios do livro que estou fazendo.

https://github.com/joaosilva-d3v/LinguagemC_Andre-Backes_Exercicios

@arfneto  Entender 100% eu não entendi, já estou com certa dificuldade para aprender esse assunto e o modo do qual você me mostrou é um pouco mais complicado para mim (lembrando que esse é meu primeiro contato com a linguagem). mas aos poucos estou tentando aplicar e compreender melhor o que me foi passado, agora mesmo estava fazendo mais exercícios e apliquei o typedef para ver como funciona (o livro também sita esse comando).

 

Não sei se pode por links de outros lugares aqui (se não poder eu apago), mas gostaria de deixar meu github caso você queira dar uma olhada em como anda minha compreensão da linguagem, lá tem um repositório com os exercícios do livro que estou fazendo.

https://github.com/joaosilva-d3v/LinguagemC_Andre-Backes_Exercicios

  • Obrigado 1
Postado
8 minutos atrás, beggarjs disse:

Entender 100% eu não entendi, já estou com certa dificuldade para aprender esse assunto e o modo do qual você me mostrou é um pouco mais complicado para mim (lembrando que esse é meu primeiro contato com a linguagem). mas aos poucos estou tentando aplicar e compreender melhor o que me foi passado, agora mesmo estava fazendo mais exercícios e apliquei o typedef para ver como funciona (o livro também sita esse comando)

 

Eu postei código e alguns argumentos. Use o forum e pergunte sobre o que não entendeu. Deve ser o caminho mais curto. E assim fica a referência para outros aqui mesmo... 

 

E sobre o código que escreveu entenda que ler do teclado é um inferno mesmo. Mas apenas programas de teste e programas de estudo fazem isso então não é algo comum fora desse ambiente. Algumas dessas coisas não tem mesmo solução simples. Veja atoi() por exemplo, que retorna 0 quando não consegue converter uma string mas também retorna 0 quando a string é "0" 😄 . Sim, pode ser que defina errno em ERANGE ou EINVAL mas ninguém testa isso. scanf() retorna um int que ninguém testa, fgets() retorna um ponteiro e os estudantes se recusam a usar e mesmo uns profissionais) e por aí vai. C não foi escrita para isso.

Uma coisa importante pra entender é que seu programa fala de um cadastro mas a estrutura é de um item só. Essa é uma bobagem comum a muitos livros e apostilas , imagino, instrutores, e assim acaba SEMPRE nos programas dos alunos.

 

É como se o lance da composição de dados fosse algo muito avançado e fosse complicar a vida do cara e então deixam para outro momento mais "orientado a objetos" a ser discutido anos depois. Só que é o contrário: usar isso faz os programas mais simples desde o dia 0.

 

Entenda a diferença para as struct que eu mostrei depois e veja como todo programa que usa isso fica mais simples.

  • Obrigado 2

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