DCL40-C. 同一の関数やオブジェクトに対して適合(compatible)しない宣言をしない
同一の関数やオブジェクトに対して複数の宣言を行う際、それらの宣言における型は適合(compatible)していなければならない。適合していない型による宣言の存在は 未定義の動作につながるからである。C 言語仕様の 6.2.7 によれば、(同一の関数やオブジェクトを表す)2つの型は全く同一の型でなくともよいが、適合型でなければならないとされている。また、2つの型が「適合している」ことの定義もなされている。
C 言語仕様では、同一の関数やオブジェクトに対して適合していない宣言を行うことによって未定義の動作(UB)となる例を4つ挙げている。
UB |
説明 |
違反コード例 |
---|---|---|
同一のオブジェクトや関数を表す二つの宣言の型が適合していない(6.2.7)。 |
このガイドラインの全ての違反コード |
|
31 | 二つの識別子が意味のない文字でだけ異なる(6.4.2.1)。 | 長すぎる識別子 |
オブジェクトに格納された値にアクセスする左辺値式の型が言語仕様上認められているものでない(6.5)。 |
||
呼び出される関数を表す式によって指される式の型が、関数定義における型と適合していない(6.5.2.2)。 |
同じプログラム内に型が適合していない2つの宣言があるというだけであれば、ほとんどの実装で問題にはならないかもしれない。しかし、関数定義における型と適合していない型を持つ式を介して関数呼び出しを行った場合の影響は致命的となることが多い。同様に、定義と適合していない型の左辺値を使用してオブジェクトにアクセスした場合の影響は、意図せぬ情報漏えい、メモリの上書き、ハードウェアトラップなど、多岐にわたる可能性がある。
違反コード (オブジェクトの宣言が適合していない)
この違反コード例では、変数 i
の宣言が、ファイル a.c
では int
型、ファイル b.c
では short
型となっている。これらの宣言は適合していないため、未定義の動作 undefined behavior 15 となる。また、関数 f()
のなかで、適合していない型の左辺値でオブジェクトにアクセスしているため、未定義の動作 undefined behavior 37 が発生する。この影響は、意図せぬ情報漏えい、メモリの上書き、ハードウェアトラップなど、多岐にわたる可能性がある。
/* In a.c */
extern int i; /* UB 15 */
int f(void) {
return ++i; /* UB 37 */
}
/* In b.c */
short i; /* UB 15 */
適合コード (オブジェクトの宣言が適合していない)
以下の適合コードでは、変数 i
の宣言は適合する型になっている。
/* In a.c */
extern int i;
int f(void) {
return ++i;
}
/* In b.c */
int i;
違反コード (配列の宣言が適合していない)
この違反コード例では、変数 a
の宣言がファイル a.c
ではポインタ型なのに対し、ファイル b.c
では配列型となっており、2つの型は適合していない。そのため、未定義の動作 undefined behavior 15 となる。前の例と同様に、関数 f()
における当該オブジェクトへのアクセスは未定義の動作 undefined behavior 37 となり、ハードウェアトラップ発生などの影響があるだろう。
/* In a.c */
extern int *a; /* UB 15 */
int f(unsigned int i, int x) {
int tmp = a[i]; /* UB 37: read access */
a[i] = x; /* UB 37: write access */
return tmp;
}
/* In b.c */
int a[] = { 1, 2, 3, 4 }; /* UB 15 */
適合コード (配列の宣言が適合していない)
この適合コードでは、a.c
と b.c
それぞれにおける変数 a
の配列宣言は適合している。
/* In a.c */
extern int a[];
int f(unsigned int i, int x) {
int tmp = a[i];
a[i] = x;
return tmp;
}
/* In b.c */
int a[] = { 1, 2, 3, 4 };
違反コード (関数の宣言が適合していない)
この違反コード例では、関数 f()
のプロトタイプ宣言がファイル a.c
とファイル b.c
で適合していないため、未定義の動作 undefined behavior 15 となる。また、関数呼出しは未定義の動作 undefined behavior 41 となり、致命的な影響をもたらす。
/* In a.c */
extern int f(int a); /* UB 15 */
int g(int a) {
return f(a); /* UB 41 */
}
/* In b.c */
long f(long a) { /* UB 15 */
return a * 2;
}
適合コード (関数の宣言が適合していない)
この適合コードでは関数 f
のプロトタイプ宣言は適合した型になっている。
/* In a.c */
extern int f(int a);
int g(int a) {
return f(a);
}
/* In b.c */
int f(int a) {
return a * 2;
}
違反コード (可変長引数関数の宣言が適合していない)
この違反コード例では、関数 buginf()
は可変引数関数として定義されている。第2引数以降は全て符号付き整数で最後の引数には目印として -1
が渡されるものとしよう。
/* In a.c */
void buginf(const char *fmt, ...) {
/* ... */
}
/* In b.c */
void buginf();
一見、このコードにはなにも問題ないように見えるかもしれないが、C 言語仕様の 6.7.6.3 の paragraph 15 [ISO/IEC 9899:2011] によれば、未定義の動作となる部分が含まれている。
ふたつの関数型が適合するためには、それぞれの返却値の型は適合していなければならない。さらに、両方が仮引数型並びをもつ場合、仮引数の個数及び省略記号の有無に関して一致し、対応する仮引数の型が適合していなければならない。一方の型が仮引数型並びをもち、他方の型が関数定義の一部でない関数宣言子によって指定され、識別子並びが空の場合、仮引数型並びは省略記号を含んではならない。また、各仮引数の型は、既定の実引数拡張を適用した結果の型と適合していなければならない。
適合コード (可変長引数関数の宣言が適合していない)
この適合コードでは、関数 buginf()
に関して適切なプロトタイプ宣言がなされている。
/* In a.c */
void buginf(const char *fmt, ...) {
/* ... */
}
/* In b.c */
void buginf(const char *fmt, ...);
違反コード (長すぎる識別子)
この違反コード例では、ファイル bashline.h
で宣言されている関数 bash_groupname_completion_function()
の識別子の長さは、規格合致処理系が扱えるべきとされている外部識別子の最小値 31 より 3 文字長くなっている。ファイル b.c
で定義されている整数型変数 bash_groupname_completion_funct
は長さ 31 であり、処理系によってはこの2つの識別子が区別できなくなる可能性がある。外部識別子の長さが 31 文字までとなっている処理系では、これは未定義の動作 undefined behavior 31 となる。2つの宣言は同一関数の適合していない宣言となり(未定義の動作 undefined behavior 15)、関数呼出しも未定義動作 undefined behavior 41 となる。
/* bash/bashline.h */
/* UB 15, UB 31 */
extern char * bash_groupname_completion_function(const char *, int);
/* In a.c */
#include "bashline.h"
void f(const char *s, int i) {
bash_groupname_completion_function(s, i); /* UB 41 */
}
/* In b.c */
int bash_groupname_completion_funct; /* UB 15, UB 31 */
NOTE: このコードで使っている識別子 bash_groupname_completion_function
は GNU Bash バージョン 3.2 のソースコードからとったものである。
適合コード (長すぎる識別子)
この適合コードでは、bashline.h
で宣言されている関数ポインタ bash_groupname_completion()
の識別子の長さは 32 文字より短い。したがって規格合致処理系であれば bash_groupname_completion_funct
と衝突するおそれはない。
/* bash/bashline.h */
extern char * bash_groupname_completion(const char *, int);
/* In a.c */
#include "bashline.h"
void f(const char *s, int i) {
bash_groupname_completion(s, i);
}
/* In b.c */
int bash_groupname_completion_funct;
リスク評価
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
DCL40-C |
低 |
低 |
中 |
P2 |
L3 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Astrée |
19.04
|
type-compatibility type-compatibility-link distinct-extern |
Fully checked |
Axivion Bauhaus Suite |
6.9.0 |
CertC-DCL40 | Fully implemented |
CodeSonar |
5.0p0
|
LANG.STRUCT.DECL.IF LANG.STRUCT.DECL.IO |
Inconsistent function declarations Inconsistent object declarations |
Coverity |
2017.07
|
MISRA C 2012 Rule 8.4 | Implemented |
LDRA tool suite | 8.5.4 |
1 X, 17 D |
Partially implemented |
Parasoft C/C++test |
10.4.1 |
CERT_C-DCL40-a |
A declaration shall be visible when an object or function with external linkage is defined |
Parasoft Insure++ |
|
|
Runtime analysis |
Polyspace Bug Finder |
R2018a |
|
Mismatch between function or variable declarations External identifiers shall be distinct All declarations of an object or function shall use the same names and type qualifiers |
PRQA QA-C |
9.5
|
0776, 0778, 0779, 0789, 1510 |
実装済み |
PRQA QA-C++ |
4.3
|
1510 |
|
RuleChecker |
19.04
|
type-compatibility type-compatibility-link distinct-extern |
実装済み |
関連するガイドライン
Taxonomy |
Taxonomy item |
Relationship |
---|---|---|
ISO/IEC TS 17961 | Declaring the same function or object in incompatible ways [funcdecl] | Prior to 2018-01-12: CERT: Unspecified Relationship |
MISRA C:2012 | Rule 8.4 (required) | Prior to 2018-01-12: CERT: Unspecified Relationship |
参考資料
[Hatton 1995] | Section 2.8.3 |
[ISO/IEC 9899:2011] | 6.7.6.3, "Function Declarators (including Prototypes)" J.2, "Undefined Behavior" |
翻訳元
これは以下のページを翻訳したものです。
DCL40-C. Do not create incompatible declarations of the same function or object (revision 87)