- Introdução
1.1 English material - Estilo de Código
2.1. Nomes de Variáveis
2.2. Nomes de Constantes
2.3. Nomes de Funções
2.4. Comentários
2.5. Indentação
2.6. Não utilize números mágicos
2.7. Inclua guards
2.8. Sempre utilize chaves
2.9. Mantenha as linhas com um comprimento razoável
2.10. Utilize aspas duplas para incluir arquivos locais
2.11. Utilize double em vez de float - Dicas
3.1. Lembre-se de liberar a memória dos ponteiros e verificar o seu retorno
3.2. Códigos não utilizados devem ser deletados
3.3. Evite funções com muitos parâmetros
3.4. Utilize espaços em branco para melhor visualização
3.5. Limite o escopo das variáveis
3.6. Sempre use typedef nas structs mais comuns
3.7. Pare e dê uma volta - Referências
Este documento apresenta um guia rápido de boas práticas em C (baseado no guia do mesmo repositório sobre C++, mas enxugando-o apenas para as práticas relevantes em C e suas funcionalidades) e é voltado para iniciantes na linguagem mas também pode ser útil para desenvolvedores intermediários.
As informações apresentadas neste guia foram obtidas de alguns materiais (livros e blogs), citados nas referências, e de conhecimento prático pessoal.
Achei importante criar este material em PT_BR pois a maioria dos materiais encontrados estão escritos na língua do Tio Sam, o que dificulta um pouco o entendimento, principalmente para os iniciantes. Além disso, tentei resumir de forma bem prática e direta alguns conceitos básicos de forma que este material possa ser utilizado como um guia de consulta rápida. (@Kelvins)
De maneira semelhante, adaptei o guia do @Kelvin sobre boas práticas em C++ para um guia específico de C, com base em conhecimento prático pessoal e no guia de estilos do curso CS50 (e claro, as próprias aulas do CS50x). (@JoaoVitorDio)
Caso você não concorde com algo ou tenha alguma informação a acrescentar, sinta-se à vontade para criar issues ou enviar pull requests.
For those who aren't able to read portuguese documents (welcome too!), I strongly recommend you to visit https://cs50.readthedocs.io/style/c/. Great part of this document was somehow inspired by that, so, feel free (and suggested) to go and read it by yourself! The purpoise of doing a Brazilian Portuguese version guide is that are few translated guides, which can be a problem for a beginner. Not everything in the guide are about style, therefore, try to search for Using valgrind to find memory leak, Typedef use, Scope rules and some advice: Remember to take a break and go away from your code, sometimes.
Todo projeto possui seu estilo de código, alguns com algumas práticas mais avançadas e outros praticamente sem nenhum padrão. Porém, o estilo de um código tem grande impacto em sua respectiva legibilidade. Sendo assim, é importante investir algumas horas do seu tempo para estudar um pouco sobre isso, além de realizar revisões de código sempre que possível, garantindo um código mais fácil de manter e evoluir.
Variáveis devem sempre começar com letra minúscula, por exemplo:
Bom
char myWeirdVariable;
// ou
char my_weird_variable;
Ruim
char MyWeird_Variable2;
// ou
char My_weirdVariable_3;
Utilize um padrão já conhecido para a declaração das variáveis, como por exemplo:
Em C, diferentemente de em C++, o padrão mais adotado é o snake_case. Raramente se vê códigos utilizando CamelCase, e quando há, normalmente são reservados para structs ou typedefs. (Não é regra)
Entretanto, quando trabalhando em um projeto conjunto, é interessante usar sempre o mesmo padrão de nomenclatura já vigente, que provavelmente será um dos citados acima (se não for, talvez valha a pena sugerir uma padronização rs).
Constantes devem ser declaradas sempre em letras maiúsculas (caixa alta). Isso também vale para MACROS ("defines").
Bom
#define MAX 500
const double PI = 3.14159;
Ruim
#define max 500
const double pi = 3.14159;
Nomes de funções devem começar com a primeira letra minúscula, assim como as variáveis. Em C, as funções majoritariamente são escritas em snake_case:
Bom
void my_function();
Apenas diferente
void myFunction();
Ruim
void MyFunction();
Pior ainda
void My_Function();
Utilize //
para blocos de comentários (comentários de múltiplas linhas) dentro de funções, por exemplo:
int equal( int value1, int value2 )
{
// Compara dois valores e retorna
// verdadeiro se os valores são iguais
if( value1 == value2 )
{
return 1;
}
return 0;
}
Caso seja necessário comentar um bloco de código para debugar ou por algum outro motivo, você não terá problemas, como por exemplo:
Bom
int equal( int value1, int value2 )
{
/*
// Compara dois valores e retorna
// verdadeiro se os valores são iguais
if( value1 == value2 )
{
return 1;
}
*/
return 0;
}
Caso contrário, não seria possível comentar o bloco de código inteiro, por exemplo:
Ruim
int equal( int value1, int value2 )
{
/*
/*
* Compara dois valores e retorna
* verdadeiro se os valores são iguais
*/
if( value1 == value2 )
{
return 1;
}
*/
return 0;
}
Além disso, na minha opinião, quando é utilizado //
para comentários de múltiplas linhas o código parece ser mais legível do que quando se utiliza /* */
.
O mais comum é a indentação ou recuo de código utilizando 4 espaços, 2 espaços ou 1 tab. Isso pode mudar de projeto para projeto ou mesmo de acordo com a linguagem de programação. Eu pessoalmente costumo utilizar 4 espaços e acredito que este seja o padrão mais utilizado pelos desenvolvedores. É possível configurar a IDE ou o editor para utilizar por padrão o indentação desejada.
Pequeno ponto de atenção: O arquivo específico do makefile necessariamente depende de hard tabs (1 tab). A opção mais comum é configurar a IDE para soft tabs de 4 espaços, e alterar somente na escrita do makefile, ou usar um conversor de soft tabs para hard tab da internet, por exemplo, esse: Conversor de espaços para hard tabs
Não utilize números 'mágicos', por exemplo:
Ruim
double calc( double value )
{
return value * 3.14159;
}
Nestes casos opte por definir uma constante, por exemplo:
Bom
const double PI = 3.14159;
double calc( double value )
{
return value * PI;
}
Mas utilize, SIM, números, quando isso fizer sentido, por exemplo:
Bom
double calc( double value )
{
return value * 2;
}
Ruim
#define TWO 2
double calc( double value )
{
return value * TWO;
}
Arquivos de cabeçalho (header files) devem utilizar guards para evitar problemas com a inclusão do mesmo arquivo múltiplas vezes e previnir conflitos com cabeçalhos de outros projetos. Include_guards
Bom
#ifndef MYTAD_H
#define MYTAD_H
struct my_struct
{
int struct_infos;
};
#endif
Ruim
struct my_struct
{
int struct_infos;
};
Sempre utilize chaves, ainda que exista apenas uma linha de código dentro de um bloco. Há desenvolvedores que se utilizam das chaves na mesma linha da função/estrutura, e outros que a colocam na linha seguinte. Essa questão é pessoal e assim como citado na escolha entre CamelCase e snake_case, é bom usar o padrão já existente no projeto em que se trabalha. Se for um projeto unicamente seu e o seu estilo é o único do código, opte pelo que julgar mais confortável. Particularmente, uso em linhas separadas por ser uma leve recomendação do guia de estilos do CS50.
A não utilização de chaves pode causar erros semânticos no código, por exemplo:
Bom
int sum = 0;
for (int i = 0; i < 15; ++i)
{
++sum;
printf("%d", i);
}
Ruim
for (int i = 0; i < 15; ++i)
printf("%d", i);
Erro semântico
int sum = 0;
for (int i = 0; i < 15; ++i)
++sum;
printf("%d", i);
Mantenha as linhas com um comprimento razoável. Caso a linha seja muito extensa, tenha muitos caracteres, vale a pena quebrá-la em múltiplas linhas, por exemplo:
Ruim
if( (x == 1 && y == 2 && myFunction() == true) || (x == 0 && y == 0 && myFunction() == false) )
{
}
Bom
if( (x == 1 && y == 2 && myFunction() == true) ||
(x == 0 && y == 0 && myFunction() == false) )
{
}
Utilize aspas duplas (""
) para incluir arquivos locais.
Ruim
#include <stdio.h>
#include <my_header.h>
Bom
#include <stdio.h>
#include "my_header.h"
A utilização de float
irá reduzir a precisão. Porém, em operações com vetores float
pode ser mais rápido que double
se você puder sacrificar a precisão.
Contudo, double
é a opção padrão recomendada já que este é o tipo padrão para valores de ponto flutuante em C.
Nesta seção você irá encontrar algumas dicas importantes que podem ser úteis durante o desenvolvimento.
Lembre-se de sempre liberar a memória alocada e jamais faça um programa com vazamento de memória (memory leak). A linguagem C não possui garbage collector, tampouco ponteiros inteligentes como C++, e toda a responsabilidade está na sua mão como desenvolvedor de fazer um código eficiente, enxuto, funcional e otimizado (e por isso a linguagem é tão rápida e poderosa).
Lembrando: As funções de alocação e desalocação de memória heap fazem parte da biblioteca stdlib.h.
Recomendações adicionais: O uso da função assert() (que faz parte da biblioteca assert.h) substitui todo o código de verificação. Se a condição não for atendida, a função interromperá o fluxo do programa instantaneamente. Sistemas Unix possuem uma ferramenta de terminal (incluída nos pacotes de desenvolvedor, comumente) super útil para verificar programas com vazamento de memória e ajudar a identificá-los. Procure por Valgrind.
Bom
#include <stdlib.h>
int rows = 5, columns = 2;
int **matriz = (int **) calloc(rows, sizeof(int *));
if (matriz == NULL) // Verificacao manual:
{
return 1;
}
for (int i = 0; i < rows; i++)
{
matriz[i] = (int *) calloc(columns, sizeof(int));
assert(matriz[i] != NULL); // verificacao usando assert:
}
// Uso aleatório do array
// Liberando a memória:
for (int i = 0; i < rows; i++)
{
free(matriz[i]);
}
free(matriz);
Ruim
#include <stdlib.h>
int rows = 5, columns = 2;
int **matriz = (int **) calloc(rows, sizeof(int *));
for (int i = 0; i < rows; i++)
{
matriz[i] = (int *) calloc(columns, sizeof(int));
}
// Uso aleatório do array
return 0;
Sempre verifique seus programas com o Valgrind.
Códigos não mais utilizados (comentados) devem ser deletados, por exemplo:
Ruim
int equal( int value1, int value2 )
{
/*
if( value1 < value2 || value1 > value2 )
{
return 0;
}
else
{
return 1;
}
*/
// Compara dois valores e retorna
// verdadeiro se os valores são iguais
if( value1 == value2 )
{
return 1;
}
return 0;
}
Bom
int equal( int value1, int value2 )
{
// Compara dois valores e retorna
// verdadeiro se os valores são iguais
if ( value1 == value2 )
{
return 1;
}
return 0;
}
Assim o código fica mais limpo e mais fácil de compreender.
Sempre que possível, evite a utilização de muitos parâmetros em funções. Funções com muitos parâmetros são geralmente difíceis de compreender. Se necessário, refatore a função.
Ruim
void show_user_information(char *firstName, char *lastName, char *gender, int age, double height, double weight);
Bom
// Onde 'User' é uma estrutura de dados
void show_user_information(user *individual_user);
Não consigo expressar a ênfase que eu gostaria de dar para essa dica específica, é ultra relevante e ignorada por muitos .
Utilize espaços em branco para melhor visualização, por exemplo:
Bom
if ((majorVersion == 2 && minorVersion == 5) || majorVersion >= 3)
Ruim
if((majorVersion==2 && minorVersion==5) || majorVersion>=3)
Pior ainda
if((majorVersion==2&&minorVersion==5)||majorVersion>=3)
Não é bem um padrão, mas, nos cursos de ciência da computação de Harvard (em que me inspirei para adquirir o meu estilo de escrita de código e cujo material está linkado no fim desse README), os espaços são usados assim, de acordo com o tipo de ferramenta da linguagem:
Funções:
void my_function(int first_parameter, char second_parameter);
Estrutura condicional e estruturas de repetição:
if (random_variable == something)
{
}
for (int i = 0; i < something; i++)
{
}
Operações aritméticas e comando de atribuição:
int x = variable % random_value;
Uso de ponteiros de structs:
typedef struct
{
int struct_infos;
} my_struct_type;
my_struct_type variable, *p_my_pointer;
p_my_pointer = &variable;
p_my_pointer -> struct_infos = some value;
// Não é necessário dar esses pulos de linha
Sempre que possível, limite o escopo das variáveis. É útil em termos de otimização de espaço da memória, quanto de legibilidade e compreensão do código, visto que o uso das variáveis estará logo nas linhas abaixo:
Bom
// Declarando a variável de contagem dentro do laço
// e garantindo seu curto período de vida na memória
for (int i = 0; i < 15; ++i)
{
my_function(i);
}
Ruim
int i;
for (i = 0; i < 15; ++i)
{
my_function(i);
}
// i ainda está ocupando memória sem motivo, e um leitor pode se perder
// com o atual valor de i quando ele for usado posteriormente, caso seja.
Pelo bem da legibilidade do seu código, não congestione-o com inúmeras variáveis, parâmetros e quaisquer outros fins da sua estrutura com "struct nome_da_estrutura nome_da_variavel". Sempre utilize typedefs para as suas estruturas e sobretudo, nas mais comuns.
Ruim
struct carro
{
int ano;
char[TAM_PLACA] placa_do_carro;
};
struct carro carrinho;
void verifica_ano_do_carro(struct carro carrinho);
Bom
typedef struct carro
{
int ano;
char[TAM_PLACA] placa_do_carro;
} carro_t;
carro_t carrinho;
void verifica_ano_do_carro(carro_t carrinho);
Sempre que estiver empacado na solução de um problema, respire fundo e vá dar uma volta ou fazer alguma outra atividade por um certo período de tempo. Isso ajuda a esfriar um pouco a cabeça e pensar em uma solução mais claramente.
C++ Best Practices: https://www.gitbook.com/book/lefticus/cpp-best-practices/details
Harvard's CS50 C Style Guide: https://cs50.readthedocs.io/style/c/.
Google C++ Style Guide: https://google.github.io/styleguide/cppguide.html
10 most voted C++ best practices: http://codergears.com/Blog/?p=1957