Substituto de função gets, etc... [RESOLVIDO]

1. Substituto de função gets, etc... [RESOLVIDO]

Guilherme Fernando Garcia de Souza
GuilhermeFernan

(usa Fedora)

Enviado em 25/05/2018 - 22:05h

Fala galera blz, estou tendo um problema em programação com C++ no CodeBlocks no linux mint.
esse pequeno algoritmo é um exemplo do que quero fazer em um outro algoritmo.

#include<iostream>
using namespace std;
int main(void)
{
char nome[50];

cout <<"Digite um nome: ";
cin >>nome;

cout <<" o nome digitado é "<<nome;
}

com a função get() eu consigo gravar até mesmo a tecla espaço ex: o usuário irá digitar "Gabriel Souza" e irá salvar o nome. Porém no linux não existe a biblioteca conio.h para estar fazendo isso. Já tentei utilizar a fget() e mesmo assim não obtive exito. Já li outros tópicos com problemas parecidos e não obtive resultado. Se alguém puder por favor escrever como salvo um nome completo dentro da variável nome agradeço demais. :)

OBS: a função fget nescessita de alguma outra biblioteca a n ser


  


2. Re: Substituto de função gets, etc...

Fernando
phoemur

(usa Debian)

Enviado em 25/05/2018 - 23:34h


#include<iostream>

using namespace std;

int main(void)
{
char nome[50];

cout <<"Digite um nome: ";
cin.getline(&nome[0], 50);

cout <<"\nO nome digitado é "<<nome;

return 0;
}


Agora esse buffer com tamanho 50 está chamando dor de cabeça (overflow, etc...)
Não tenha vergonha de usar std::string !!!

#include <iostream>
#include <string>

using namespace std;

int main(void)
{
string nome;

cout <<"Digite um nome: ";
getline(cin, nome);

cout <<"\nO nome digitado é "<<nome;

return 0;
}

Note que agora qualquer tamanho de nome passado não vai dar problema no programa, pois std::string é dinamicamente alocada na heap (explicando simplificadamente)


3. Re: Substituto de função gets, etc...

Guilherme Fernando Garcia de Souza
GuilhermeFernan

(usa Fedora)

Enviado em 26/05/2018 - 02:15h

phoemur escreveu:


#include<iostream>

using namespace std;

int main(void)
{
char nome[50];

cout <<"Digite um nome: ";
cin.getline(&nome[0], 50);

cout <<"\nO nome digitado é "<<nome;

return 0;
}


Agora esse buffer com tamanho 50 está chamando dor de cabeça (overflow, etc...)
Não tenha vergonha de usar std::string !!!

#include <iostream>
#include <string>

using namespace std;

int main(void)
{
string nome;

cout <<"Digite um nome: ";
getline(cin, nome);

cout <<"\nO nome digitado é "<<nome;

return 0;
}

Note que agora qualquer tamanho de nome passado não vai dar problema no programa, pois std::string é dinamicamente alocada na heap (explicando simplificadamente)





Então man o problema não é usar dessa forma com strings e sim os benditos vetores, cujo professor pediu com char.

No windows com utilização do dev c++ plus ele compila de boas junto com as bibliotecas.

#include <iostream>
#include <stdlib.h>
#include<conio.h>
using namespace std;

main()
{
char nomes [2][30], x, escolha;
int indice;
int ano_atual, ano_nascimento, idade[2];

do
{

cout<<"Digite o ano atual: ";
cin>>ano_atual;

for (indice = 0; indice < 2; indice++)
{
cout << "Digite o nome: ";
x = getchar();
gets(nomes[indice]);
cout <<"Digite o ano de nascimento de " << nomes[indice] << ": ";
cin >>ano_nascimento;
system("cls");
idade[indice] = ano_atual - ano_nascimento;
}

for (indice = 0; indice < 2; indice++)
{
cout <<nomes[indice]<< " tem " << idade[indice] << " anos. \n\n";
}


cout<<"Deseja refazer? ";
cin >>escolha;
system("cls");
}while (escolha = 's');

}

agora no Linux esse bendito gets() não funciona e da maneira como me informou ali em cima, não consegui utilizar da forma que me informou.


4. Re: Substituto de função gets, etc...

Paulo
paulo1205

(usa Ubuntu)

Enviado em 26/05/2018 - 09:08h

Resposta curta (outra mais elaborada, que analisa o código que você postou, vem mais tarde).

O problema é que gets() foi oficialmente removida da biblioteca padrão do C (e, por conseguinte, do C++). Se no Windows funciona para você, você deve estar usando um compilador antigo, que não segue a última versão do padrão do C (de 2011; mas mesmo no padrão anterior, de 1999, gets() já era sinalizada como obsoleta, com sinalização de que não deveria mais ser usada).

