Programando com uma granada de mão: uma visão da linguagem C

C é uma linguagem maravilhosa para se programar. Porém, alguns cuidados são necessários a fim de se evitarem dores de cabeça.

[ Hits: 37.365 ]

Por: Ronaldo Faria Lima em 08/06/2004


Os calcanhares de Aquiles



Apesar de ser flexível, C tem alguns "drawbacks" que não são muito explícitos. Um programa que abusa dos pontos fracos da linguagem tem o potencial de causar problemas, mas pode compilar sem nenhuma warning. Aqui estão as limitações do C:
  • Namespace extremamente sujo e nomes de identificadores.
  • Verificação relaxada de tipos de dados.
  • Ausência completa de verificação de limites.
  • Namespace sujo e nomes de identificadores.

Um espaço de nomes define um limite onde os nomes de identificadores podem ser declarados. Em C, o espaço de nomes é sempre global ou dentro do escopo de um bloco. Esta limitação traz um problema sério para aqueles que adoram usar variáveis globais. Pode acontecer que uma determinada variável global entre em conflito com outra que exista dentro de uma biblioteca, por exemplo, o que pode levar o programa a ter um comportamento completamente fora do esperado e sem uma causa clara mesmo em sessões de depuração. O efeito é que a variável muda seu conteúdo sem uma operação aparente.

O melhor é sempre evitar de criar variáveis globais. Se for imprescindível, tente prefixá-la com o nome do sistema, módulo, uma sigla, algo que você julgue que seja único. Variáveis globais, via de regra, denotam design ruim de um aplicativo. É totalmente possível viver sem elas. Apenas em alguns casos muito particulares é que se tem de lançar mão deste recurso, por não haver outra alternativa: tratamento de eventos assíncronos. Por exemplo, um handle de sinal que precisa salvar um status para uso posterior. Via de regra esse handle vai ter de lidar com alguma coisa que precisa ser persistido e que precisa ter seu status alterado de forma assíncrona, sem a intervenção direta do programa.

Um erro comum, também, está na forma como se define nomes. É comum ver programadores iniciando nomes com caractere sublinhado com a primeira letra seguinte maiúscula. O compilador não reclama desta prática mas o comitê ANSI-X3J11 convencionou que nomes que seguem esta linha são reservados ao implementador da biblioteca Standard C. Em outras palavras: evite de iniciar nomes com o caractere sublinhado tendo a segunda letra em maiúsculas.

Outra dica importante: a linguagem C suporta identificadores com, no máximo, 32 caracteres. Além dos 32 caracteres, o compilador trunca os nomes. Se dois nomes distintos tiverem os primeiros 32 caracteres iguais, o compilador irá tratá-los como o mesmo nome, o que pode gerar conflitos. Portanto, evite identificadores muito compridos.

Verificação relaxada de tipos de dados


O comitê ANSI tentou contornar este problema criando os protótipos de funções. Os protótipos de funções são uma forma de forçar o compilador a realizar uma checagem mais rígida dos parâmetros e tipos de retorno. Porém, o máximo que irá acontecer é o compilador lhe dar uma warning. É perfeitamente possível chamar uma função que espera receber um ponteiro para char com um número inteiro. Isso gera um executável que provavelmente fará alguma bobagem na memória.

Portanto, tome extremo cuidado com as atribuição dentro do seu código. Usar casts faz somente com que o compilador pare de reclamar. A melhor idéia é simplesmente verificar se os tipos são realmente compatíveis e usar do bom senso sempre.

Ausência de verificação de limites


Esta é uma característica dos vetores. Se o seu programa passar do fim de um vetor, passou. Vai acessar na memória sabe Deus lá o que. Tome extremo cuidado ao escrever rotinas que utilizem vetores, principalmente rotinas que trabalham com cadeias de caracteres. É muito comum em programas C esquecerem de que o fim de uma string precisa ter o caractere '\ 0'. Se o nosso querido '\ 0' não estiver dentro do vetor, significa que a string está aberta e um simples strcpy pode causar uma catástrofe no seu programa.

Página anterior     Próxima página

Páginas do artigo
   1. C: Uma linguagem flexível
   2. Os calcanhares de Aquiles
   3. Conclusão
Outros artigos deste autor

Como funcionam os alocadores de memória do STD C?

Escrevendo o caos em C

Leitura recomendada

Aprendendo a utilizar o GNU Debugger (parte 1)

Utilizando a biblioteca NCURSES - Parte II

Escrevendo o caos em C

Tutorial OpenGL v2.0

Túnel do Tempo: a função itoa()

  
Comentários
[1] Comentário enviado por rapatao em 08/06/2004 - 02:05h

Bom artigo... estou aprendendo C e isso vai ser muito util!

