Analisando código-fonte C/C++ com a ferramenta Cppcheck

- por Sergio Prado

Categorias: Linguagem C Tags: ,

Cppcheck é uma ferramenta de análise estática de código para aplicações escritas em C/C++ (se você nunca teve contato com uma ferramenta de análise estática de código, dê uma olhada no artigo Análise estática de código que escrevi algum tempo atrás).

O objetivo principal da ferramenta é encontrar erros que normalmente não são identificados pelo compilador, incluindo:

  • Dereferenciamento de um ponteiro não inicializado.
  • Acesso fora dos limites de um vetor.
  • Utilização de variáveis não inicializadas.
  • Vazamento de recursos (arquivos abertos mas não fechados, memória alocada mas não desalocada, etc).
  • Avisos sobre código não utilizado ou duplicado.

É um projeto de código-aberto, no momento hospedado no Sourceforge, com suporte aos sistemas operacionais GNU/Linux, Windows e Mac.

INSTALANDO

No Linux, o Cppcheck pode ser instalado através da ferramenta de gerenciamento de pacotes da sua distribuição. Por exemplo, para o Ubuntu 14.04 basta executar o comando abaixo:

$ sudo apt-get install cppcheck

A única desvantagem de utilizar a versão empacotada pela distribuição é o fato da versão disponibilizada ser mais antiga. No momento em que escrevo este artigo, a versão mais atual do Cppcheck é a 1.70, porém nos repositórios oficiais do Ubuntu está disponível apenas a versão 1.61.

Portanto, para utilizar a versão mais atual da ferramenta, basta baixar e compilar o código-fonte:

$ git clone git://github.com/danmar/cppcheck.git
$ cd cppcheck/
$ git checkout 1.70
$ make CFGDIR=/etc/cppcheck/ HAVE_RULES=yes
$ sudo CFGDIR=/etc/cppcheck/ make install

Após a compilação, a versão instalada pode ser verificada com o comando abaixo:

$ cppcheck --version
Cppcheck 1.70

TESTANDO

Vamos agora testar a ferramenta.

O código-fonte abaixo possui uma função para calcular a soma de todos os elementos de um vetor de inteiros. E ele tem dois problemas que não são identificados pela maioria dos compiladores, incluindo o GCC. Você consegue identificá-los?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void calc(void)
{
    int buf[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int result;
    int i;
 
    for (i = 0; i <= 10; i++) {
        result += buf[i];
    }
}
 
int main(void)
{
    calc();
    return 0;
}

Conseguiu? Aposto que o Cppcheck consegue identificar os problemas mais rapidamente! :)

$ cppcheck test.c
Checking test.c...
[test.c:8]: (error) Array 'buf[10]' accessed at index 10, which is out of bounds.
[test.c:8]: (error) Uninitialized variable: result

O primeiro erro está relacionado ao acesso fora dos limites do vetor buf, e o segundo erro está relacionado ao uso da variável result antes de inicializá-la.

O pior é que o GCC compila o código sem nenhuma mensagem de erro!

$ gcc -Wall -Wextra -Werror -Wpedantic test.c -o test

Por padrão, o Cppcheck irá procurar apenas por erros. Checagens adicionais podem ser habilitadas através do parâmetro enable, incluindo mensagens de aviso, estilo de codificação, problemas de performance e portabilidade (mais informações sobre estas checagens na página de manual do Cppcheck).

$ man cppcheck

O parâmetro enable=all permite realizar todas as checagens existentes:

$ cppcheck --enable=all test.c 
Checking test.c...
[test.c:8]: (style) Variable 'result' is assigned a value that is never used.
[test.c:8]: (error) Array 'buf[10]' accessed at index 10, which is out of bounds.
[test.c:8]: (error) Uninitialized variable: result

Veja que ele realizou um teste adicional de estilo de codificação, indicando que a variável result nunca é utilizada pela aplicação.

À propósito, o parâmetro doc permite exibir todas as checagens que podem ser realizadas pelo Cppcheck:

$ cppcheck --doc

BUSYBOX

Para testar a ferramenta em uma outra aplicação, decidi executá-la em um projeto de médio porte. Baixei a versão 1.23.2 do Busybox e rodei o Cppcheck para analisar seu código-fonte:

$ cppcheck . >busybox.log 2>&1

Foram analisados no total 723 arquivos em pouco mais de dois minutos, com 74 erros identificados pela ferramenta.

$ cat busybox.log | grep "(error)"
[applets/applet_tables.c:171]: (error) Resource leak: fp
[archival/libarchive/decompress_bunzip2.c:162]: (error) Uninitialized variable: runCnt
[coreutils/dos2unix.c:45]: (error) Uninitialized variable: temp_fn
[coreutils/dos2unix.c:46]: (error) Uninitialized variable: resolved_fn
[coreutils/expr.c:326]: (error) Uninitialized variable: l
[coreutils/expr.c:327]: (error) Uninitialized variable: v
[coreutils/test.c:633]: (error) Uninitialized variable: i
[editors/awk.c:2500]: (error) Uninitialized variable: L_d
...

Analisando algumas das mensagens de erro, encontrei alguns falso-positivos, mas também alguns problemas que precisam ser corrigidos ou trechos de código que podem ser refatorados.

ESTENDENDO O CPPCHECK

O Cppcheck pode ser estendido criando-se novas regras de checagem com expressões regulares ou mesmo através de módulos escritos em Python.

Existem também plugins do Cppcheck para diversas ferramentas de desenvolvimento populares como o Eclipse, Visual Studio, Code::Blocks, Sublime Text e QtCreator.

cppcheck

Além é claro do suporte ao Vim através do plugin Syntastic! :)

syntastic

O manual da ferramenta está disponível online no site do projeto ou em formato PDF.

Conclusão? O Cppcheck é uma ferramenta extremamente útil e simples de usar. Integre-o ao sistema de build da sua aplicação e utilize-o em todos os seus projetos desenvolvidos em C/C++. Esta ferramenta não vai resolver todos os seus problemas, mas com certeza irá auxiliar na melhoria da qualidade do código-fonte da sua aplicação, identificar alguns problemas na sua origem e diminuir o tempo que você passa corrigindo bugs.

Happy coding!

Sergio Prado

Faça um Comentário

Navegue
Creative Commons Este trabalho de Sergio Prado é licenciado pelo
Creative Commons BY-NC-SA 3.0.