Você não deve nem ao menos tentar usar gets(), mesmo que suas ferramentas de compilação a implementem, pois ela tem uma falha grave de projeto, que a torna gravemente insegura em qualquer contexto.

Alternativas:

  • Em C: fgets(), scanf("%[^\n]", ...), getline() (POSIX, possivelmente não disponível no Windows). Todas têm comportamentos diferentes de gets(), de modo que você terá de fazer adaptações.

  • Em C++: std::istream::getline() (a mais conveniente para strings contidas num array de char, na minha opinião), std::istream::get(char *, std::streamsize) (mais parecida com scanf("%[^\n]", ...)). Podendo usar std::string, std::getline() é muito melhor.


5. Re: Substituto de função gets, etc...

Paulo
paulo1205

(usa Ubuntu)

Enviado em 26/05/2018 - 11:04h

GuilhermeFernan escreveu:

Então man o problema não é usar dessa forma com strings e sim os benditos vetores, cujo professor pediu com char.


Não sei onde você estuda, mas uma triste verdade em grande parte dos cursos de formação em TI é que os professores frequentemente não entendem nada daquilo que estão ensinando.

Assim sendo, não limite seu aprendizado àquilo que diz o professor. Quando você tiver uma tarefa para entregar, especialmente se valer nota, siga o que ele manda, mas não permita de maneira nenhuma que isso limite o seu próprio horizonte. Estude além do que ele pede. Saiba mais do que ele. Vá além.

No windows com utilização do dev c++ plus ele compila de boas junto com as bibliotecas.

#include <iostream>
#include <stdlib.h>
#include<conio.h>


Não vejo nenhum motivo para você usar <conio.h> nesse programa. Você não usou nenhuma função dessa interface não-padronizada, então poderia suprimir sumariamente esse cabeçalho obsoleto do seu programa.

Quanto ao resto, uma vez que você está usando C++, você deveria provavelmente usar a forma C++ “<cstdlib>” em lugar de “<stdlib.h>”, que é a forma do C.

using namespace std;

main()


Essa forma de declarar uma função, omitindo o tipo de retorno, é inválida, e provavelmente será rejeitada por compiladores modernos bem configurado.

Se você foi ensinado a fazer desse jeito, isso reforça o que eu disse sobre professores (ou autores de apostila) que não sabem o que estão ensinando.

C++ tem aversão ao recurso de versões antigas do C de assumir implicitamente o tipo int quando o nome do tipo é omitido numa declaração. Desde muito cedo na história do C++ (ou seja, antes mesmo de haver um padrão oficial para a linguagem, o qual foi publicado em 1998) isso era considerado um erro. O próprio C, aliás, passou a repudiar tal prática a partir da revisão de seu padrão de 1999. Que alguns compiladores aceitem isso, ainda mais em C++, é um relaxamento do padrão, em favor de compatibilidade com código obsoleto. O correto mesmo seria atualizar código velho, e jamais produzir código novo com a cara de código antigo.

É uma boa prática configurar seu compilador para gerar a maior quantidade possível de mensagens de diagnóstico, que ajudem a localizar e apontar código perigoso ou obsoleto. Se você estiver usando um compilador da família do GCC, eu recomendo as opções “-Wall -Werror -O2 -pedantic-errors”.

