LoginSignup
0
0

More than 5 years have passed since last update.

gccでuninitializedなdoubleを参照渡しした場合にwarningが出ない問題

Last updated at Posted at 2019-03-22

まとめ

  • gccでuninitializedなdoubleを参照渡ししてもwarningが出ない
  • cppcheckを使うと検知できることもあるが、検知できないこともある
  • clang-tidy, clang analyzerでは検知できない

発端

以下のコード。
test2はコンパイルエラーになるが、testはパスしてしまう。
valgrindなら検出できるが、コンパイルエラーで検知してほしい。

#include <algorithm>

#if 1
int test() {
    int a = 3;
    int b;
    return std::max<int>(a, b);
}
#else
int test2() {
    int b;
    return b;
}
#endif

コンパイル結果 (-Werror -Wall)
https://godbolt.org/z/D3L2oC

試行錯誤

std::maxのせいではないか?

std::maxではなく、自前で似たような関数を作ってみると、-Wallで検知できる。

#include <algorithm>

#if 1
int mymax(int a, int b) {
    return a > b ? a : b;
}
int test() {
    int a = 3;
    int b;
    return std::max<int>(a, b);
}
int test3() {
    int a = 3;
    int b;
    return mymax(a, b);
}
#else
int test2() {
    int b;
    return b;
}
#endif

コンパイル結果
https://godbolt.org/z/a6kmmm

const 参照渡しだと通ってしまった

#include <algorithm>

#if 1
int mymax(const int &a, const int& b) {
    return a > b ? a : b;
}
int test() {
    int a = 3;
    int b;
    return std::max<int>(a, b);
}
int test3() {
    int a = 3;
    int b;
    return mymax(a, b);
}
#else
int test2() {
    int b;
    return b;
}
#endif

コンパイル結果
https://godbolt.org/z/mXlKwm

const ポインタ渡しだとパスした

int max(const int *a, const int *b) {
    return *a > *b ? *a : *b;
}

int test() {
    int a;
    int b = 3;
    return max(&a, &b);
}

コンパイル結果
https://godbolt.org/z/CtRYmQ

参照渡しでコンパイルエラーにするにはどうすればいいか?

関連記事
https://stackoverflow.com/questions/32264463/w-flag-for-gcc-to-detect-taking-const-pointer-of-uninitialised-variable

-O2をつけてみたら、エラーになった
std::max: https://godbolt.org/z/elwQsT
const参照: https://godbolt.org/z/3-M74q
constポインタ: https://godbolt.org/z/BcbtE_

結論、最適化オプションをつけていれば問題なかった

と思ったが、doubleだとパスする

const double &mymax(const double &a, 
const double &b) {
    return a > b ? a : b;
}

double test(double x) {
    double a = 3;
    double b;
    return mymax(a, b);
}

コンパイル結果
https://godbolt.org/z/EodiVt

発端はdoubleだったので、困る

値渡しだと?

double mymax(double a, double b) {
    return a > b ? a : b;
}

double test(double x) {
    double a = 3;
    double b;
    return mymax(a, b);
}

エラーになる。

コンパイル結果
https://godbolt.org/z/luJrGq

どうする?

cppcheckで検知できました。
http://cppcheck.sourceforge.net/

cmakeにcppcheckを組み込む方法

基本

cppcheckのエラー時にcmakeのビルドをエラーにする方法

背景

cppcheckはデフォルトだとエラーでもexit statusは0になる。
--error-exitcode=X オプションを使うとexit statusをエラー時のXにできる。
しかし、これを指定してもcmake経由で使うとエラーにならない。

解決策

以下のプルリクを発見。
https://gitlab.kitware.com/cmake/cmake/merge_requests/2459

2018/10/12にmasterにマージされている。

多分最新版のcmakeを使えばOK
(具体的にどのバージョンから含まれているかは未調査)

必要なマクロを定義する

cppcheckは普通のコンパイラで暗黙に定義されているマクロが無いので、OSごとに分岐しているコードなどで、どのOSにも該当しないケースでこけることがある。

以下でかいけつ
https://srz-zumix.blogspot.com/2012/03/cppcheck.html

特定のエラーや特定のファイルを除外する

以下でかいけつ
https://stackoverflow.com/questions/37609705/cppcheck-how-to-skip-a-directory-of-third-party-header-files

悲報

発端となった実際のコードにcppcheckをかけたら、なぜかuninitialized doubleの参照渡しを検知できませんでした・・・

この用途ではcppcheckは使えませんでしたが、他のバグを検出できたのでよしとします。

-> gcc + -O2 + -ffast-mathで検知できました。詳細はコメント参照

他のstatic analyzer

uninitialized doubleの参照渡しを検知できるstatic analyzerを探します。

clang-tidy

検知できなかった

clang-analyzer

検知できなかった

sonarqube

調べていない

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0