1. 程式人生 > >使用cppcheck檢測程式碼警告、錯誤

使用cppcheck檢測程式碼警告、錯誤

cppcheck是一個C/C++靜態檢查工具。它可以幫助我們檢測出程式碼存在(潛在)的問題,比如陣列越界、記憶體申請未釋放、檔案開啟未關閉。注意,cppcheck不是編譯器,替代不了gcc。

在ubuntu下安裝cppcheck十分簡單,命令如下:

[email protected]:latelee# apt-get install cppcheck

下面程式碼片段包含了陣列越界、記憶體申請未釋放、檔案開啟未關閉等錯誤。由於是演示用,不必太在意程式碼細節。

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

void init_buffer(void)
{
    char filename[128] = {""}; // 這樣初始為0,如果是{"1"},則只有第1個位元組為1,其它為0 --不知其它編譯器會怎樣
    
    printf("test of buffer\n");

    dump(filename, 128);
    
    char unused_buffer[7*1024*1024] = {0};   // 沒有使用的緩衝區,超過棧最大值,有coredump。
    char unused_buffer1[1*1024*1024] = {0};
    
    strcpy(unused_buffer1, "hello");
}

// 陣列範圍越界
void out_of_array(void)
{
    int foo[2]; // 範圍不夠
    int aaa = 250;
    
    foo[0] = 1;
    foo[1] = 2;
    // 下面這些是不行的
    foo[2] = 3;
    foo[3] = 4;
    foo[4] = 5;
    foo[5] = 6;
    
    printf("%d %d \n", foo[0], foo[1]);
}

#include <sys/types.h>
#include <dirent.h>
// 開啟未關閉
void open_not_close()
{
    // 記憶體洩漏
    char* p = new char[100];
    strcpy(p, "hello");
    printf("p:%s\n", p);
    

    FILE* fp = NULL;
    fp = fopen("aaa", "a");
    
    if (fp)
    {
        // 注:這裡返回時沒有關閉檔案
        return;
    }
    
    fclose(fp);
    

    DIR *dir = NULL;
    dir = opendir("./");
    
}

int main(void)
{
    int ret = 0;

    foo();

    init_buffer();
    out_of_array();
    open_not_close()
    return 0;
}

注意,在open_not_close函式中的return前並沒有關閉fp檔案指標。這種錯誤特別容易發生,幸運的是,cppcheck可以檢查出來。下面是是檢測結果:

[email protected]~/test# cppcheck gcc_warning.cpp  --enable=all
Checking gcc_warning.cpp...
[gcc_warning.cpp:56] -> [gcc_warning.cpp:50]: (warning) Possible null pointer dereference: fp - otherwise it is redundant to check it against null.
[gcc_warning.cpp:13]: (style) Variable 'unused_buffer' is assigned a value that is never used.
[gcc_warning.cpp:23]: (style) Variable 'aaa' is assigned a value that is never used.
[gcc_warning.cpp:59]: (style) Variable 'dir' is assigned a value that is never used.
[gcc_warning.cpp:65]: (style) Variable 'ret' is assigned a value that is never used.
[gcc_warning.cpp:28]: (error) Array 'foo[2]' accessed at index 2, which is out of bounds.
[gcc_warning.cpp:29]: (error) Array 'foo[2]' accessed at index 3, which is out of bounds.
[gcc_warning.cpp:30]: (error) Array 'foo[2]' accessed at index 4, which is out of bounds.
[gcc_warning.cpp:31]: (error) Array 'foo[2]' accessed at index 5, which is out of bounds.
[gcc_warning.cpp:53]: (error) Memory leak: p
[gcc_warning.cpp:53]: (error) Resource leak: fp
[gcc_warning.cpp:61]: (error) Resource leak: dir
Checking usage of global functions..
(information) Cppcheck cannot find all the include files (use --check-config for details)

從上述資訊中可以清晰地看到哪些程式碼存在問題,原因也一一給出。根據提示去修改程式碼即可。

注1:只要是人寫的程式碼,都有可能存在這種那種問題。程式碼問題關鍵因素還是人,但可以藉助工具幫助我們減少錯誤。——至少,如果在某個角落中陣列越界了,cppcheck能檢測出來。

注2:上述程式碼無法通過g++編譯,因為有很多錯誤。但cppcheck不是編譯器,所以無法檢測出來。

注3:cppcheck不是萬能的,比如在一個函式申請二級記憶體,在另一個函式不釋放,此情況有記憶體洩漏,但cppcheck檢測不出來。

另外也可以參考筆者的文章:

李遲 2016.6.15 週三 晚