メインコンテンツ

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

CERT C++: DCL60-CPP

Obey the one-definition rule

説明

ルール定義

単一定義のルールに従います。1

Polyspace 実装

このチェッカーは、以下をチェックします。

  • インライン制約が守られていません

  • モジュール間で関数またはオブジェクトの定義が同一ではない

すべて展開する

問題

"インライン制約が守られていません" は、非静的インライン関数内で、ファイル スコープの変更可能な静的変数を参照する場合に発生します。チェッカーは、変数が const 修飾子付きでない場合、その変数を変更可能と見なします。

たとえば、var は、inline 関数 func 内で定義された変数です。g_step は、同じインライン関数内で参照先となるファイル スコープの変更可能な静的変数です。

static int g_step;
inline void func (void) {
    int var = g_step++; // Violation
}

リスク

複数の関数呼び出しで静的変数を変更すると、各呼び出しで同じ変数が変更されると想定されます。たとえば、前述のコードでは、func が呼び出されるたびに g_step の同じインスタンスがインクリメントされると想定されます。

関数にインラインと非インラインの定義が個別のファイル内にある場合に、その関数を呼び出すとき、C++ 標準では、インラインまたは非インライン形式のいずれかを使用することがコンパイラに許可されます (ISO®/IEC 9899:2011 の節6.7.4).コンパイラで、1 つの呼び出しではインライン定義を使用し、別の呼び出しでは非インラインを使用している場合、両方の呼び出しで同じ変数が変更されることはなくなります。この動作は、静的変数に対する想定に反し、One Definition Rule (定義は 1 度の規則) に違反します。

修正方法

次のいずれかの修正方法を使用します。

  • 変数を変更する意図がない場合は、変数を const として宣言する。

    変数を変更しなければ、予期しない変更の問題は起こりません。

  • 変数を非 static にする。宣言から static 修飾子を削除します。

    変数が関数内で定義されている場合、その変数は標準ローカル変数になります。ファイル スコープで定義されている場合は extern 変数になります。この動作の変更が意図したとおりであることを確認します。

  • 関数を static にする。static 修飾子を関数定義に追加します。

    関数を static にすれば、インライン定義のあるファイルでは関数を呼び出すときに常にインライン定義が使用されます。他のファイルでは関数の別の定義が使用されます。どの関数定義が使用されるかという問題が、コンパイラ任せでなくなります。

例 — inline 関数内で変更される、ファイル スコープの static 変数

この例では、インライン関数 foo() がファイル スコープの変数 var をインクリメントしてから、参照によって myFunc に渡します。Polyspace® は、これらの演算に対してこのルールの違反を報告します。


typedef int int32_t;
typedef const int cint32_t;
static int32_t var = 42;

int32_t myFunc(cint32_t &arg1, cint32_t &arg2);

inline int32_t foo(int32_t num)
{
  var++; //Noncompliant
  return myFunc(num, var);  //Noncompliant
}
修正 — inline 関数を static として宣言する

このルールの違反を修正するには、foo()static 関数として宣言します。このように明確に指定することで、静的変数の使用に関するあいまいさが解消されます。

typedef int int32_t;
typedef const int cint32_t;
static int32_t var = 42;

int32_t myFunc(cint32_t &arg1, cint32_t &arg2);

static inline int32_t foo(int32_t num)
{
  var++; // Compliant
  return myFunc(num, var);  // Compliant
}

問題

モジュール間で関数またはオブジェクトの定義が同一ではないは、関数またはオブジェクトが複数のモジュールで定義されているが、識別子、キーワード、リテラル、演算子、記号の付け方、その他の区切り記号が異なる場合に発生します。このチェッカーは、次のような未使用のコードでは起動されません。

  • インスタンス化されていないテンプレート

  • 呼び出されない関数 static または extern

  • 呼び出されない未定義のローカル関数

  • 未使用の型および変数

既定の Polyspace as You Code 解析では、チェッカーはこの問題にフラグを設定しませんPolyspace as You Code 解析で非アクティブにされるチェッカー (Polyspace Access)を参照してください

リスク

同じオブジェクトまたは非インライン関数の定義がモジュールによって異なると、予期せぬ動作が発生します。プログラムは、使用しているソフトウェアとハードウェアによって、クラッシュする場合やメモリ リークが発生する場合があります。

修正方法

オブジェクトと非インライン関数を、トークンの使用に差が無いように定義します。モジュール間で、オブジェクトと非インライン関数の定義のトークンを同じ順序と種類で使用します。

例: オブジェクトの定義でトークンに相違がある

次の例では次の 2 つのファイルを使用しています。

  • file1.cpp:

    
    typedef struct S //Noncompliant
    {
       int x;
       int y;
    }S; 
    void foo(S& s){
    //...
    }
  • file2.cpp:

    
    typedef struct S 
    {
       int y;
       int x;
    }S ; 
    void bar(S& s){
    //...
    }

この例では、file1.cppfile2.cpp の両方で構造体 S を定義しています。定義では構造体のフィールドの順序が入れ替わっています。

修正: モジュール間で同一の定義を使用する

1 つの修正方法として、ヘッダー ファイルで構造体 S を定義し、2 つのモジュールでそのヘッダーをインクルードします。

  • S.h:

    struct S //Compliant
    {
       int x;
       int y;
    }; 
  • file1.cpp:

    #include"S.h" 
    void foo(S& s){
    //...
    }
  • file2.cpp:

    #include"S.h" 
    void bar(S& s){
    //...
    }

チェック情報

グループ: 01.宣言と初期化 (DCL)

バージョン履歴

R2019a で導入

すべて展開する


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.