DCL31-C. 識別子は宣言してから使用する
C11 では、関数宣言において型指定子を必須とし、暗黙的な型指定を禁じている。C90 では変数および関数の暗黙的な型指定を許容しており、既存のコードの中には暗黙的な型指定を行っているものもある。暗黙的な型指定を許容して従来のコードに対応している C コンパイラもあるが、新規に作成するコードでは暗黙的な型指定を使用すべきではない。このような処理系では、暗黙的な宣言が行われていると仮定してコンパイルを続行する。
違反コード (暗黙的な int
型指定)
現在の C 標準では型指定子を省略した宣言は許されない。C 標準[ISO/IEC 9899:2011]、6.7.2 には以下のように記載されている。
それぞれの宣言の宣言指定子列で、またはそれぞれの
struct
宣言および型名の型指定子型修飾子の並びで、1つ以上の型指定子を指定しなければならない。
以下の違反コード例では、型指定子を省略している。
extern foo;
処理系によっては、暗黙的な型指定が行われているコードに対して警告メッセージを発行しない。このように言語規格に準拠していない処理系では、暗黙的な型指定が行われている宣言に対し、int
型が指定されているとみなした処理が行われる。
適合コード (暗黙的な int
型指定)
以下の適合コードでは、型指定子を明示的に指定している。
extern int foo;
違反コード (暗黙的な関数宣言)
現在の言語標準において、暗黙的な関数宣言は許されない。すべての関数は、呼び出しが行われる前に明示的に宣言されていなければならない。言語標準の旧バージョン C90 では、明示的なプロトタイプを持たない関数の呼び出しがあった場合にはコンパイラが暗黙的な宣言を提供するものとされていた。
C90 標準[ISO/IEC 9899:1990]の記載は以下のとおりである。
関数呼び出し式において、括弧で囲まれた引数リストの前に付く式が単一の識別子
identifier
のみで構成され、この識別子の宣言が見当たらない場合、この関数呼び出し式を含む最も内側のブロックにextern int identifier();
という宣言があるものとして、暗黙的に識別子が宣言される。
つまり、関数呼び出し時点で関数の宣言が行われていない場合、C90 準拠の実装系では extern int identifier();
の暗黙的な宣言があるものと仮定する。
このような暗黙的な宣言では、関数が任意の数および任意の型の引数をとり、int
を返すものとみなす。しかし、現在の C 標準に準拠するためには、すべての関数は呼び出し前に明示的に宣言されていなければならない。暗黙的な関数宣言をサポートするかどうかは処理系に任されているが、C 標準に準拠している処理系では、宣言なしで関数が使用されている場合には必ず警告メッセージを発行しなければならない。
以下の違反コード例では、stdlib.h
のインクルードや malloc()
の明示的な宣言を行っていないため、C90 のみに準拠する処理系では、暗黙的に int malloc()
という宣言があるものとして扱われる。実行環境において int
のサイズが32ビット、ポインタのサイズが64ビットの場合、malloc()
の暗黙的な宣言により malloc()
の返り値であるポインタは32ビット整数に切り詰められてしまうだろう。
#include <stddef.h>
/* #include <stdlib.h> がない */
int main(void) {
for (size_t i = 0; i < 100; ++i) {
/* int malloc() を想定 */
char *ptr = (char *)malloc(0x10000000);
*ptr = 'a';
}
return 0;
}
処理系固有の詳細
この違反コード例を64ビットプラットフォーム用の Microsoft Visual Studio 2013 でコンパイルした場合、ループ内で ptr
を参照するところでアクセス違反となる。
適合コード (暗黙的な関数宣言)
以下の適合コードでは、適切なヘッダファイルをインクルードして malloc()
を宣言している。
#include <stdlib.h>
int main(void) {
for (size_t i = 0; i < 100; ++i) {
char *ptr = (char *)malloc(0x10000000);
*ptr = 'a';
}
return 0;
}
関数宣言については「DCL07-C. 関数宣言子には適切な型情報を含める」も参照。
違反コード (暗黙的な戻り値の型)
戻り値の型が暗黙的に指定される関数宣言を行ってはならない。たとえば、意味のある整数値を返す関数の場合、明示的に int
を返す関数として宣言すること。意味のある整数値を返さない関数の場合には void
を返す関数として宣言すること。
#include <limits.h>
#include <stdio.h>
foo(void) {
return UINT_MAX;
}
int main(void) {
long long int c = foo();
printf("%lld\n", c);
return 0;
}
この違反コード例では、foo()
が int
型の値を返すと想定され、UINT_MAX
は −1
に変換されてしまう。
適合コード (暗黙的な戻り値の型)
以下の適合コードでは、foo()
の返り値の型を明示的に unsigned int
型と定義している。その結果、関数は正しく UINT_MAX
を返す。
#include <limits.h>
#include <stdio.h>
unsigned int foo(void) {
return UINT_MAX;
}
int main(void) {
long long int c = foo();
printf("%lld\n", c);
return 0;
}
リスク評価
暗黙的な宣言では厳密な型チェックが行われないため、予期しない動作や誤った動作が誘発されることがある。実際のコードでは型指定子が省略されている例はめったにない。たとえあったとしても、影響はそれほど深刻ではなく、プログラムの異常終了程度であろう。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
DCL31-C |
低 |
低 |
低 |
P3 |
L3 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Astrée |
19.04
|
type-specifier function-return-type implicit-function-declaration undeclared-parameter |
Fully checked |
Axivion Bauhaus Suite |
6.9.0 |
CertC-DCL31 | Fully implemented |
Clang |
3.9
|
-Wimplicit-int |
|
Compass/ROSE |
|
|
|
Coverity |
2017.07
|
MISRA C 2012 Rule 8.1 | 実装済み |
1.2
|
CC2.DCL31 |
実装済み |
|
GCC |
4.3.5
|
|
|
Klocwork |
2018
|
CWARN.IMPLICITINT RETVOID.IMPLICIT |
|
LDRA tool suite |
9.7.1
|
24 D, 41 D, 20 S, 326 S, 496 S |
実装済み |
Parasoft C/C++test |
10.4.1
|
CERT_C-DCL31-a |
すべての関数は使用する前に宣言されていなければならない |
Polyspace Bug Finder |
R2018a |
Types shall be explicitly specified A function shall not be declared implicitly |
|
PRQA QA-C |
9.5
|
0434 (C) |
実装済み |
SonarQube C/C++ Plugin |
3.11
|
S819, S820 | 部分的に実装済み、暗黙的な戻り値の型の検出は含まれていない。 |
RuleChecker |
19.04
|
type-specifier function-return-type implicit-function-declaration undeclared-parameter |
Fully checked |
関連するガイドライン
Taxonomy |
Taxonomy item |
Relationship |
---|---|---|
CERT C コーディングスタンダード | DCL07-C. 関数宣言子には適切な型情報を含める | Prior to 2018-01-12: CERT: Unspecified Relationship |
ISO/IEC TR 24772:2013 | Subprogram Signature Mismatch [OTR] | Prior to 2018-01-12: CERT: Unspecified Relationship |
MISRA C:2012 | Rule 8.1 (required) | Prior to 2018-01-12: CERT: Unspecified Relationship |
参考資料
[ISO/IEC 9899:1990] |
|
[ISO/IEC 9899:2011] | Subclause 6.7.2, "Type Specifiers" |
[Jones 2008] |
|
翻訳元
これは以下のページを翻訳したものです。
DCL31-C. Declare identifiers before using them (revision 137)