[2] Comentário enviado por y2h4ck em 08/06/2004 - 08:16h

Certos casos nem mesmo o {COMENTARIO} pode ajudar eheh. Como o caso de exploracoes avançadas em Memória Adjacente e smashing overflows... mesmo tendo controles na variavel pode-se explorar o programa do mesmo jeito...

Todo caso ta muito massa o Artigo.
[]s Boa sorte

[3] Comentário enviado por tiago.accon em 08/06/2004 - 11:40h

Perfeito artigo ! A linguagem C, por ser de baixo nível já fala por si própria. Ela é extremamente flexivel, mas para programadores que não são organizados é um perigo iminente. Parabens :] tiago.accon

[4] Comentário enviado por jllucca em 08/06/2004 - 14:03h

Acho a artigo excelente, mas ele fala da linguagem C. Mas, cita coisas de C++ tambem. Acho que todo mundo termina por misturar. Soh nao acho legal o pessoal dizer que ela eh "uma linguagem de baixo nivel que fala por si propria". Depois de muito debater uma pessoa cheguei a conclusao de que linguagens nao tem niveis e nem podemos falar em niveis sem citar parametros comparacao. Mas, isso eh uma picuinha minha mesmo ^^ .

[5] Comentário enviado por y2h4ck em 09/06/2004 - 09:19h

Baixo nivel para mim e ASM, frotran, cobol ... :P

C e nivel Medio ...

[6] Comentário enviado por taudujas em 09/06/2004 - 12:45h

O que necessita para começar a programar C?

[7] Comentário enviado por ron_lima em 09/06/2004 - 13:38h

Acredito que o pré-requisito para se programar em C é, antes de mais nada, procurar entender bem de algoritmos e estruturas de dados. Este pré-requisito é válido para todas as linguagens de programação, na verdade. A linguagem, propriamente dita, é simples (mas não fácil). C contém apenas 32 palavras reservadas (apesar de que algumas implementações reservam mais nomes). Penso que o segredo para o sucesso no entendimento da linguagem vem, antes de mais nada, de uma boa base de algoritmos e estruturas de dados. O restante é questão de ler um bom livro, como o excelente "A Linguagem C" de Brian Kernighan e Dennis Ritchie, os criadores da linguagem.

[8] Comentário enviado por jayro rodrigues em 10/06/2004 - 19:51h

Eu estou começando em C e percebi a flexibilidade da linguagem. Uma grande dificuldade que estou tendo é no projeto dos programas, ou seja, ALGORITIMO. É aconselhavel que se estude algorítimo primeiramente. VALEU !!!

[9] Comentário enviado por vvega em 21/06/2004 - 10:30h

Muito bom o artigo, mas um pouco confuso para quem eh iniciante, como ja dito aqui nos comentarios. Mas vale o alerta, pois programar em C nao eh la tao facil como alguns dizem, ainda que, tambem, nao seja tao complicado como dizem outros. Que tal ser fizessemos uma apostila (dai um prototipo de um livro) sobre a linguagem C baseada em experiencias de programadores ? Nao seria algo para ensinar o C, mas um documento para quem ja aprendeu o nivel basico (estruturas sequenciais, condicionais e de repeticao, vetores, matrizes, registro, ponteiros, pilhas e filas, por exemplo) e deseja se embrenhar no mundo mais avancado.

[10] Comentário enviado por genioloco em 18/10/2004 - 02:34h

Ótimo artigo, que serve como ponto de referência aos limites da linguagens C para quem está chegando agora ou para quem já conhece a linguagem.

[11] Comentário enviado por HackSpy em 10/11/2004 - 16:20h

Acho que a linguagem "C/C++" antes de tudo, deve ser uma linguagem planejada. Não se pode apenas ligar o micro e começar a digitar. Tudo que é feito em cima de analises e programado com competência, com certeza irá gerar algo altamente confiável e rápido. Agora, quando se programa amadoramente o risco é altissímo, um exemplo seria um erro de lógica cujo o compilador na acusa erro algum mas seu sistema complexo não funciona. PLANEJEM !!!!. Parabens pelo artigo.

[12] Comentário enviado por ron_lima em 11/11/2004 - 01:46h

Acredito que esta observação vale para toda e qualquer linguagem de programação. A implementação é apenas uma das fases do desenvolvimento do software e não é a fase principal. Em verdade a análise dos requisitos e o modelamento do problema é que ficam no ponto de destaque. Normalmente, sistemas bem analisados e modelados são aqueles que são entregues no prazo e sem uma quantidade infinita de erros... Sem dúvida, HackSpy, você está coberto de razão! Planejar antes de programar!

[13] Comentário enviado por nick_skyp em 16/01/2005 - 03:50h