{
char nomes [2][30], x, escolha;
int indice;
int ano_atual, ano_nascimento, idade[2];

do
{

cout<<"Digite o ano atual: ";
cin>>ano_atual;

for (indice = 0; indice < 2; indice++)
{
cout << "Digite o nome: ";
x = getchar();
gets(nomes[indice]);


Muita gente — inclusive eu — entende que não é muito bom misturar o uso de funções de entrada e saída de dados em C e em C++. No mínimo, por uma questão de consistência. Num caso extremo, pode não haver sincronismo entre as operações com funções do C e os objetos de stream do C++.

Além disso, você usou essas funções sem ter incluído <cstdio>. Dependendo da implementação, isso pode vir a causar erros de compilação, por causa de símbolos não-declarados.

Mais ainda: getchar() retorna um valor do tipo int, mas você atribui esse valor a uma variável do tipo char, o que implica que pode haver truncamento. Sobre este ponto, gostaria de fazer dois comentários. O primeiro é que você deve se acostumar a não misturar esses tipos (como subcomentário, ressalto que esse é outro erro comumente cometido por maus professores), por dois bons motivos: o primeiro é que getchar()/fgetc()/getc() empregam os bits que um int tem a mais que um char para sinalização de erro, e tais bitas são desprezados quando você trunca o valor de retorno para caber num char. Tão grave (ou até mais grave) que isso é que, dependendo da implementação, char pode ser com sinal (funcionalmente equivalente a signed char, com valores que vão de -128 a 127) ou sem sinal (funcionalmente equivalente a unsigned char, com valores de 0 a 255). Quando getchar() recebe um caráter válido (ou seja, sem erro), ela sempre devolve um valor que seria compatível um um unsigned char (i.e. de 0 a 255), e que, portanto, é compatível com as funções de <cctype>/<ctype.h>. Se você converter esse valor para char numa máquina em que esse char é com sinal, pode acabar ficando com valores negativos, e tais valores negativos se mantêm negativos ao serem convertidos de volta para int, tornando-se incompatíveis com as funções de <cctype>/<ctype.h> (e possivelmente até com outras funções da biblioteca padrão).

O segundo é que você simplesmente despreza o valor que acabou de ler para x. Por um lado, seria interessante sempre tratar todos os resultados de operações de leitura. Mais cedo ou mais tarde, você vai encontrar situações em que os dados dispostos na entrada terão um formato diferente daquilo que você esperaria, que por fim prematuro dos dados, quer por letras onde você esperaria números, ou mesmo por conteúdo corrompido. Cada uma das operações de leitura (no seu caso, são três: scanf(), getchar() e gets()) deveria ser tratado. Isso pode parecer tedioso — e em boa medida é mesmo! —, mas é algo melhor de se aprender a praticar desde quando você ainda tem programas simples, para que você não descubra isso apenas quando já tiver programas bem mais complexos, e portanto mais difíceis de depurar.

Eis como isso poderia ter sido feito (seguindo o formato atual, misturando os estilos de C e C++).

int ano_atual, x;
char nome[50];
/* ... */
cout << "Informe o ano atual: ";
if(!(cin >> ano_atual)){
cerr << "Erro de leitura.\n";
exit(1);
}
do
x=getchar();
while(isspace(x) && x!='\n'); // Descarta espaços em branco; para ao encontrar fim de linha.
if(x==EOF){
cerr << "Erro de leitura.\n";
exit(1);
}
else if(x!='\n'){
cerr << "Dados extras na linha (deveria haver apenas um número). Abortando.\n";
exit(1);
}
if(!fgets(nome, sizeof nome, stdin)){
cerr << "Erro de leitura.\n";
exit(1);
}
else{
// fgets() captura também a marca de fim de linha ('\n'), então verifica
// se essa marca existe, para poder removê-la, ou se ela não existe,
// indicando leitura truncada.
size_t tam_nome=strlen(nome);
if(nome[tam_nome-1]!='\n'){
// Não achou fim de linha: a entrada está truncada.
if(tam_nome+1>=sizeof nome){
// Usuário digitou uma linha longa demais.
cerr << "Nome longo demais. Abortando.\n";
}
else{
// Trucamento mesmo havendo espaço livre: isso indica erro de leitura
// ou fim prematuro dos dados de entrada.
cerr << "Erro de leitura.\n";
}
exit(1);
}
nome[--tam_nome]='\0'; // Remove a marca de fim de linha. Tamanho da string encolhe uma posição.
if(tam_nome==0){
// Usuário digitou uma linha vazia. Essa situação pode não ser válida, dependendo do contexto.
cerr << "Leitura inválida: nome vazio.\n";
exit(1);
}
}
// Se chegar neste ponto, todas as leituras feitas acima foram válidas.


Com leituras puramente em C++, o tratamento de erros poderia ser feito com exceções.

int ano_atual;
char nome[50];

try {
cin.exceptions(ios::bad_bit);
cout << "Digite o ano atual: ";
cin >> ano_atual;
cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
cin.getline(nome, sizeof nome);
}
catch(ios_base::failure &e){
cerr << "Erro de leitura\n";
exit(1);
}


(Contudo, se você de todo decidir que não quer tratar o valor de retorno, poderia ter desprezado também o valor retornado por getchar(), em lugar de o atribuir a uma variável com o tipo errado.)

cout <<"Digite o ano de nascimento de " << nomes[indice] << ": ";
cin >>ano_nascimento;
system("cls");


Você tem realmente de limpar a tela aqui? Chamar system() significa executar um programa externo ao seu através do sistema operacional. Essa operação é computacionalmente cara, e sujeita a vários erros — inclusive ao fato de que o comando invocado pode ter outro nome ou nem mesmo existir em máquinas diferentes (no Linux, por exemplo, o comando para limpar a tela é clear, não CLS, como no mundo Microsoft).

idade[indice] = ano_atual - ano_nascimento;
}

for (indice = 0; indice < 2; indice++)
{
cout <<nomes[indice]<< " tem " << idade[indice] << " anos. \n\n";
}


cout<<"Deseja refazer? ";
cin >>escolha;
system("cls");
}while (escolha = 's');

}


agora no Linux esse bendito gets() não funciona e da maneira como me informou ali em cima, não consegui utilizar da forma que me informou.


Essa já foi respondida na outra mensagem.






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts