Programando em C: Boas Práticas
Este artigo aborda algumas dicas que visam melhorar a legibilidade do código fonte.
Em 1972 o funcionário da AT&T Dennis Ritchie iniciou o
desenvolvimento de uma linguagem de programação que simplificasse a sua
tarefa diária de programador nos laboratórios Bell. Chamou-a
simplesmente de "C", em referência clara à uma linguagem anterior,
escrita por Ken Thompson, conhecida como "B". Após estar madura o
suficiente para substituir o assembly no kernel do sistema Unix em
1973, a linguagem ganhou o mundo, evoluindo com o tempo e tornando-se
referência, mas sem perder as principais características que a fizeram
ocupar uma posição de destaque.
Este artigo pretende conversar diretamente com o programador, revelando
algumas boas práticas que podem auxiliá-los no desenvolvimento e
manutenção do código. Apesar de genérico à linguagem C, os tópicos
abordados são frutos da experiência como desenvolvedor de sistemas
embarcados, sendo estes o nosso foco principal.
Mantendo Bonito
A linguagem C, por definição, não exige que você escreva o programa
seguindo regras que o tornem esteticamente agradável de ver (e ler).
Nada impede você de escrever algo como:
/*#define ppp xp
* /## define */#define x =
#define zim void
/*\**\*\***//*/ #denife $/*/
#define p ()
#define iu for(
/*\**\*\***//*/ #defie $##def x^3printdf/*/
#define xyz <
/*/* 8/ *******/ #define o ;}
#define v "\na l o m u n d o "
int s (zim) { char b x 1; iu b x 9;\
b xyz 21; b++){ printf( v ) o /*}}}*/}
zim main( zim ) { s p o
Exceto, claro, o bom senso. De uma maneira geral, o código bonito e
organizado é sempre mais fácil de ler.
Identação & Espaços
Utilizar a identação no código é algo essencial para a legibilidade.
O tamanho do espaçamento pode variar, mas o importante é sempre manter
blocos aninhados respeitando os níveis de identação das estruturas
condicionais e laços de repetição. De uma forma geral, o nível de
identação muda sempre após o início de uma função, estrutura
condicional ou laço:
void exemplo ( void ) {
if ( a > 5 ) {
b += 3;
for ( i=0; i<5; i++ )
{
z++;
x--;
}
}
}
No exemplo acima foram utilizados quatro espaços entres os blocos. Você
pode configurar o seu editor de texto preferido para que a tecla TAB
substitua o caracter invisível de tabulação por estes espaços, é
bastante útil. Evite espaçamentos muito curtos, como apenas um espaço,
ou muito longos.
Além da identação, que você já deve estar habituado, outro recurso que
facilita a legibilidade é a utilização de espaços horizontais e
verticais. Aumentar o número de linhas e espaços não vai, obviamente,
aumentar o tamanho do binário compilado. Ao invés de escrever uma
sentença condicional como:
if (a<20 &&(c==5||d==6)) { }
prefira algo assim:
if ( (a < 20) && ( (c==5) ||
(d==6) ) ) { }
que é visivelmente mais agradável de avaliar. Separar as sentenças por
parênteses também evita algum erro de precedência que você possa
cometer ao digitar as expressões, reduzindo o tempo de depuração no
futuro.
Laços complexos também podem ser quebrados em várias linhas, com o
mesmo objetivo:
for ( cont = 0, aux = 3;
cont < 100;
cont++, aux+=2 ) {
a = c + b;
d = e - f;
}
Quando falo em legibilidade do código, lembro-me que existe um operador
na linguagem C, o ternário, que utilizado em excesso pode tornar as
expressões pouco legíveis aos olhos menos treinados. De uma forma geral
eu evito utilizar o operador ternário, mesmo em sentenças simples. A
operação
aux = ( cont < aux ) ? cont : cont * -1;
poderia ser escrita, sem nenhum prejuízo, como:
if ( cont < aux )
aux = cont;
else
aux = cont * -1;
Mas se você gosta do operador ternário, uma opção para melhorar a
legibilidade é separar as sentenças por linhas identadas. A mesma
expressão poderia ser escrita assim:
aux = ( cont < aux )
? cont
:
cont * -1;
Isto será especialmente útil caso você utilize o operador ternário de
forma encadeada. Este código:
a = ( b < z )
? x
: ( i > x )
?
i
: ++i;
é certamente mais legível que este:
a = ( b < z ) ? x : ( i > x ) ? i : ++i;
- ++ e --
Evite agregar mais de um ++ ou -- na mesma sentença. Com o mesmo
intuito, evite atribuir ao resultado da operação operadores que
utilizaram o incremento ou decremento na expressão.
Portando, expressões como:
z = a++ + ++b;
ou
a[b] = b++;
devem ser evitadas e substituidas por cláusulas que inibam, ou pelo
menos minimizem, interpretações errôneas.
Números Mágicos
As constantes são suas amigas, e os "#define" uma das mais ricas
formas de melhorar a legibilidade do código. "Números Mágicos" são
valores, inteiros ou não, que aparecem no código e que são constantes,
mas não foram declarados como tais. Fazer isso prejudica a organização
e dificulta a manutenção.
Ao invés de ter condições como:
while ( tensao_bateria < 30 ) {
if ( potencia_transmissor
> 50 )
diminua_potencia();
else {
desliga_radio();
while ( tensao_bateria < 30 ) { /* espera bateria carregar */
}
liga_radio();
}
}
substitua os inteiros por constantes com nomes adequados, como
LIMITE_BATERIA_CRITICA e POTENCIA_MINIMA_TRANSMISSOR. Além de melhorar
a organização facilita a manutenção, em caso de alterações nestas
constantes.
Loops "Parados"
Utilizando o exemplo anterior, observe que escrevemos
while ( tensao_bateria < 30 ) { /* espera bateria carregar */
}
ao invés de simplesmente
while ( tensao_bateria < 30 );
A primeira forma deixa explícita a nossa intenção, ao contrário da
segunda, que poderia ser confundida com um erro do programador ou
causar problemas posteriormente, caso o ";" fosse erroneamente removido
(possivelmente sem erro na compilação).
Convenções
Não importa exatamente quais convenções você utiliza, o importante é
que você defina uma (própria ou não) e siga-a da forma mais rigorosa
possível. Preferencialmente estas convenções devem ser descritas em um
documento simples, que permita consultas rápidas sempre que necessário.
Exemplificarei algumas abaixo que são bastante populares, mas você deve
adaptá-las às suas necessidades.
- Constantes: para as constantes utilize sempre letras maiúsculas.
Desta forma será fácil distingui-la entre as variáveis.
Ex.:
#define LIMITE_TEMPERATURA 40
#define BATERIA_BAIXA 30
- Variáveis: mais importante que as convenções, uma regra básica:
utilize nomes claros para as variáveis, abreviando apenas o que é
bastante conhecido ou óbvio. Programadores são, em sua maioria, ágeis
no teclado. Portanto, abreviar pressaoMotorEsquerdo para preMotE
provavemente não vai economizar muito do seu tempo durante o
desenvolvimento e certamente dificultará a leitura do código.
Existem algumas convenções mais antigas como a Notação Húngara ou mais modernas, como a Notação
Camelo, muito utilizada na programação orientada a objetos.
Usualmente, em C, é bastante comum todas as variáveis serem escritas
com letras minúsculas e/ou com o "_" como separador de palavras. Sendo
assim, variáveis como velocidademaxima ou velocidade_maxima são
bastante comuns, sendo esta última forma mais freqüente.
- Tipos de Variáveis: uma das grandes vantagens da linguagem C é a
portabilidade entre arquiteturas. No entanto, alguns tipos de variáveis
possuem comprimento variável dependendo da arquitetura e compilador, o
que pode ocasionar problemas no software, como overflow ou erros de
conversões. Por exemplo, o tipo inteiro (int) normalmente representa o
tamanho da palavra do processador, nos processadores modernos de 32 e
64 bit e compiladores como o GCC. Mas isso não é necessariamente uma
regra; no compilador HITECH para microcontroladores PIC de 8 bit, por
exemplo, o inteiro possui 16 bit.
Sendo assim, defina tipos claros e utilize-os, como uint8 para inteiros
não sinalizados de 8 bit ou int16 para inteiros sinalizados de 16
bit.
Também seja atencioso ao declarar ponteiros. Uma declaração como:
char* s, t, u;
não está errada, mas provavelmente não é o que você deseja, já que t e
u não serão declarados como ponteiros. Sendo assim, prefira esta forma
para declarar os ponteiros sem margem para erros:
char *s, *t, *u;
Conclusão
Nossa intenção neste artigo foi abordar algumas técnicas que podem
ajudá-lo a melhorar a legibilidade do seu código. Apesar de existirem
vários outros pontos que podem ser abordados neste tópico, de uma forma
geral, escrevendo um código organizado, bonito e bem comentado existe
uma grande probabilidade do seu código tornar-se bastante legível,
quesito que traduz-se em agilidade e produtividade em todas as fases do
ciclo de vida do software.
Roberto Alcântara
roberto@eletronica.org
Obrigado
Abraços

Legibilidade melhorada