Parabens pelo artigo,
...com certeza sem artigos como esse, mtos iniciantes em C, tenham q sofrer um poukinho e conhecer os topicos citados pelo seu artigo sentindo na pele esses tipos de erros... {experiência própria..hehehe :-D}
entao.. axei bastante feliz sua observação qto ao estudo da Standard C, qeria q vc me indicasse algum material sobre ela (apostila, tutorial se possivel em portugues)...
parabeins... teh +!

[14] Comentário enviado por voulogarso1vez em 02/03/2005 - 23:19h

Ih, faltou tanta coisa que poderia ser dita...
*Falta de manipulação de exceções (só setjmp/longjmp, difíceis de usar). Tratamento de erros fica uma mistura feita de códigos de retorno, errno, parâmetros de erro (passados por ponteiro) e setjmp/longjmp para os mais ousados.
*Nenhuma estrutura de dados que cresça automaticamente (não consigo imaginar fazer um programa que manipule muitas strings em C puro: ia ser um inferno, cheio de bugs, falhas de segurança e/ou limites arbitrários nos tamanhos das strings). Estruturas mais automáticas realmente abrem possibilidades de estilos de programação muito mais poderosos.
*Muitos casos de "comportamento indefinido", "dependentes da implementação" que dificultam portar o código.
*Biblioteca padrão extremamente limitada e com funções seriamente mal projetadas (gets é a pior, mas tem a sprintf, strtok, setlocale, strncpy, strcat, errno e todas as funções de entrada de dados são bastante limitadas).
*Se formos comparar com linguagens mais dinâmicas (Lua, Python, Lisp), aí sim C perde feio, sem reflexão, sem funções como valores de primeira classe, sem listas e passagem de parâmetros unificada, sem execução de código a partir de strings, sem hashtables, etc.

[15] Comentário enviado por ron_lima em 06/03/2005 - 07:53h

Setjmp e longjmp não foram projetados para serem tratadores de exceções.A idéia era fornecer à linguagem uma forma de realizar um goto que não fosse local a uma determinada função. Pelo que li no seu comentário, você compara a linguagem C com linguagens orientadas ao objeto, como o Python, e esta é uma comparação infeliz. C é uma linguagem procedural, antes de mais nada. C é uma linguagem de nível médio, ou seja, tem as características do assembly e características de linguagem de nível alto, como as que você citou. No entanto, pense no C como um melhoramento do assembly, ou seja, uma linguagem de programação que tem keywords ao invés de mnemônicos.

[16] Comentário enviado por Herr_Filip em 17/10/2005 - 12:05h

achei muito interessante a leitura, gostei da linha de pensamento da autora.
eu (pelo menos hoje em dia) nao programa em C e meu conhecimento em C é bem pobrezinho, entao começar a estudar a linguagem baseado em textos mais teóricos ajuda bastante! (apenas de que eu já tinha conhecimento de alguns "defeitos", ou... "detalhes" da linguagem que a Autora cita

muito bom, parabens!

[17] Comentário enviado por pirrola em 22/03/2008 - 12:43h

eu só sei C por enquanto, mas dizem que C é bem parecido com Java, que tvz seja a mais usada...

[18] Comentário enviado por ron_lima em 22/03/2008 - 19:42h

Na verdade, a sintaxe do java assemelha-se muito com a da linguagem C. Obviamente, a primeira diferencia-se pelo fato de ser orientada ao objeto, coisa não suportada pela linguagem C.

C é uma linguagem muito antiga, já com 38 anos de idade, ao passo que java popularizou-se no fim dos anos 90 no século passado. Cada linguagem tem seu nicho de uso. Por exemplo, você jamais escreveria um device driver em java devido às limitações impostas pela linguagem.

[19] Comentário enviado por canguru em 03/01/2010 - 19:32h

Ei...só para constar....a linguagem mais flexível do mundo é o assembly....nada é mais flexível q ele.....pois com ele vc se comunica direto com a maquina...se vc quer botar algo no video...vc tem q mandar uma interupção para a placa de video.....quer pegar algo do teclado..a mesma coisa.....

[20] Comentário enviado por ron_lima em 04/01/2010 - 06:57h

Desculpe por discordar, Canguru. Não confunda os recursos de uma linguagem com sua flexibilidade. Assembly é uma das linguagens mais inflexíveis que existem. A flexibilidade está ligada ao fato da linguagem permitir a descrição de estruturas e manipulações de dados de maneira fácil e eficaz e não com os recursos que a linguagem lhe dá. C++ é flexível, pois permite que você dê novos significados aos operadores, mas PHP, por exemplo, é ainda mais flexível, pois permite que expressões sejam criadas e avaliadas dinamicamente sem muito esforço de programação.


Contribuir com comentário




Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts