×
Ir ao conteúdo
  • Cadastre-se

C Coisas obsoletas/que deveriam ser evitadas em C


Lucca Rodrigues
Ir à solução Resolvido por mauro_b,

Posts recomendados

Com a prática, adquire-se experiência, e imagino que muitos aqui tem bem mais experiência que eu.

Como já dizia @arfneto e outros programadores, há uma tal sobrevivente, a função getch(), dos anos 80, da biblioteca conio, que tem 30 anos e de nada serve há pelo menos 20... Vejo muitos usando, inclusive eu tenho o mau hábito de usar de vez em quando, mas agora gostaria de saber, o que mais há de ser obsoleto ou que deveria ser evitado?

Como por exemplo, não é muito bom usar scanf() para ler do teclado, já que ela lê a entrada formatada e talvez possam haver algumas inconveniências quanto a isso, enfim, gostaria de ler suas opiniões, explicações e alternativas (inclusive a cerca de getch() e scanf()), já que isso só se adquire com experiência.

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

@Flávio Pedroza Isso mesmos!

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

Difícil recomendas alternativas quanto as padronizações porque para mim são padrões então não tem muito o que discutir.

 

"De a César o que é de César."

 

No caso de conio.h, por exemplo; Que não é padrão, mas é roupagem do windows.h que troca nomes e simplifica poucos métodos somente no Windows.

 

Ora, se é uma aplicação MS-DOS\Prompt Windows ™ então use somente windows.h

 

Inclusive, não precisa usar nada do padrão.

 

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

5 minutos atrás, Mauro Britivaldo disse:

Difícil recomendas alternativas quanto as padronizações porque para mim são padrões então não tem muito o que discutir.

Sei lá, nem sempre o padrão vai ser o mais adequado a se usar...

 

2 minutos atrás, Matheus Maldi disse:

Domine a linguagem, e você saberá o que deve ou não usar

É o que eu to tentando fazer kkk, aos poucos...

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

  • Solução

é, quem busca excelência encontra mais facilmente nas bibliotecas apropriadas.

 

No exemplo citei Windows.h então quando importa ela esqueça stdio.h e todo o resto.

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

7 minutos atrás, Mauro Britivaldo disse:

citei Windows.h então quando importa ela esqueça stdio.h e todo o resto

Realmente.

De qualquer forma, sei que posso contar com vocês pra qualquer coisa, não deixarei de perguntar aqui no fórum :)

Valeu pela ajuda.

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

Bom, segue algumas recomendações:

Use o compilador Clang, funciona em todas as plataformas nunca terá problema com licença.

Não use funções que possam causar overflow.

Aprenda usar algum sistema de build, eu gosto do meson+ninja, mas aprenda o make e quando perceber que precisa de algo a mais, vá para o meson.

Prefira usar calloc em vez de malloc.

Use valgrind para verificar se tem algum leak.

Prefira este Style Guide: https://cs50.readthedocs.io/style/c/

 

Veja este video: https://www.youtube.com/watch?v=443UNeGrFoM

 

 

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

4 horas atrás, Lucca Rodrigues disse:

há uma tal sobrevivente, a função getch(), dos anos 80, da biblioteca conio, que tem 30 anos e de nada serve há pelo menos 20

 

  • conio.h: Esse era meu primeiro palpite, e era fácil. Eu postei outro dia o que tem nessa biblioteca e não é nada empolgante. E achar uma cópia disso para manter na máquina para usar uma única função cujo propósito é ler uma letrinha é algo pra pensar. E ver ncurses, sucessora de curses, ser proposta como alternativa em muitos lugares também é folclórico. Soube até que alguém que portou isso para Windows, mas não sei se existe de fato. Mas algo conio dos anos 80 escrita para DOS tem boa chance de ser obsoleto.
     
  • scanf() para consumir dados do teclado também é fácil de listar. Mas obsoleta não é a palavra. Apenas é inadequada. scanf() é sensacional e foi escrita para ler dados formatados --- scan f ormatted data --- acho que por um dos maiores desenvolvedores de todos os tempos. É como um mini AWK dentro do C. Se você tem uma tabela de milhares de linhas com vários tipos de dados vai gostar de consumir tudo em umas 10 linhas usando scanf(). Mas não é pra ler entrada do teclado.
     
  • Eu listaria como obsoleto em C qualquer programa interativo, desses que leêm do teclado e escrevem na tela, aquelas coisas ingênuas com menus e tal.
  • Programas de console em modo texto no geral não dá mais pra taxar de obsoletos porque com a debandada dos computadores para a nuvem tudo vem mudando. Tem a necessidade de administrar essas máquinas com interfaces de texto e nem sempre arquivos JSON e XML ou YAML são suficientes e se vê alguma necessidade de programas rápidos para rodar nesses ambientes ou em contato com eles, já que do lado administrativo essas máquinas não tem outra interface em muitos casos. Essa é a a razão eu acho de a Microsoft por exemplo ter gasto milhões de dólares em desenvolvimento de um novo modelo de console para Windows com o ConPTY a partir de 2017: Azure, Google Cloud, AWS, Heroku, containers...
     
  • Taxar a linguagem C em si como obsoleta é um provável exagero. Me lembra de quantas vezes já ouvi e li que "o dinheiro de papel ia acabar", que "os livros eram coisa do passado", que "as crianças hoje em dia sabem tudo de computador", que "o meu sobrinho sabe tudo de tecnologia" e coisas assim. O mercado de automação e eletrônica embarcada, os cross-compilers, a absurda quantia de software existente escrito em C, incluindo aí até algumas das propostas linguagens modernas muito adequadas para substituir C :) e que foram escritas em C e rodam em sistemas escritos em C. E quanto das appliances em uso na internet tiveram e tem firmware escrito em C? 
    Acha que alguém escreve software para roteadores em puro Assembler? No mais alto nível de Python? Em java? Na verdade não. Software para 5G em Go? Provavelmente não.E as bibliotecas extraordinárias que permitem fazer tudo em Python, deste tortas e bolos, cuidar das crianças e pesquisa genética são em geral escritas em C... Coisas assim sugerem que a linguagem vai estar aí por um tempo ainda...
  • Curtir 3
  • Amei 1
  • Haha 1
Link para o comentário
Compartilhar em outros sites

As minhas dicas, algumas já foram repetidas acima:

Se possível atualize o seu compilador. GCC ainda é o compilador universal, Clang é tão bom quanto mas o suporte é limitado para arquiteturas mais específicas, como microcontroladores.

Evite as bibliotecas do DOS, se puder.

Corra das funções consideradas obsoletas, por exemplo, `gets()`.

Saiba usar o `scanf()`. No GNU/Linux, leia toda a `man 3 scanf`, vale a pena.

Não usar `fflush(stdin)`ou outros comportamentos indefinidos.

É bom evitar os VLAs (vetores alocados dinamicamente na stack) e a função `alloca()`.

Macros do preprocessador são poderosas, mas são difíceis de debugar e podem causar problemas surpreendentes. Como boa prática coloque suas macros dentro de parêntesis ou do-whiles sempre que possível para evitar problemas na ordem de avaliação de condicionais e bizarrices com o ponto e vírgula.

Evite usar `-ffast-math`. Ponto flutuante já surpreende com o comportamento normal.

Sempre leia os avisos do compilador e pense duas vezes antes de ignorá-los.

Garanta que suas bibliotecas/cabeçalhos entrem só uma vez no código (include guards ou pragmas)

Os compiladores verificam mais profundamente seu código quando habilita certas otimizações, o que expões alguns bugs que podem estar escondidos.

Use o Valgrind para verificar vazamentos de memória e acessos indevidos.

Lembre-se que C não é C++ e vice-versa.

Não compare variáveis com sinal com sem sinal.

É uma boa ideia inicializar os ponteiros em NULL.

Prefira deixar o `malloc()` e o `calloc()` sem o cast, em C a promoção é automática. Também relacionado a isso, é bom não especificar o tipo da variável diretamente no operador `sizeof`.

 

Dependendo do seu caso de uso, habilitar todas as flags do GCC recomendadas pela Red Hat, também comuns em outras distribuições, pode ser uma boa ideia:

https://developers.redhat.com/blog/2018/03/21/compiler-and-linker-flags-gcc/

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

1 hora atrás, arfneto disse:

scanf() para consumir dados do teclado também é fácil de listar. Mas obsoleta não é a palavra. Apenas é inadequada

Sim, ja tive alguns problemas por conta de usá-la para ler do teclado, e imagino que todo mundo já passou por isso no início. Deveria ser evitada para ler do teclado há muito tempo.

 

@arfneto @Davi Silva Santos Obrigado pelas dicas. Fico feliz de conhecer algumas dicas citadas por vocês, é sinal de que estou aprendendo haha

