JPCERT コーディネーションセンター

DCL07-C. 関数宣言子には適切な型情報を含める

DCL07-C. 関数宣言子には適切な型情報を含める

関数宣言子は、返り値の型や引数のリストなどを含む適切な型情報をつけて宣言されなくてはならない。関数指定子に型情報が適切に指定されないと、コンパイラは関数の型情報を適切にチェックすることができない。標準ライブラリを使用する場合、適切な型情報を持つ関数宣言子を取得するもっとも簡単で(かつ推奨される)方法は適切なヘッダファイルをインクルードすることである。

適切な型情報を持たない関数宣言子を含むプログラムをコンパイルしようとすると、典型的には警告が出るが、コンパイルは通る。これらの警告は解決すべきである(「MSC00-C. 高い警告レベルで警告を出さずにコンパイルする」を参照)。

違反コード (Non-Prototype-Format Declarators)

以下のコード例では、識別子並びの形を引数の宣言に用いている。

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

C 標準 [ISO/IEC 9899:2011] のセクション 6.11.7 には「仮引数の識別子並びと宣言並びを別々に与える関数定義(関数原型形式の仮引数の型及び識別子の宣言ではない。)の使用は、廃止予定事項とする」と書かれている。

適合コード (Non-Prototype-Format Declarators)

以下の適合コードでは、int が型指定子であり、max(int a, int b) は関数宣言子であり、中括弧の中のブロックは関数本体である。

int max(int a, int b) {
  return a > b ? a : b;
}
違反コード (関数プロトタイプ)

プロトタイプなしで関数宣言を行うと、コンパイラは正しい数および型の引数が関数に渡されているものと想定して動作する。これにより、プログラマが予期せぬ未定義の動作が引き起こされる恐れがある。

以下のコード例では、file_a.c 中の func() の定義は3つの引数をとるが、実際には2つしか与えられていない。

/* file_a.c source file */
int func(int one, int two, int three){
  printf("%d %d %d", one, two, three);
  return 1;
}

しかし、file_b.c 中に func() のプロトタイプが存在しないため、コンパイラは正しい数の引数が渡されていると想定し、プログラムスタック上の次の値を第三引数として使用する。

/* file_b.c source file */
func(1, 2);

C99 では暗黙的な関数宣言を C 言語からとりのぞいている。しかし、多くのコンパイラは、警告は出すかもしれないが、いまだ暗黙的に宣言された関数のコンパイルを許している。これらの警告は解決すべきである(「MSC00-C. 高い警告レベルで警告を出さずにコンパイルする」を参照)。

適合コード (関数プロトタイプ)

以下の適合コードは、func()の関数プロトタイプを、それが呼び出される翻訳単位のなかに正しく含めている。また、関数呼び出しを修正し、正しい数の引数を渡している。

/* file_b.c source file */
int func(int, int, int);

func(1, 2, 3);
違反コード (関数ポインタ)

関数ポインタが互換性のない関数を指しているとき、その関数を関数ポインタを通じて呼び出すと、プロセススタックを破壊するかもしれない。呼出し側の関数による、予期せぬデータへのアクセスにつながるかもしれない。

以下のコード例では、関数ポインタ fn_ptr は整数引数を三つとる関数 add() を指している。しかし、fn_ptr は整数引数を二つとると指定されている。fn_ptradd() を参照するようにセットすると、予期せぬプログラムの動作を引き起こす。この例は、「DCL35-C. 関数定義と一致しない型で関数を呼び出さない」にも違反している。

int add(int x, int y, int z) {
   return x + y + z;
}

int main(int argc, char *argv[]) {
   int (*fn_ptr) (int, int);
   int res;
   fn_ptr = add;
   res = fn_ptr(2, 3);  /* 間違い */
   /* ... */
   return 0;
}
適合コード (関数ポインタ)

このコードを修正するには、fn_ptr を引数を三つとるように変更すればよい。

int add(int x, int y, int z) {
   return x + y + z;
}

int main(int argc, char *argv[]) {
   int (*fn_ptr) (int, int, int) ;
   int res;
   fn_ptr = add;
   res = fn_ptr(2, 3, 4);
   /* ... */
   return 0;
}
リスク評価

関数宣言子に型情報を含めないと、予期せぬあるいは意図しないプログラムの動作を引き起こすかもしれない。

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

DCL07-C

P3

L3

自動検出

ツール

バージョン

チェッカー

説明

ECLAIR

1.1

decltype

実装済み

GCC

V. 4.3.5

 

-Wstrict-prototypes フラグを使用すると、このレコメンデーションの違反を検出できる。

LDRA tool suite

V. 8.5.4

21 S
135 S
170 S

実装済み

PRQA QA-C 8.1

3335
3450
0563
2050

実装済み
関連するガイドライン
ISO/IEC TR 24772:2013 Type System [IHN]
Subprogram Signature Mismatch [OTR]
MISRA-C Rule 8.2
参考資料
[ISO/IEC 9899:2011] Section 6.11.7, "Function Definitions"
[Spinellis 2006] Section 2.6.1, "Incorrect Routine or Arguments"
翻訳元

これは以下のページを翻訳したものです。

DCL07-C. Include the appropriate type information in function declarators (revision 153)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter