Cppcheck 1.85
2018-04-23
イントロダクション
Cppcheck は C/C++の静的解析ツールです。C/C++ コンパイラやその他の解析ツールとは異なり、シンタックスエラーを検出しません。 その代わりに、Cppcheckは、コンパイラが通常、検出に失敗するような種類のバグを検出します。このプロジェクトのゴールは、擬陽性 0 です。
サポートしているプログラムのソースコードとプラットフォーム:
さまざまなコンパイラの拡張構文や、インラインアセンブル等を含む、非標準的なソースコードをチェックできます。
Cppcheck は 最新のC++規格をサポートしている、あらゆるC++コンパイラでコンパイルできるようにしています。
Cppcheck は 十分なCPUパワーとメモリーのある、あらゆるプラットフォームで動作するようにしています。
Cppcheckに限界があることをご理解ください。Cppcheckの報告しているエラーに稀に間違いのあることがあります。また、Cppcheck が検出しないバグが残っていることもあります。
ソフトウェアを注意深くテストすれば、Cppcheckを使うより、より多くのバグを検出できるでしょう。ソフトウェアを注意深く実装すれば、Cppcheckを使うより、より多くのバグを検出できるでしょう。しかし、あなたのソフトウェアを実装するときやテストするときに見逃したバグのいくつかを Cppcheckが検出できるでしょう。
GUIでのはじめ方
GUIの起動
新しいプロジェクト(New Project)
新しプロジェクトのファイルの作成は必要ではありませんが、最初のステップに最適です。ファイル(File)と新しいプロジェクトファイル(New project file)を通じて学べます。
新しいプロジェクト(New Project) - パス(Paths)と定義(Defines)
あなたのプロジェクトはどのようなプロジェクトでしょうか。あなたのプロジェクトがVisual Studioのプロジェクトの場合、または(cmake/qbs/等の)コンパイルデータベースqが精製できる場合、あなたはプロジェクトをインポート(import)できます。
そうではない場合には、そのプロジェクトのパスと定義をマニュアルで調整します。次の図は、Visual Studio のプロジェクトファイルをインポートした場合のスクリーンショットです。
新しいプロジェクト(New Project) - プロジェクト(Project)
プロジェクトタブ(Project tab)では、ビルドディレクトリ(Cppcheck build dir)を設定しましょう。これはCppcheckが様々な分析する情報を保管するために使用します。プログラム全体の解析、インクリメンタル解析、統計などです。それぞれのプロジェクトは、それぞれのビルドディレクトリを持ちます。次のスクリーンショットはビルドディレクトリをcppcheck-build-dirと設定しています。このパスはプロジェクトファイルからの相対パスです。
あなたは、あなた使用する全てのライブラリーを選択すべきです。次のスクリーンショットではmicrosoft_sal と windowsライブラリーを選択しています。ライブラリーについてはこのマニュアルを参照してして下さい。
新しいプロジェクト(New Project) - アドオン(Addons)
ここでは 除外タブ(Exclude)と抑制タブ(Suppressions)をスキップします。これは結果をあとで微調整するために使います。
アドオンタブ(Addons)であなたは別の分析を追加できます。このアドオンにはpythonが必要です。
解析(Analyze)
ダイアログのOKボタンをクリックします。解析がすぐに始まります。
全ての警告が有効になり、やや賑やかになります。あなたが注意しない様々な警告があり得ます。これは簡単に修正できます。メッセージを右クリックして、隠す(Hide)または抑制( Suppress)を選びます。メッセージの隠しは永久ではありません。これは次の解析でまた表示されます。メッセージの抑制は、永久です。抑制されたidはプロジェクトファイルに保存されるので、これらは二度と表示されません。
コマンドラインでの始め方
最初のテスト
これは単純なソースコードです。
int main()
{
char a[10];
a[10] = 0;
return 0;
}
このソースコードをfile1.cに保存して次のコマンドを実行します。
cppcheck file1.c
cppcheck は次のように出力するでしょう。
Checking file1.c...
[file1.c:4]: (error) Array 'a[10]' index 10 out of bounds
フォルダ内の全てのファイルをチェックする
通常、プログラムは多くのソースファイルから構成されます。そして、それら全てをチェックしたいでしょう。Cppcheck は一つのディレクトリ以下の全てのソースファイルをチェックできます。
cppcheck path
ここで"path"はディレクトリのパスです。このようにすれば cppcheck はディレクトリ以下の全てのファイルを再帰的にチェックします。
Checking path/file1.cpp...
1/2 files checked 50% done
Checking path/file2.cpp...
2/2 files checked 100% done
マニュアルでファイルをチェックまたはプロジェクトファイルの使用
Cppcheckでは、ファイルやパスを指定する事でファイルチェックを指定できます。一方ででプロジェクトファイル(cmake/visual studio)を使用できます。
プロジェクトファイルの使用は早急に始められます、というのもあなたが設定してする項目が少なくなるからです。
マニュアルでのファイルチェックは、解析をより細かく制御できます。
どちらのアプローチが良い結果になるかはわかりません。両方の方法を試して下さい。両方のアプローチを使用するとより多くののバグを見つけられる結果が得られるかもしれません。
以降の章でより詳細を説明します。
チェックからファイルやフォルダを除外する
ファイルやフォルダをチェック対象から除外する方法は二つあります。最初の方法は、あなたがチェックしたいファイルやフォルダだけをcppcheckに指定することです。
cppcheck src/a src/b
src/a と src/b 以下の全てのファイルだけをチェックします。
第二の方法は、-iオプションと共に除外したいファイルやフォルダを指定することです。次のコマンドではsrc/c以下のファイルをチェックしません。
cppcheck -isrc/c src
このオプションは現在--projectオプションと同時に使用できません。また、このオプションが有効なのは、インプットディレクトリが提供するされたときです。複数のディレクトリを無視するためには、-iを複数回使用します。次のコマンドではsrc/b と src/c 以下のファイルをチェックしません。
cppcheck -isrc/b -isrc/c
Severities(厳格度)
メッセージのseverities(厳格度)には次のものがあります。:
error(エラー)
バグが検出されたときに使用します。
warning(警告)
防衛的プログラミングでバグを避けるための提案です。
style
コードの可読性の向上に関連した、スタイル関連の指摘(未使用関数、冗長なコードなど)
performance
コードの高速化のための提案。これらの提案は、一般的な知識に基づいたものでしかありません。このメッセージの修正によって計測できるほど処理速度が向上するかどうかはわかりません。
portability
移植性についての警告。64 bit CPUへの移植性。コンパイラ依存(独自拡張)ソースコードについての警告など。
information
設定上の問題設定を変更している間だけ有効にすることをお勧めします。
メッセージの表示
デフォルトではerrorのメッセージだけを表示します。--enableを使用すると他のチェックを有効にできます。
# warning のメッセージを有効にします。
cppcheck --enable=warning file.c
# performanceのメッセージを有効にします。
cppcheck --enable=performance file.c
# informationのメッセージを有効にします。
cppcheck --enable=information file.c
# 歴史的な理由により --enable=style を指定すると warning, performance,
# portability と styleのメッセージを有効にします。古いxml形式を使用しているときには、これらの厳格度を"style"として報告されます。
cppcheck --enable=style file.c
# warning と performance のメッセージを有効にします。
cppcheck --enable=warning,performance file.c
# unusedFunction のチェックを有効にします。今回は --enable=styleでは有効にできない。
# というのは、これではライブラリではうまく動作しないからです。
cppcheck --enable=unusedFunction file.c
# 全てのメッセージを有効にします。
cppcheck --enable=all
--enable=unusedFunctionはプログラム全体をチェックするときにだけ有効にしてください。また、--enable=allもプログラム全体をチェックするときにだけ有効にしてください。というのは、unusedFunction チェックは、関数が呼び出されなかったときに警告するチェックだからです。関数呼び出しがチェック範囲にみつからなかったという可能性のノイズになります。
疑いのあるチェック
Cppcheckはデフォルトで解析に疑いのない場合にだけエラーメッセージを表示します。しかし、--inconclusiveオプションを使用すると、解析に疑いのある場合であってもエラーメッセージを表示します。
cppcheck --inconclusive path
これは、もちろん、実際に問題がないものに対しても、警告することになります。このオプションは、疑いのある警告を表示してもよい場合に限り、使用してください。
結果をファイルに保存
多くの場合、チェックの結果をファイルに保存したいと考えるでしょう。通常のシェルのリダイレクション機能を使って、エラー出力をファイルに保存することができます。
cppcheck file1.c 2> err.txt
マルチスレッドチェック
オプションの-j を使用してスレッド数を指定することができます。例えば、4スレッドを使ってフォルダ以下の全てのファイルをチェックする場合は次のように実行します。
cppcheck -j 4 path
このチェックでは未使用関数の検出(unusedFunction checking)は無効になることに注意してください。
プラットフォーム
あなたがターゲットとするプラットフォームの設定を使用すべきです。
デフォルトで、Cppcheckはネイティブのプラットフォームの設定を使用しますので、あなたのソースコードがローカルの環境でコンパイルし実行する場合には正常に動作するでしょう。
Cppcheck にはビルトインのプラットフォーム設定として、unixとwindowsをターゲットにしたものがあります。コマンドラインオプションの--platformを使ってプラットフォーム設定を指定できます。
XMLファイルで自身のプラットフォームにあった設定ファイルを作成することもできます。ここに例をあげます。:
<?xml version="1"?>
<platform>
<char_bit>8</char_bit>
<default-sign>signed</default-sign>
<sizeof>
<short>2</short>
<int>4</int>
<long>4</long>
<long-long>8</long-long>
<float>4</float>
<double>8</double>
<long-double>12</long-double>
<pointer>4</pointer>
<size_t>4</size_t>
<wchar_t>2</wchar_t>
</sizeof>
</platform>
Project(プロジェクト)
CMakeやVisual Studioを使っているとき、あなたは--projectを使ってプロジェクトを解析できます。
これでカンタンにチェックでき、結果も得られます。あなたに必要な設定項目はありません。しかしこれが最も良い結果を得る方法とは限りません。私たちは、このプロジェクトファイルを利用する方法と、--projectを利用しない方法を試してよいオプションを選ぶようにお勧めします。
CMake
Cppcheckはコンパイルデータベースを理解します。あなたはこれをCMakeで生成できます。
例:
$ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .
compile_commands.jsonファイルが現在のディレクトリに生成されます。
それからCppcheckをこのように実行します。:
$ cppcheck --project=compile_commands.json
Visual Studio
あなたは個々にのプロジェクトファイル(*.vcxproj)でCppcheckを実行できますし、ソルーション全体(*.sln)でも実行できます。
# run cppcheck on a whole solution
$ cppcheck --project=foobar.sln
# run cppcheck on a individual project
$ cppcheck --project=foobar.vcxproj
Visual Studio内でcppcheckを実行するための、Visual Studioプラグインもあります。
プリプロセッサの設定
あなたが --projectを使用した場合、Cppcheckはプロジェクトファイルからプリプロセッサーの設定を読み取ります。
そうでなければ、あなたはインクルードパスやディレクティブを設定したくなるでしょう。
ディレクティブ
ここに2つの設定があるファイルがあります(Aが定義された場合と定義されていない場合):
#ifdef A
x = y;
#else
x = z;
#endif
Cppcheckはデフォルトでプリプロセッサのデファインのコンパイルスイッチ設定の組み合わせを全てチェックします。(ただし、これらのうち #error を除く)そのため上のコードは、Aが定義された場合とAが定義されていない場合の両方を解析します。
これを変更するには -D を使います。また -D を使用した場合、cppcheckは与えられたコンパイルスイッチだけが有効でその他は設定されていないとしてチェックします。これは、コンパイラのように動作します。また、 --force や --max-configs を使用すると、コンパイルスイッチの組み合わせの上限を上書きしてチェックすることができます。
# 全てのコンパイルスイッチの組み合わせをチェックする。
cppcheck file.c
# Aのコンパイルスイッチが有効になっている場合の組み合わせをチェックする
cppcheck -DA file.c
# check all configurations when macro A is defined
cppcheck -DA --force file.c
また、もう一つのオプションに-U があります。これはシンボルのundefとなります。使用例:
cppcheck -UX file.c
これはXが定義されていないことを意味します。Cppcheck は Xが定義されている組み合わせをチェックしません。
インクルードパス指定
インクルードパスを追加するには-Iオプションに続けてパスを指定します。
Cppcheckのプリプロセッサは基本的に他のプリプロセッサと同様にインクルードを扱います。しかし、その他のプリプロセッサはヘッダファイルが見つからない場合に停止するのとは違って、cppcheckはただ単に、メッセージ情報を表示してソースコードの解析を続けます。
cppcheckは常にソースコード全体を確認する必要がないので、このような仕様になっています。実際に、全てのインクルードパスを与えないことを推奨しています。もちろん、クラスのメンバーの実装を確認した上でクラスの宣言をCppcheckでチェックするのは有用ではありますが、標準ライブラリのヘッダーをCppcheckに確認させるのは有用ではありません。というのは、チェックにかかる時間が長くなり、あまりよくない結果が表示されるからです。そのような場合、.cfg ファイル (後述します)によってcppcheckに関数や型の実装の情報を提供する方がよいでしょう。
XML出力
Cppcheckは出力をXML形式に変更できます。--xml オプションでフォーマットを指定します。
ファイルをチェックし、XML形式で出力するコマンドのサンプルです。:
cppcheck --xml file1.cpp
これが出力例です。:
<?xml version="1.0" encoding="UTF-8"?>
<results version="2">
<cppcheck version="1.66">
<errors>
<error id="someError" severity="error" msg="short error text"
verbose="long error text" inconclusive="true" cwe="312">
<location file0="file.c" file="file.h" line="1"/>
</error>
</errors>
</results>
<error> 要素
それぞれのエラーは<error>要素に記載されます。属性:
id
エラーのidこれは、妥当なシンボル名です。
severity
以下のいずれかです: error, warning, style, performance, portability, information
msg
短い形式のエラーメッセージ
verbose
長い形式のエラーメッセージ
inconclusive
この属性は、メッセージに疑いのある場合にのみ使用されます。
cwe
メッセージのCWE ID。この属性は、メッセージのCWE IDが判明している場合のみ使用されます。
<location>要素
エラーに関連する全ての位置情報は<location> 要素内にリストアップされます。主要な位置は、リストの最初の要素になります。
属性:
file
ファイル名相対パスまたは絶対パスのどちらかです。
file0
ソースファイルの名前(オプション)
line
数
info
オプションの、それぞれの位置につiての短い情報
出力の形式の変更
もし、テンプレートを使用して、出力の形式を変更することができます。
事前定義した出力フォーマット
Visual Studioに互換性のある形式が必要な場合には、--template=vsを使用します。
cppcheck --template=vs samples/arrayIndexOutOfBounds/bad.c
このオプションは出力形式を次のように変更します。:
Checking samples/arrayIndexOutOfBounds/bad.c ...
samples/arrayIndexOutOfBounds/bad.c(6): error: Array 'a[2]' accessed at index 2, which is out of bounds.
gccに互換性のある出力が必要な場合には、--template=gccを使用します。:
cppcheck --template=gcc samples/arrayIndexOutOfBounds/bad.c
このオプションは出力形式を次のように変更します。:
Checking samples/arrayIndexOutOfBounds/bad.c ...
samples/arrayIndexOutOfBounds/bad.c:6:6: warning: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds]
a[2] = 0;
^
ユーザー定義出力形式(1行)
自分で自身でパターンを作成できます。例えば古いgcc のよuな出力フォーマットで警告メッセージを出力してほしい場合次のように指定します。:
cppcheck --template="{file}:{line}: {severity}: {message}" samples/arrayIndexOutOfBounds/bad.c
このオプションは出力形式を次のように変更します。:
Checking samples/arrayIndexOutOfBounds/bad.c ...
samples/arrayIndexOutOfBounds/bad.c:6: error: Array 'a[2]' accessed at index 2, which is out of bounds.
コンマ区切りフォーマット:
cppcheck --template="{file},{line},{severity},{id},{message}" samples/arrayIndexOutOfBounds/bad.c
このオプションは出力形式を次のように変更します。:
Checking samples/arrayIndexOutOfBounds/bad.c ...
samples/arrayIndexOutOfBounds/bad.c,6,error,arrayIndexOutOfBounds,Array 'a[2]' accessed at index 2, which is out of bounds.
ユーザー定義出力形式(複数行)
多くの警告は、複数の位置を指定します。サンプルコード:
void f(int *p)
{
*p = 3; // line 3
}
int main()
{
int *p = 0; // line 8
f(p); // line 9
return 0;
}
3行目でヌルポインタのデリファレンスの可能性があります。Cppcheckは追加の位置情報を表示してその結論がどこから発生したかを示すことができます。そのためには、コマンドラインで--template と --template-locationの両方を使用する必要があります。
サンプルコマンド:
cppcheck --template="{file}:{line}: {severity}: {message}\n{code}" --template-location="{file}:{line}: note: {info}\n{code}" multiline.c
cppcheck は次のように出力します。
Checking multiline.c ...
multiline.c:3: warning: Possible null pointer dereference: p
*p = 3;
^
multiline.c:8: note: Assignment 'p=0', assigned value is 0
int *p = 0;
^
multiline.c:9: note: Calling function 'f', 1st argument 'p' value is 0
f(p);
^
multiline.c:3: note: Null pointer dereference
*p = 3;
^
この警告の最初の行は--template で指定したフォーマットです。
この警告の残りの行は--template-locationで指定したフォーマットです。
--templateで指定するフォーマット
--template では以下の要素が利用できます。:
{file}
ファイル名
{line}
行数
{column}
カラム番号
{callstack}
全ての位置。それぞれの位置は[{file}:{line}]のフォーマットで記載され、また->で位置を区切ります。例えば次のようになります。: [multiline.c:8] -> [multiline.c:9] -> [multiline.c:3]
{inconclusive:text}
警告が確定的でない場合のメッセージを表示します。このメッセージは含まれていない場合もある、任意のテキストです。サンプル: {inconclusive:inconclusive,}
{severity}
エラー/警告/スタイル/性能/移植性/情報
{message}
警告メッセージ
{id}
警告id
{code}
実際のコード
\t
タブ
\t
改行
\r
キャリッジリターン
--template-location で指定するフォーマット
--template-locationでは以下の要素が利用できます。:
{file}
ファイル名
{line}
行数
{column}
カラム番号
{info}
現在位置についての情報メッセージ
{code}
実際のコード
\t
タブ
\t
改行
\r
キャリッジリターン
Misra
CppcheckはMISRA C 2012 向けのチェッカのアドオンを持っています。
要求事項
必要なもの:
Python (2系 または 3系)
MISRA C 2012の PDFこのPDFはhttp://www.misra.org.ukで購入できます (15-20 ポンド)
MISRA テキストファイル
MISRAルールテキストの公開は禁止されています。そのためMISRAルールテキストはこのアドオンから直接利用できません。代わりにこのアドオンはテキストファイルからルールのテキストを読み込みます。MISRA PDFの ”Appendix A Summary of guidelines"のテキストをコピーペーストした場合、それがルールのテキストになります。
もしあなたがxpdfを持っているなら、テキストファイルはコマンドラインから簡単に生成できます。 (pdftotext はxpdfに含まれています。):
pdftotext misra-c-2012.pdf output.txt
この出力は100%完璧であるとは限りません。少し手直しする必要があることもあります。
その他のpdfからtextに変換するソフトでもうまくいくでしょう。
テキストファイルをマニュアルで作成してするには、MISRA PDFの Appendix A "Summary of guidelines" をコピーペーストします。フォーマット:
Appendix A Summary of guidelines
Rule 1.1
Rule text
Rule 1.2
Rule text
...
あなたが無効にしたいルールは、ルールテキストがなくても構いません。ルールテキストのないルールはアドオンによって抑制されます。
出力の抑制
ある種のエラーをフィルタリングしたい場合、出力を抑制することができます。
プレーンテキスト抑制
エラーの種類によって出力を抑制することができます。つぎのいずれかの形式で出力を抑制します。:
[error id]:[filename]:[line]
[error id]:[filename2]
[error id]
このerror id は抑制したいエラーのidです。このエラーのIDを簡単に調べるには、--xmlオプションをコマンドラインで与えます。そのXML出力から、idの文字列が取得できます。このエラーのIDに*を指定して全ての種類のメッセージを抑制することができます。(これは指定したファイルに限ることができます。)
またfilenameにはワイルドキャラクターである、* または ?を含めることができます。前者には全ての文字列にマッチし、後者は任意の一文字にマッチします。またWindowsを含む全てのOSで、パス区切りに"/" を使うことをお勧めします。
コマンドライン抑制
--suppress=のコマンドラインオプションを使用して、コマンドラインで抑制を指定することができます。例:
cppcheck --suppress=memleak:src/file1.cpp src/
ファイルで抑制リストを指定
また、抑制ファイルを作成することもできます。例:
// src/file1.cppのmemleak と exceptNew の エラーを抑制
memleak:src/file1.cpp
exceptNew:src/file1.cpp
// 全てのファイルのuninitvarエラーを抑制する。
uninitvar
空行やコメント行を抑制ファイルに記載することができます。
そして、この抑制ファイルは次のようにして使用します。:
cppcheck --suppressions-list=suppressions.txt src/
XML 抑制
XMLファイルで抑制を指定できます。サンプルファイル:
<?xml version="1.0"?>
<suppressions>
<suppression>
<id>uninitvar</id>
<fileName>src/file1.c</fileName>
<lineNumber>10</lineNumber>
<symbolName>var</symbolName>
</suppression>
</suppressions>
このXMLフォーマットは拡張可能であり、将来さらなる属性を加えるかもしれません。
インライン出力抑制
エラー出力の抑制をソースコード中に直接、コメントの形で記載することもできます。このコメントには特別なキーワードを含めて記載します。ただし、インライン出力を抑制するコメントをソースコードに追加すると、ソースコードの可読性が少し悪くなってしまうかもしれません。
このソースコードは通常エラメッセージを出力する例です。:
void f() {
char arr[5];
arr[10] = 0;
}
前のソースコードに対する出力は次のようになります。:
# cppcheck test.c
Checking test.c...
[test.c:3]: (error) Array 'arr[5]' index 10 out of bounds
このエラーメッセージを抑制するには次のようなコメントを追加します。:
void f() {
char arr[5];
// cppcheck-suppress arrayIndexOutOfBounds
arr[10] = 0;
}
これで、--inline-suppr オプションの準備ができました。次のようにcppcheckを起動するとエラーが抑制されます。:
cppcheck --inline-suppr test.c
特定のシンボルにのみ適用するインライン抑制を指定できます。:
// cppcheck-suppress arrayIndexOutOfBounds symbolName=arr
抑制のためにコメントを書きます。; や // を使って開始点を指定できます。
// cppcheck-suppress arrayIndexOutOfBounds ; some comment
// cppcheck-suppress arrayIndexOutOfBounds // some comment
ライブラリ設定
WinAPI, POSIX, gtk, Qtなど他の外部のライブラリを使用した場合、Cppcheckは外部の関数がどのようなものであるかがわかりません。Cppcheck はそのため、メモリリークやバッファオーバーフロー、ヌルポインタのデリファレンスの可能性といったさまざまな問題が検出できません。これを解決するには設定ファイル(.cfg file)を使用します。
Cppcheckはいくつかのライブラリ用の設定を持っています。これらは次のようにしてロードできます。cppcheckは C または C++言語用の標準ライブラリの設定 std.cfgはいつもロードします。ご注意ください。もしあなたが有名なライブラリの設定ファイルを作成した場合や更新した場合には、私達のサイトにアップロードしてくれると非常に助かります。
カスタム設定ファイル(.cfg file)の使用
あなたのプロジェクト専用の設定ファイルを作成し、使用することができます。そのためには、--check-library と--enable=information を使用して設定のためのヒントを入手します。
設定ファイルの編集に、Library Editorの使用をお勧めします、これはCppcheck GUIに含まれています。これはViewメニューで使用できます。すべての設定がこのマニュアルに載っていません。
この設定ファイル.cfgのフォーマットに質問がある場合、フォーラム(http://sourceforge.net/p/cppcheck/discussion/)で質問してください。
コマンドラインのcppcheck はカスタマイズした設定ファイル(.cfg files)を作業パスから読み込もうとします。作業パスはcppcheckを実行しているパスですでそこに設定ファイルがあると考えます。
GUIのcppcheckはプロジェクトのファイルパスから設定ファイルを読み込もうとします。カスタマイズした設定ファイル(.cfg file)は プロジェクトファイルの編集 ダイアログで確認できます。このタイアログを表示させるにはファイル メニューから開いてください。
メモリーリソースのリーク
Cppcheck はリークのチェックが調整できます。言い換えれば、あなたはメモリーやリソースを割り当てる関数またはその割り当てを回収する関数を指定できます。
alloc と dealloc
ここにサンプルのプログラムがあります。:
void test()
{
HPEN pen = CreatePen(PS_SOLID, 1, RGB(255,0,0));
}
上のコード例はリソースリークの欠陥があります。 - CreatePen() は WinAPI 関数でpenを作成します。しかし、Cppcheckは関数からの返り値が解放されていなければならないと仮定しません。そのためエラーメッセージは表示されません。:
# cppcheck pen1.c
Checking pen1.c...
もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。:
# cppcheck --library=windows.cfg pen1.c
Checking pen1.c...
[pen1.c:3]: (error) Resource leak: pen
これが最小限のwindows.cfg ファイルです:
<?xml version="1.0"?>
<def>
<resource>
<alloc>CreatePen</alloc>
<dealloc>DeleteObject</dealloc>
</resource>
</def>
このアロケート関数とデアロケート関数はグループにまとめられています。それぞれのグループは<resource> や <memory> タグ中で定義されており、その<dealloc>関数によって特定されます。これは、<dealloc>タグでオーバーラップしたグループはマージされます。
leak-ignore とuse
しばしば、割り当てられたポインタを関数に渡すことがあります。例:
void test()
{
char *p = malloc(100);
dostuff(p);
}
もし設定ファイルがなく、Cppcheckがdostuffの仕様を把握していなければ、Cppcheckはdostuffがメモリーについて配慮しており、メモリーリークは発生しないと仮定します。
dostuffがメモリーについて配慮せず、解放などを行なっていないことを指定するためには、leak-ignoreを<function> タグ中で使います。:
<?xml version="1.0"?>
<def>
<function name="dostuff">
<leak-ignore/>
<arg nr="1"/>
</function>
</def>
これとは逆にdostuffがメモリーについて配慮している場合には次のように設定します。:
<?xml version="1.0"?>
<def>
<memory>
<dealloc>free</dealloc>
<use>dostuff</use>
</memory>
</def>
なお、この<use>の設定は論理的に全く無意味です。この設定がない場合でも同じエラーが表示されます。これは--check-libraryのinformationメッセージを減らすために使用します。
関数の動作
関数の動作や関数の使用方法を指定するのに、<function>タグが使えます。関数は、その名前によって特定されます。この名前は、name 属性とその引数によって指定されます。この名前はコンマで区切られた関数名のリストです。名前空間やクラス中の関数の場合には、完全修飾名で指定されます。例: <function name="memcpy,std::memcpy">もしテンプレート関数がある場合、インスタンス化した名前を提供してします。<function name="dostuff<int>">.
関数引き数
関数がとる引数は、<arg>タグで指定できます。引数のそれぞれは、引数の順番(1始まり)をnr属性で示します。nr="any" は任意の引き数を表します。また、nr="variadic"は可変長引数を表します。オプション引数は、デフォルト値で指定します。: default="value". それぞれの引数に対する設定は、全ての引数に対する指定を上書きします。
非ブール
ここで誤った比較のあるサンプルプログラムがあります。:
void test()
{
if (MemCmp(buffer1, buffer2, 1024==0)) {}
}
Cppcheckは、この関数にブール値を渡してよいと仮定します。:
# cppcheck notbool.c
Checking notbool.c...
もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。:
# cppcheck --library=notbool.cfg notbool.c
Checking notbool.c...
[notbool.c:5]: (error) Invalid MemCmp() argument nr 3. 非ブール値が求められています。
ここで最小のnotbool.cfgを用意しました。
<?xml version="1.0"?>
<def>
<function name="MemCmp">
<arg nr="1"/>
<arg nr="2"/>
<arg nr="3">
<not-bool/>
</arg>
</function>
</def>
未初期化メモリ
ここにサンプルのプログラムがあります。:
void test()
{
char buffer1[1024];
char buffer2[1024];
CopyMemory(buffer1, buffer2, 1024);
}
このプログラムのバグは buffer2 が初期化されていないことです。CopyMemory 関数の第二引数は初期化されている必要があります。しかし、Cppcheckは関数に未初期化の変数を渡してもよいと仮定しています。:
# cppcheck uninit.c
Checking uninit.c...
もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。:
# cppcheck --library=windows.cfg uninit.c
Checking uninit.c...
[uninit.c:5]: (error) Uninitialized variable: buffer2
注意:これは、ポインタが示すメモリ領域が初期化されていなければならないことを意味しています。
これが最小限のwindows.cfgファイルです。:
<?xml version="1.0"?>
<def>
<function name="CopyMemory">
<arg nr="1"/>
<arg nr="2">
<not-uninit/>
</arg>
<arg nr="3"/>
</function>
</def>
ヌルポインタ
Cppcheckは、関数にヌルポインタを渡してもよいと仮定しています。ここにサンプルのプログラムがあります。:
void test()
{
CopyMemory(NULL, NULL, 1024);
}
MSDNの文書はこれが問題あるかないかを明らかにしていません。しかし、ここでは問題ありと仮定します。Cppcheck は関数にヌルポインタを渡してもよいと仮定していますので、エラーを出力しません。:
# cppcheck null.c
Checking null.c...
もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。:
cppcheck --library=windows.cfg null.c
Checking null.c...
[null.c:3]: (error) Null pointer dereference
注意:<not-uninit>は値について意味しています。初期化されていないメモリが関数に渡されています。
これが最小限のwindows.cfg ファイルです:
<?xml version="1.0"?>
<def>
<function name="CopyMemory">
<arg nr="1">
<not-null/>
</arg>
<arg nr="2"/>
<arg nr="3"/>
</function>
</def>
フォーマット文字列
フォーマット文字列を扱う関数を定義できます。例:
void test()
{
do_something("%i %i\n", 1024);
}
これについてもエラーは報告されません。:
# cppcheck formatstring.c
Checking formatstring.c...
引数がフォーマット文字列であることを出力する設定ファイルが作成できます。設定ファイルの例です。:
<?xml version="1.0"?>
<def>
<function name="do_something">
<formatstr type="printf"/>
<arg nr="1">
<formatstr/>
</arg>
</function>
</def>これで、Cppcheckはエラーを報告できるようになりました。:
cppcheck --library=test.cfg formatstring.c
Checking formatstring.c...
[formatstring.c:3]: (error) do_something format string requires 2 parameters but only 1 is given.
このフォーマット文字列のtype属性は次のどちらかになります。:
printf - printf のルールに従うフォーマット文字列
scanf - scanf のルールに従うフォーマット文字列
値の範囲
有効な値の範囲が定義できます。想像してください。:
void test()
{
do_something(1024);
}
これについてもエラーは報告されません。:
# cppcheck valuerange.c
Checking valuerange.c...
1024 が 範囲外の値であることを出力する設定ファイルが作成できます。設定ファイルの例です。:
<?xml version="1.0"?>
<def>
<function name="do_something">
<arg nr="1">
<valid>0:1023</valid>
</arg>
</function>
</def>これで、Cppcheckはエラーを報告できるようになりました。:
cppcheck --library=test.cfg range.c
Checking range.c...
[range.c:3]: (error) Invalid do_something() argument nr 1. この値は1024ですが、妥当な値は0から1023までです。
validの要素で次のような表現が利用できます。:
0,3,5 => 0, 3 それに 5 だけが有効な値です。
-10:20 => -10 から 20 までの値(両端含む)が有効な値です。
:0 => 0または0未満の値が有効な値です。
0: => 0または0以上の値が有効な値です。
0,2:32 => 0 または2から32までの値(両端含む)が有効な値です。
-1.5:5.6 => -1.5 から 5.6 までの値(両端含む)が有効な値です。
最小サイズ
いくつかの関数はバッファーを引数にとります。バッファの最小サイズを指定することができます。(要素数ではなくバイト数です。)想像してください。:
void test()
{
char str[5];
do_something(str,"12345");
}
これについてもエラーは報告されません。:
# cppcheck minsize.c
Checking minsize.c...
設定ファイルで、例えば、引数1のバッファのサイズが引数2の文字列長より大きくなればならないと警告するような設定ファイルを作成できます。例を挙げます。:
<?xml version="1.0"?>
<def>
<function name="do_something">
<arg nr="1">
<minsize type="strlen" arg="2"/>
</arg>
<arg nr="2"/>
</function>
</def>これで、Cppcheckはこのエラーを報告できるようになりました。:
cppcheck --library=1.cfg minsize.c
Checking minsize.c...
[minsize.c:4]: (error) Buffer is accessed out of bounds: str
minsizes はいくつかの種類があります。:
strlen
バッファーのサイズが、その他の引数の文字列長より大きくなければなりません。例: std.cfg のstrcpyの設定を参照してください。
argvalue
バッファーのサイズがその他の引数の値より大きくなればなりません。例: std.cfg のmemsetの設定を参照してください。
sizeof
バッファーのサイズがその他の引数のバッファーのサイズより大きくなればなりません。例:posix.cfgのmemccpyの設定をみてください。
mul
バッファーのサイズがその他の2つの引数の値の積より大きくなればなりません。典型的な使用例としては、一つの引数が構造体などの要素のサイズを指定し、もうひとつの引数が要素の個数を定義するような場合です。例: std.cfg のfreadの設定を参照してください
strz
これを指定すると、数が0終端文字列でなければならないということができます。
<?xml version="1.0"?>
<def>
<function name="do_something">
<arg nr="1">
<strz/>
</arg>
</function>
</def>
noreturn
Cppcheck はこの関数がいつも値を返すとは仮定していません。ここにサンプルのプログラムがあります。:
void test(int x)
{
int data, buffer[1024];
if (x == 1)
data = 123;
else
ZeroMemory(buffer, sizeof(buffer));
buffer[0] = data; // <- error: xが1でないとき初期化されていない
}
理屈の上では、ZeroMemoryがプログラムを終了させてもバグはありません。そのため Cppcheckはエラーを報告しません。:
# cppcheck noreturn.c
Checking noreturn.c...
しかし、--check-library と--enable=informationをつかうとエラーが出力されます。:
# cppcheck --check-library --enable=information noreturn.c
Checking noreturn.c...
[noreturn.c:7]: (information) --check-library: Function ZeroMemory() should have <noreturn> configuration
もし適切な windows.cfg が提供されていましたら、このバグは検出されます。:
# cppcheck --library=windows.cfg noreturn.c
Checking noreturn.c...
[noreturn.c:8]: (error) Uninitialized variable: data
これが最小限のwindows.cfg ファイルです:
<?xml version="1.0"?>
<def>
<function name="ZeroMemory">
<noreturn>false</noreturn>
<arg nr="1"/>
<arg nr="2"/>
</function>
</def>
use-retval
他になにも指定されていない限り、cppcheckは関数が返り値を無視していても問題ないと仮定します。:
bool test(const char* a, const char* b)
{
strcmp(a, b); // <- bug: strcmp の呼び出しは副作用を持ちませんが返り値を無視している。
return true;
}
strcmp が副作用を持つ場合、パラメータが関数に渡されている結果を無視しても問題はなく、このような仮定は正しいといえます。:
# cppcheck useretval.c
Checking useretval.c...
もし適切なlib.cfg が提供されていましたら、このバグは検出されます。:
# cppcheck --library=lib.cfg --enable=warning useretval.c
Checking useretval.c...
[useretval.c:3]: (warning) Return value of function strcmp() is not used.
これが最小限のlib.cfg ファイルです。:
<?xml version="1.0"?>
<def>
<function name="strcmp">
<use-retval/>
<arg nr="1"/>
<arg nr="2"/>
</function>
</def>
pure関数(pure)とconst関数
これらは、GCC関数属性のpureとconstに対応します。
pure関数は、値を返す以外の効果を持ちません。そしてその返り値はその関数の引数とグローバル変数によってのみ決まります。
const関数は、値を返す以外の効果を持ちません。そしてその返り値はその関数の引数によってのみ決まります。
ここにサンプルのプログラムがあります。:
void f(int x)
{
if (calculate(x) == 213) {
} else if (calculate(x) == 213) {
// 到達不能コード
}
}
もしcalculate() がconst関数であれば、calculate(x)は両方の条件で同じ値を返します。というのも、同じパラメータを引数にしているからです。
Cppcheck は通常、その結果が異なると仮定するため、Cppcheckはこのコード例に警告を出しません。:
# cppcheck const.c
Checking const.c...
もし適切なconst.cfg が提供されていましたら、このバグは検出されます。:
# cppcheck --enable=style --library=const const.c
Checking const.c...
[const.c:7]: (style) Expression is always false because 'else if' condition matches previous condition at line 5.
これが最小限のconst.cfg ファイルです。:
<?xml version="1.0"?>
<def>
<function name="calculate">
<const/>
<arg nr="1"/>
</function>
</def>
関数strcpyの設定例
標準関数のstrcpyのための適切な設定は次のようになる。:
<function name="strcpy">
<leak-ignore/>
<noreturn>false</noreturn>
<arg nr="1">
<not-null/>
</arg>
<arg nr="2">
<not-null/>
<not-uninit/>
<strz/>
</arg>
</function>
この<leak-ignore/> は、リークチェック中に関数呼び出しを無視するように、Cppcheckに伝えます。この関数は、割り当てられたメモリを解放しないことを意味しています。
この<noreturn> は、この関数が、返り値を返すかどうかをCppchecに伝えます。
この関数は第一引数にポインタを取ります。しかしこのポインタは、ヌルポインタであってはなりません。というのは<not-null>が使用されているからです。
この関数は第二引数にポインタを取ります。このポインタはヌルポインタであってはなりません。また、このポインタは初期化されたデータを指していなければなりません。<not-null> と <not-uninit> は正しく使用されています。さらにいえば、このポインタは0終端文字列(zero-terminated string)でなければなりません。そのため<strz>が使用されています。
define
ライブラリはマクロプリプロセッサのdefineを使用することができます。例:
<?xml version="1.0"?>
<def>
<define name="NULL_VALUE" value="0"/>
</def>
プリプロセッサの段階でソースコード中に "NULL_VALUE" が現れるごとに、"0"で置き換えます。
podtype
多くのソースコードで、プラットフォームに依存しない型をtypedefによって定義しています。"podtype"のタグによって、cppcheckがこれらをサポートするために必要な情報を提供できます。このような情報のない場合、cppcheckは次の例でみるような "uint16_t" 型を理解できません。
void test() {
uint16_t a;
}
そのため、未使用変数である、'a'が未使用であるとのメッセージが表示されません。
# cppcheck --enable=style unusedvar.cpp
Checking unusedvar.cpp...
もし uint16_t が以下のように定義されていた場合、結果にメッセージが反映されます。
<?xml version="1.0"?>
<def>
<podtype name="uint16_t" sign="u" size="2"/>
</def>
型のサイズはバイトサイズで指定します。符号の "sign" 属性は 符号ありの "s" か 符号無し "u" のどちらかです。これらの属性はオプションです。このライブラリを使用しますと、cppcheckはメッセージを表示できるようになります。
# cppcheck --library=lib.cfg --enable=style unusedvar.cpp
Checking unusedvar.cpp...
[unusedvar.cpp:2]: (style) Unused variable: a
コンテナ(container)
C++ ライブラリの多くや STL 自身は、非常によく似た機能性をもつコンテナを提供します。ライブラリによってその動作をcppcheckに伝えられます。それぞれのコンテナの設定にはユニークなIDが必要とします。コンテナの設定には、startPatternを加えることができます(オプション)。この startPatternはToken::Match パターンとendPattern に有効でなけばなりません。また、このendPatternはリンクしているトークンと比較されるものです。オブション属性の"inherits"は事前に定義されたコンテナのIDをとります。
<container>タグの内部で、<size>、<access>、<other>を選択して使用して関数を定義できます。これらのタグはそれぞれ、"resize" やその結果を与えるような動作を指定することができます。その例 "end-iterator"を示します。
次の例は、std::vectorの為の定義を示しています。std::vectorは"stdContainer"の定義に基づいていますが、ここには表示していません。:
<?xml version="1.0"?>
<def>
<container id="stdVector" startPattern="std :: vector <" inherits="stdContainer">
<size>
<function name="push_back" action="push"/>
<function name="pop_back" action="pop"/>
</size>
<access indexOperator="array-like">
<function name="at" yields="at_index"/>
<function name="front" yields="item"/>
<function name="back" yields="item"/>
</access>
</container>
</def>
ルール(Rules)
正規表現を使用して、ユーザーがルール(rule)を定義することができます。
これらのカスタムルールは、ソースコードを高度に分析した結果を使用することができません。しかしソースコード中の非常にシンプルなパターンについて簡単にルールを作成することができます。
ルールの作成を始めるには次の関連記事を参照してください。:
http://sourceforge.net/projects/cppcheck/files/Articles/
ルールのファイルフォーマットは次のとおりです。:
<?xml version="1.0"?>
<rule>
<tokenlist>LIST</tokenlist>
<pattern>PATTERN</pattern>
<message>
<id>ID</id>
<severity>SEVERITY</severity>
<summary>SUMMARY</summary>
</message>
</rule>
patternタグ中にCDATAを含めた場合、XMLに干渉する可能性がありますので使用時はご注意ください。:
<![CDATA[some<strange>pattern]]>
<tokenlist>
この<tokenlist> 要素はオプションです。この要素がある場合、どのトークンをチェックするかを指示することができます。このLISTはdefine, raw, normal , simpleのいずれかです。
define
#define プリプロセッサの記述をチェックするために使用します。
raw
プリプロセッサの出力をチェックするために使用します。
normal
normal のトークンリストをチェックするために使用します。ソースコードをある程度、単純化した結果をチェックすることになります。
simple
単純なトークンリストをチェックするために使用します。ソースコードを完全に単純化した結果をチェックすることになります。ほとんどの Cppcheckのチェックには、この 単純ばトークンリストを使用します。
もし<tokenlist>要素を省略した場合、simple が使用されます。
<pattern>
このPATTERN にはPerlの正規表現と互換性のある正規表現 PCREを指定します。
<id>
この ID にはユーザーが定義した message idを指定します。
<severity>
このSEVERITYにはCppcheck の厳格度(severities)である、次のいずれかを指定します。: information, performance, portability, style, warning,error
<summary>
オプションです。メッセージのサマリーです。もしこのsummaryトークンが指定されていなければ、マッチしたトークンが出力されます。
Cppcheck アドオン
Cppcheckのアドオンは、個別のスクリプトや個別のプログラムとして実装されています。Cppcheckのアドオンを使用すると次のような利点があります。
洗練された分析の結果を使用した個別の、外部チェックを追加できます。
ソースコードが可視化できます。
その他
Cppcheckアドオンの使用方法
現在、アドオンを使用するには2段階の操作が必要です。:
Cppcheckを実行し、ダンプファイルを生成します。
アドオンでダンプファイルを処理します。
--dumpフラグを使用するとダンプファイルを生成できます。foo/ フォルダ以下の全てのソースファイルからダンプファイルを生成するには次のようにします。
cppcheck --dump foo/
foo/ フォルダ以下の全てのダンプファイルをアドオンで処理するには次のようにします。
python addon.py foo/*.dump
Cppcheckアドオンの見つけ方
ダウンロードできる、アドオンがいくつかあります。
Cppcheck プロジェクトはいくつかのアドオンを以下の場所で提供しています。: http://github.com/danmar/cppcheck/blob/master/addons
ublinterは規格で定義されていない未定義動作に注力した"lint"です。: http://github.com/danmar/ublinter
あなたのアドオンの情報をご紹介ください。(商用、フリーを問いません。)
Cppcheck アドオンの作成
Cppcheck は XML形式でダンプファイルを生成できます。このファイルには以下のようなものが含まれています。:
トークンリスト(Token list)
シンタックスツリー(Syntax trees)
シンボルデータベース(関数、クラス、変数、スコープ)
既知の値(value flow analysis)
Cppcheckはアドオンを直接実行することはできません。直接実行するためにインターフェースはありません。これは、次のような制限がないことを意味します。:
アドオンを作成しリリースする際に、どのようなライセンスでも適用できます。
アドオンの作成に、どのようなスクリプト言語やプログラミング言語で作成できます。
アドオン作成者がユーザーインターフェースと出力を決定できます。
警告の生成以外の目的にもアドオン使用できます。
アドオン作成者の利便性のために、Cppcheck プロジェクトは PythonからCppcheckのデータにアクセスするための cppcheckdata.pyを提供しています。cppcheckdata.pyの使用はオプションです。
使用例1 - 全トークンの表示
Script:
import sys
import cppcheckdata
def printtokens(data):
for token in data.tokenlist:
print(token.str)
for arg in sys.argv[1:]:
printtokens(cppcheckdata.parse(arg))
使用例2 - 全関数リストアップ
Script:
import sys
import cppcheckdata
def printfunctions(data):
for scope in data.scopes:
if scope.type == 'Function':
print(scope.className)
for arg in sys.argv[1:]:
printfunctions(cppcheckdata.parse(arg))
使用例 3 - 全クラスリストアップ
Script:
import sys
import cppcheckdata
def printclasses(data):
for scope in data.scopes:
if scope.type == 'Class':
print(scope.className)
for arg in sys.argv[1:]:
printfunctions(cppcheckdata.parse(arg))
HTML 形式での報告
cppcheckのXML出力をHTML形式に変更できます。これを利用するには、Python と pygments module (http://pygments.org/) が必要です。Cppcheckのソースツリーにhtmlreportというフォルダがあります。このフォルダには、CppcheckのXMLファイルをHTML出力に変換するスクリプトがあります。
このコマンドでヘルプ画面を生成するには次のように実行します。
htmlreport/cppcheck-htmlreport -h
出力画面には次の内容が表示されます。:
Usage: cppcheck-htmlreport [options]
Options:
-h, --help show this help message and exit
--file=FILE The cppcheck xml output file to read defects from.
Default is reading from stdin.
--report-dir=REPORT_DIR
The directory where the html report content is written.
--source-dir=SOURCE_DIR
Base directory where source code files can be found.
使用例:
./cppcheck gui/test.cpp --xml 2> err.xml
htmlreport/cppcheck-htmlreport --file=err.xml --report-dir=test1 --source-dir=.
グラフィカルインターフェースGUI
イントロダクション
Cppcheck GUIが利用できます。
メイン画面は、このソフトを起動時に表示されます。
ソースコードのチェック
Checkメニューを使用します。
結果の確認
結果はリスト表示されます。
View メニューを操作して、メッセージの種類毎に表示/非表示を切り替えできます。
結果をXML ファイルに保存して、後で確認できます。Save results to file と Open XMLを参照してください。
設定
Languageメニューからいつでも使用言語を変更できます。
設定は、 Edit Preferences で変更できます。
プロジェクトファイル
プロジェクトファイルは、プロジェクト固有の設定を保存するのに使用します。固有の設定には次のものがあります。:
インクルードパス
プリプロセッサのdefine
このマニュアルの3 章にあるように、全てのコンパイルスイッチの組み合わせをチェックします。コンパイルスイッチの組み合わせを制限したい場合にだけ、プリプロセッサのdefineを指定してください。