Table of Contents
Cppcheck は C/C++の静的解析ツールです。C/C++ コンパイラやその他の解析ツールとは異なり、シンタックスエラーを検出しません。 その代わりに、Cppcheckは、コンパイラが通常、検出に失敗するような種類のバグを検出します。このプロジェクトのゴールは、擬陽性 0 です。
サポートしているプログラムのソースコードとプラットフォーム:
さまざまなコンパイラの拡張構文や、インラインアセンブル等を含む、非標準的なソースコードをチェックできます。
Cppcheck は 最新のC++規格をサポートしている、あらゆるC++コンパイラでコンパイルできるようにしています。
Cppcheck は 十分なCPUパワーとメモリーのある、あらゆるプラットフォームで動作するようにしています。
Cppcheckに限界があることをご理解ください。Cppcheckの報告しているエラーに稀に間違いのあることがあります。また、Cppcheck が検出しないバグが残っていることもあります。
ソフトウェアを注意深くテストすれば、Cppcheckを使うより、より多くのバグを検出できるでしょう。ソフトウェアを注意深く実装すれば、Cppcheckを使うより、より多くのバグを検出できるでしょう。しかし、あなたのソフトウェアを実装するときやテストするときに見逃したバグのいくつかを Cppcheckが検出できるでしょう。
Table of Contents
GUIの起動
新しプロジェクトのファイルの作成は必要ではありませんが、最初のステップに最適です。ファイル(File
)と新しいプロジェクトファイル(New project file
)を通じて学べます。
あなたのプロジェクトはどのようなプロジェクトでしょうか。あなたのプロジェクトがVisual Studioのプロジェクトの場合、または(cmake/qbs/等の)コンパイルデータベースqが精製できる場合、あなたはプロジェクトをインポート(import)できます。
そうではない場合には、そのプロジェクトのパスと定義をマニュアルで調整します。次の図は、Visual Studio のプロジェクトファイルをインポートした場合のスクリーンショットです。
プロジェクトタブ(Project tab)では、ビルドディレクトリ(Cppcheck build dir
)を設定しましょう。これはCppcheckが様々な分析する情報を保管するために使用します。プログラム全体の解析、インクリメンタル解析、統計などです。それぞれのプロジェクトは、それぞれのビルドディレクトリを持ちます。次のスクリーンショットはビルドディレクトリをcppcheck-build-dir
と設定しています。このパスはプロジェクトファイルからの相対パスです。
あなたは、あなた使用する全てのライブラリーを選択すべきです。次のスクリーンショットではmicrosoft_sal と windowsライブラリーを選択しています。ライブラリーについてはこのマニュアルを参照してして下さい。
ここでは 除外タブ(Exclude
)と抑制タブ(Suppressions
)をスキップします。これは結果をあとで微調整するために使います。
アドオンタブ(Addons)であなたは別の分析を追加できます。このアドオンにはpythonが必要です。
Table of Contents
これは単純なソースコードです。
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(厳格度)には次のものがあります。:
バグが検出されたときに使用します。
防衛的プログラミングでバグを避けるための提案です。
コードの可読性の向上に関連した、スタイル関連の指摘(未使用関数、冗長なコードなど)
コードの高速化のための提案。これらの提案は、一般的な知識に基づいたものでしかありません。このメッセージの修正によって計測できるほど処理速度が向上するかどうかはわかりません。
移植性についての警告。64 bit CPUへの移植性。コンパイラ依存(独自拡張)ソースコードについての警告など。
設定上の問題設定を変更している間だけ有効にすることをお勧めします。
デフォルトでは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 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>
Table of Contents
CMakeやVisual Studioを使っているとき、あなたは--project
を使ってプロジェクトを解析できます。
これでカンタンにチェックでき、結果も得られます。あなたに必要な設定項目はありません。しかしこれが最も良い結果を得る方法とは限りません。私たちは、このプロジェクトファイルを利用する方法と、--project
を利用しない方法を試してよいオプションを選ぶようにお勧めします。
Cppcheckはコンパイルデータベースを理解します。あなたはこれをCMakeで生成できます。
例:
$ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON .
compile_commands.json
ファイルが現在のディレクトリに生成されます。
それからCppcheckをこのように実行します。:
$ cppcheck --project=compile_commands.json
Table of Contents
あなたが --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に関数や型の実装の情報を提供する方がよいでしょう。
Table of Contents
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>
要素に記載されます。属性:
id
エラーのidこれは、妥当なシンボル名です。
severity
以下のいずれかです: error
, warning
, style
, performance
, portability
, information
msg
短い形式のエラーメッセージ
verbose
長い形式のエラーメッセージ
inconclusive
この属性は、メッセージに疑いのある場合にのみ使用されます。
cwe
メッセージのCWE ID。この属性は、メッセージのCWE IDが判明している場合のみ使用されます。
Table of Contents
もし、テンプレートを使用して、出力の形式を変更することができます。
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; ^
自分で自身でパターンを作成できます。例えば古い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
では以下の要素が利用できます。:
ファイル名
行数
カラム番号
全ての位置。それぞれの位置は[{file}:{line}]のフォーマットで記載され、また->で位置を区切ります。例えば次のようになります。: [multiline.c:8] -> [multiline.c:9] -> [multiline.c:3]
警告が確定的でない場合のメッセージを表示します。このメッセージは含まれていない場合もある、任意のテキストです。サンプル: {inconclusive:inconclusive,}
エラー/警告/スタイル/性能/移植性/情報
警告メッセージ
警告id
実際のコード
タブ
改行
キャリッジリターン
Table of Contents
CppcheckはMISRA
C 2012 向けのチェッカのアドオンを持っています。
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 ...
あなたが無効にしたいルールは、ルールテキストがなくても構いません。ルールテキストのないルールはアドオンによって抑制されます。
Table of Contents
ある種のエラーをフィルタリングしたい場合、出力を抑制することができます。
エラーの種類によって出力を抑制することができます。つぎのいずれかの形式で出力を抑制します。:
[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/
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
Table of Contents
WinAPI, POSIX, gtk, Qtなど他の外部のライブラリを使用した場合、Cppcheck
は外部の関数がどのようなものであるかがわかりません。Cppcheck
はそのため、メモリリークやバッファオーバーフロー、ヌルポインタのデリファレンスの可能性といったさまざまな問題が検出できません。これを解決するには設定ファイル(.cfg file)を使用します。
Cppcheckはいくつかのライブラリ用の設定を持っています。これらは次のようにしてロードできます。cppcheckは C または C++言語用の標準ライブラリの設定 std.cfg
はいつもロードします。ご注意ください。もしあなたが有名なライブラリの設定ファイルを作成した場合や更新した場合には、私達のサイトにアップロードしてくれると非常に助かります。
あなたのプロジェクト専用の設定ファイルを作成し、使用することができます。そのためには、--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 はリークのチェックが調整できます。言い換えれば、あなたはメモリーやリソースを割り当てる関数またはその割り当てを回収する関数を指定できます。
ここにサンプルのプログラムがあります。:
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>
タグでオーバーラップしたグループはマージされます。
しばしば、割り当てられたポインタを関数に渡すことがあります。例:
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 はいくつかの種類があります。:
バッファーのサイズが、その他の引数の文字列長より大きくなければなりません。例: std.cfg のstrcpyの設定を参照してください。
バッファーのサイズがその他の引数の値より大きくなればなりません。例: std.cfg のmemsetの設定を参照してください。
バッファーのサイズがその他の引数のバッファーのサイズより大きくなればなりません。例:posix.cfgのmemccpyの設定をみてください。
バッファーのサイズがその他の2つの引数の値の積より大きくなればなりません。典型的な使用例としては、一つの引数が構造体などの要素のサイズを指定し、もうひとつの引数が要素の個数を定義するような場合です。例: std.cfg のfreadの設定を参照してください
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
をつかうとエラーが出力されます。:
# cppcheck --check-library 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>
他になにも指定されていない限り、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>
これらは、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のための適切な設定は次のようになる。:
<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を使用することができます。例:
<?xml version="1.0"?> <def> <define name="NULL_VALUE" value="0"/> </def>
プリプロセッサの段階でソースコード中に "NULL_VALUE" が現れるごとに、"0"で置き換えます。
多くのソースコードで、プラットフォームに依存しない型を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
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>
Table of Contents
正規表現を使用して、ユーザーがルール(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>
要素はオプションです。この要素がある場合、どのトークンをチェックするかを指示することができます。このLIST
はdefine
, raw
, normal
, simple
のいずれかです。
#define プリプロセッサの記述をチェックするために使用します。
プリプロセッサの出力をチェックするために使用します。
normal
のトークンリストをチェックするために使用します。ソースコードをある程度、単純化した結果をチェックすることになります。
単純なトークンリストをチェックするために使用します。ソースコードを完全に単純化した結果をチェックすることになります。ほとんどの Cppcheckのチェックには、この 単純ばトークンリストを使用します。
もし<tokenlist>要素を省略した場合、simple
が使用されます。
このSEVERITY
にはCppcheck
の厳格度(severities)である、次のいずれかを指定します。: information
, performance
, portability
, style
, warning
,error
Table of Contents
Cppcheckのアドオンは、個別のスクリプトや個別のプログラムとして実装されています。Cppcheckのアドオンを使用すると次のような利点があります。
洗練された分析の結果を使用した個別の、外部チェックを追加できます。
ソースコードが可視化できます。
その他
現在、アドオンを使用するには2段階の操作が必要です。:
Cppcheckを実行し、ダンプファイルを生成します。
アドオンでダンプファイルを処理します。
--dump
フラグを使用するとダンプファイルを生成できます。foo/ フォルダ以下の全てのソースファイルからダンプファイルを生成するには次のようにします。
cppcheck --dump foo/
foo/ フォルダ以下の全てのダンプファイルをアドオンで処理するには次のようにします。
python addon.py foo/*.dump
Cppcheck は XML形式でダンプファイルを生成できます。このファイルには以下のようなものが含まれています。:
トークンリスト(Token list)
シンタックスツリー(Syntax trees)
シンボルデータベース(関数、クラス、変数、スコープ)
既知の値(value flow analysis)
Cppcheckはアドオンを直接実行することはできません。直接実行するためにインターフェースはありません。これは、次のような制限がないことを意味します。:
アドオンを作成しリリースする際に、どのようなライセンスでも適用できます。
アドオンの作成に、どのようなスクリプト言語やプログラミング言語で作成できます。
アドオン作成者がユーザーインターフェースと出力を決定できます。
警告の生成以外の目的にもアドオン使用できます。
アドオン作成者の利便性のために、Cppcheck プロジェクトは PythonからCppcheckのデータにアクセスするための cppcheckdata.pyを提供しています。cppcheckdata.pyの使用はオプションです。
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))
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))
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=.
Table of Contents
結果はリスト表示されます。
メニューを操作して、メッセージの種類毎に表示/非表示を切り替えできます。
結果をXML ファイルに保存して、後で確認できます。Save results to file
と Open XML
を参照してください。
プロジェクトファイルは、プロジェクト固有の設定を保存するのに使用します。固有の設定には次のものがあります。:
インクルードパス
プリプロセッサのdefine
このマニュアルの3 章にあるように、全てのコンパイルスイッチの組み合わせをチェックします。コンパイルスイッチの組み合わせを制限したい場合にだけ、プリプロセッサのdefineを指定してください。