Vou pesquisar certinho aqui tudo que foi dito, obrigado novamente.

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

10 horas atrás, Lucca Rodrigues disse:

Sim, ja tive alguns problemas por conta de usá-la para ler do teclado, e imagino que todo mundo já passou por isso no início. Deveria ser evitada para ler do teclado há muito tempo

 

Talvez eu já tenha te dito isso. Me desculpe se soar repetitivo

  • Em geral não era nem pra ler to teclado mesmo. Interfaces gráficas são mais controláveis e o visual e o resultado compensam. A API do sistema era pra ser o padrão mas o modelo de programação é um p0R$R$3, difícil de escrever, de ler e de manter. Então ninguém usa. Todas as API que já vi e que tiveram ou tem algum sucesso vão no caminho de como era em curses nos '80. Eventos, Initialize(), CallBacks... Como o Delphi dos '90, o Qt, SDL, wxwidgets, GTK, OpenGL, DirectX...
  • De todo modo para o cara que está aprendendo e para programas que podem tolerar alguma falta de controle o melhor é ler as letras com fgetc() ou as linhas com fgets(). E converter os tipos usando as bibliotecas. Muito simples e efetivo.
  • Para o uso profissional e para programas que não podem tolerar uma entrada imprevista ou zoar uma tela bonitinha então o caminho é
    • Em windows: não ler do teclado. Usar PeekConsoleInput() e ler direto do buffer. Desabilitar echo usando SetConsoleMode() e separar a leitura do display: você marca as teclas que aceita e quando vem algo:
      • se for uma delas: você mostra o símbolo na tela, avança o cursor ou faz o que faz sentido no seu programa.
      • Se leu o campo todo você retorna o valor e pronto
      • Se for inválida você usa um Beep() ou mostra uma mensagem de status e continua esperando
    • Em Linux/MacOS você usa ioctl() e muda VTIME e VMIN para zero, desabilita eco e faz a mesma coisa que no Windows, só que como o Linux não tem console você usa read() mesmo. Ou fgetc() ou qualquer coisa. Apenas lê como seria no Windows. Só que o programa não para.
    • O essencial é não parar o programa na leitura. Já tive dificuldade para explicar isso :( mas imagine um programa/jogo de tiro parar num read() e perguntar: "Tecle ENTER para atirar" :D "Digite a direção do tiro e tecle 2" :D 
  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

Eu acho que o pior erro das pessoas que estão começando é querer pular etapas, o mínimo de etapas para mim para quem está querendo aprender a programar em C é.

 

1. Aprender a instalar o compilador que vai usar, muitas pessoas pegam um DevC da vida e pensam que o programa é magico, que simplesmente faz um executável surgir.

2. Usar as coisas velhas do próprio C, mas especificamente as bibliotecas, em C as padrões são.

Spoiler

<assert.h>
<complex.h>
<ctype.h>
<errno.h>
<fenv.h>
<float.h>
<inttypes.h>
<limits.h>
<locale.h>
<math.h>
<setjmp.h>
<signal.h>
<stdarg.h>
<stdbool.h>
<stddef.h>
<stdint.h>
<stdio.h>
<stdlib.h>
<string.h>
<time.h>

 

3. Aprender a usar as flags do compilador, principalmente as que servem para reportar erros.

as flags que eu penso ser mais importantes no GCC são

Spoiler

-Wextra -fsanitize=address

Essa ultima é mais rápida do que o valgrind.

 

as outras coisas o @arfneto já fez um resumo de tudo.

 

mas!

23 horas atrás, arfneto disse:

E ver ncurses, sucessora de curses, ser proposta como alternativa em muitos lugares também é folclórico.

para o terminal, eu acho que ncurses é sim uma alternativa viável, tanto pelo suporte atualizado com o windows (sim isso mesmo windows, e ela foi atualizada esse mês. regularmente desde 1994) quanto para fazer programas para o linux.

 

Uma coisa ruim sobre C é que não há limites. se você só programou em C isso pode parecer estranho, mas quando você aceita qualquer coisa como entrada isso pode causar alguma dores de cabeça como buffer Overflow, arbitrary code execution e muitas outras coisas.

aqui um exemplo interessante.

[TAS] NES Super Mario Bros. 3 "arbitrary code execution"

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

10 minutos atrás, KXSY disse:

para o terminal, eu acho que ncurses é sim uma alternativa viável, tanto pelo suporte atualizado com o windows (sim isso mesmo windows, e ela foi atualizada esse mês. regularmente desde 1994) quanto para fazer programas para o linux

 

Eu disse que é folclórico porque ncurses é totalmente baseada em terminfo e ioctl() e essas coisas não tem correspondente em Windows. Ou não tinham até 2018 e o CONPTY da Microsoft. E do lado do Windows tem o SUBSYSTEM:CONSOLE e tudo em volta dele, que não tem correspondente no Linux. Por isso acho folclórico implementar toda uma camada de suporte para as funções rodarem no Windows.
 

Usei curses durante muito tempo, em Unix. 


Sempre tive meus candidatos a portar do Unix pro Windows e até precisei portar algumas coisas, como getopt() em especial, mas nunca pensei em ncurses. Talvez porque nunca escrevi nada usando curses que tivesse valor em usar no Windows. E exceto pela portabilidade eu nunca cheguei a ver razão para usar ncurses e não qualquer outra API ou framework disponível no Windows mesmo.

 

Mas é legal que seja portável. Nesses dias mesmo pensei em usar algo assim para um configurador em uma máquina na nuvem. 

 

 

Link para o comentário
Compartilhar em outros sites

  • mês depois...

@Matheus Maldi e @Davi Silva Santos, Boa noite...

 

Em 03/09/2020 às 22:12, Matheus Maldi disse:

Prefira usar calloc em vez de malloc.

Poderia me explicar o motivo? Comecei a estudar alocação dinâmica não faz muito tempo, e voltei aqui porque me lembro de ter lido isso.

 

Em 03/09/2020 às 23:17, Davi Silva Santos disse:

Prefira deixar o `malloc()` e o `calloc()` sem o cast, em C a promoção é automática. Também relacionado a isso, é bom não especificar o tipo da variável diretamente no operador `sizeof`.

O que isso quer dizer?

Eu pesquisei e achei algo dizendo que converter o valor de retorno de, por exemplo, malloc() para x não altera o tipo do ponteiro, então não muda nada. Seria isso?

Isso vale também pra realloc()?

 

Obrigado pela ajuda pessoal.

Link para o comentário
Compartilhar em outros sites

4 horas atrás, Lucca Rodrigues disse:
Em 03/09/2020 às 22:12, Matheus Maldi disse:

Prefira usar calloc em vez de malloc.

Poderia me explicar o motivo? Comecei a estudar alocação dinâmica não faz muito tempo, e voltei aqui porque me lembro de ter lido isso.

Basicamente porque o calloc inicializa todas as posições com zero, mas C é uma linguagem espartana, então só utilize quando precisar (ex: precisa reservar um bloco de memoria que não será utilizado em seguida, então inicialize com zero).

 

4 horas atrás, Lucca Rodrigues disse:
Em 03/09/2020 às 23:17, Davi Silva Santos disse:

Prefira deixar o `malloc()` e o `calloc()` sem o cast, em C a promoção é automática. Também relacionado a isso, é bom não especificar o tipo da variável diretamente no operador `sizeof`.

O que isso quer dizer?

Eu pesquisei e achei algo dizendo que converter o valor de retorno de, por exemplo, malloc() para x não altera o tipo do ponteiro, então não muda nada. Seria isso?

Isso vale também pra realloc()?

Deixar sem o cast seria, deixar de especificar para qual tipo você quer que a variável seja.

Exemplo

	d = malloc(sizeof(int));

O compilador se encarregara de fazer.

	d = (int*)malloc(sizeof(int));

Quando você passa um endereço de memoria que é void para um ponteiro de int automaticamente ele se torna um ponteiro para inteiro.

 

É especificar o tipo de variável seria isso.

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

typedef unsigned short hu16;

int main(void)
{
    hu16 *p, n;
    /* Reserva memoria para p */
    p = malloc(sizeof(*p)); /* *p vai dar o tamanho para o ponteiro */

    printf("\nTamanho da memoria reservada para o ponteiro:\t%lu\\bytes",sizeof(*p));
    printf("\nTamanho do endereco ponteiro:\t%lu\\bytes",sizeof(p));

    printf("\n\nTamaho da memoria reservada para n:\t %lu\\bytes",sizeof(n));
    printf("\nTamanho do endereco que aponta para n:\t %lu\\bytes",sizeof(&n));

    getchar();  /* Pausa */
    /* Libera a memoria de p */
    free(p);
    p = NULL;
    return(0);
}

Veja que se você tivesse colocado no malloc p = malloc(sizeof(hu16)) , se você precisasse mudar o nome da referencia do tipo (hu16) para outro nome (digamos hu32) você teria que mudar todos os lugarares onde faz referencia ao tipo (hu16).

 

Vou deixa alguns links relacionados.

C-Faq

KernelCodingStyle

 

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

@KXSY

8 horas atrás, KXSY disse:

Deixar sem o cast seria, deixar de especificar para qual tipo você quer que a variável seja.

Isso vale pra realloc() também, né?

Aqui no CodeBlocks da erro, falando algo como: "invalid conversion from 'void*' to 'char*'", e a mesma coisa acontece com malloc() e calloc().

Inclusive eu testei esse programa que mandou:

image.png.4d3492ebf1ac8d8e219d98890166a3cf.png

 

Se eu deixar sem o cast quando uso sizeof(), por exemplo, ao fazer uma comparação, ele só da um aviso, algo como: "comparison of integer expressions of different signedness: 'int' and 'long long unsigned int'", então não chega a ser erro. Mas deixar com o cast implica em algo? Caso a resposta seja não, acho que iria preferir deixar com à ter que ver esses avisos e erros...

 

8 horas atrás, KXSY disse:


/* Libera a memoria de p */ 
free(p); 
p = NULL; 
return(0);

Isso é necessário? Ou é só para caso de não precisar mais do ponteiro, daí libero a memória.

Link para o comentário
Compartilhar em outros sites

3 horas atrás, Lucca Rodrigues disse:

Aqui no CodeBlocks da erro, falando algo como: "invalid conversion from 'void*' to 'char*'", e a mesma coisa acontece com malloc() e calloc().

Inclusive eu testei esse programa que mandou:

image.png.4d3492ebf1ac8d8e219d98890166a3cf.png

 

O problema é que o seu codeblocks está configurado para compilar para C++ é não para C, hoje em dia todo compilador de C++ consegue compilar C, mas você teria que configurar o compilador para isso.

 

Tentativa de compilar em um compilador configurado para C++.

1050070954_erroC.thumb.png.12a729935c0a7e2608d6b8f2fcdd7436.png

 

Sintaticamente correto em um compilador de C.

COk.thumb.png.19c2f49e2318346008d443972308fe9a.png

 

Pelo o que eu vi em C++ o cast e obrigatório, já em C e opcional.

 

3 horas atrás, Lucca Rodrigues disse:

Se eu deixar sem o cast quando uso sizeof(), por exemplo, ao fazer uma comparação, ele só da um aviso, algo como: "comparison of integer expressions of different signedness: 'int' and 'long long unsigned int'", então não chega a ser erro. Mas deixar com o cast implica em algo? Caso a resposta seja não, acho que iria preferir deixar com à ter que ver esses avisos e erros...

Eu acho que você ainda não entendeu muito bem as expressões em C, recomendo ler no livro c completo e total as expressões em C na pagina 56.

Essa nota de uma aula da usp resume bem o porque de não colocar cast.    

 

3 horas atrás, Lucca Rodrigues disse:
12 horas atrás, KXSY disse:


/* Libera a memoria de p */ 
free(p); 
p = NULL; 
return(0);

Isso é necessário? Ou é só para caso de não precisar mais do ponteiro, daí libero a memória.

Se você não for mais usar o ponteiro que você reservou memoria para ele, o correto e liberar a memoria do mesmo.

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

@KXSY 

40 minutos atrás, KXSY disse:

O problema é que o seu codeblocks está configurado para compilar para C++ é não para C, hoje em dia todo compilador de C++ consegue compilar C, mas você teria que configurar o compilador para isso.

Realmente... Criei um novo projeto configurado para compilar em C e deu certo.

 

40 minutos atrás, KXSY disse:

Eu acho que você ainda não entendeu muito bem as expressões em C

Bom, sizeof() é usado para obter o tamanho dos tipos ou variáveis em bytes, e retorna um tipo inteiro sem sinal.

Pelo visto, em um compilador de C, o casting é automático para esse caso também... sizeof() tem retorno em size_t, então casting não tem efeito.

 

Obrigado pela ajuda :D

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

minicurso-montagem-popup.jpg

MINICURSO GRÁTIS!

Como ganhar dinheiro montando computadores!

CLIQUE AQUI E INSCREVA-SE AGORA MESMO!