DCL36-C. 矛盾する結合の種類を使用して識別子を宣言しない
異なるスコープで宣言された識別子や同一スコープ内で複数回宣言された識別子からオブジェクトや関数への参照を結合(linkage)と呼ぶ。結合には、外部結合、内部結合、無結合の3種類がある。これらは次のような特徴を持つ[Kirch-Prinz 2002]。
- 外部結合: 外部結合の識別子は、プログラム全体(つまり、そのプログラムに属するすべての翻訳単位とライブラリ)で同じオブジェクトまたは関数を表す。リンカはこの識別子を利用できる。外部結合を持つ同じ識別子が再び宣言されると、リンカはその識別子を同じオブジェクトまたは関数と結び付ける。
- 内部結合: 内部結合の識別子は、ある翻訳単位内の同じオブジェクトまたは関数を表す。リンカは内部結合の識別子に関する情報を持たない。そのため、これらの識別子は翻訳単位の内部のみで有効となる。
- 無結合: 識別子に結合がなければ、その識別子を使用した新しい宣言はすべて、新しい変数や新しい型など何か新しいものを宣言する。
C 標準 [ISO/IEC 9899:2011] において、結合は次のように定義されている。
オブジェクトまたは関数に対するファイルスコープの識別子の宣言が記憶域クラス指定子
static
を含む場合その識別子は、内部結合を持つ。識別子が、その識別子の以前の宣言が可視であるスコープにおいて、記憶域クラス指定子
extern
を伴って宣言される場合、次のとおりとする。以前の宣言において内部結合または外部結合が指定されているならば、新しい宣言における識別子は以前の宣言と同じ結合を持つ。可視である以前の宣言がない場合、または以前の宣言が無結合である場合、この識別子は外部結合を持つ。関数の識別子の宣言が記憶域クラス指定子を持たない場合、その結合は、記憶域クラス指定子
extern
を伴って宣言された場合と同じ規則で決定する。オブジェクトの識別子の宣言がファイルスコープをもち、かつ記憶域クラス指定子を持たない場合、その識別子の結合は、外部結合とする。オブジェクトまたは関数以外を宣言する識別子、関数仮引数を宣言する識別子、および記憶域クラス指定子
extern
を伴わないブロックスコープのオブジェクトを宣言する識別子は、無結合とする。
(1つの翻訳単位内で)内部結合および外部結合の両方に分類される識別子を使用すると、未定義の動作となる。(「undefined behavior 8」も参照。)翻訳単位には、ソースファイルとそのヘッダ、そして前処理指令 #include
によってインクルードされるすべてのソースファイルが含まれる。
以下の表は、単一の翻訳単位内で 2 回宣言されるオブジェクトに割り当てられる結合を示す。列は最初の宣言を、行は2回めの宣言を表す。
1 回目 / 2 回目 |
|
なし |
|
---|---|---|---|
|
内部 |
未定義 |
内部 |
なし |
未定義 |
なし |
外部 |
|
未定義 |
未定義 |
外部 |
違反コード
以下のコード例では、i2
と i5
は内部結合および外部結合の両方を持つように定義されている。どちらの識別子を使用する場合も、未定義の動作となる。
int i1 = 10; /* 定義、外部結合 */
static int i2 = 20; /* 定義、内部結合 */
extern int i3 = 30; /* 定義、外部結合 */
int i4; /* 仮定義、外部結合 */
static int i5; /* 仮定義、内部結合 */
int i1; /* 正しい仮定義 */
int i2; /* 未定義、前の定義と結合の不一致 */
int i3; /* 正しい仮定義 */
int i4; /* 正しい仮定義 */
int i5; /* 未定義、前の定義と結合の不一致 */
int main(void) {
/* ... */
return 0;
}
処理系固有の詳細
Microsoft Visual Studio 2013 でコンパイルすると、最も厳しい診断レベルを指定していても、このコード例に対して警告を出さない。
GCC コンパイラは、i2
と i5
の矛盾する定義に対し、致命的エラーを出す。
適合コード
以下の適合コードには矛盾する定義は含まれていない。
int i1 = 10; /* 定義、外部結合 */
static int i2 = 20; /* 定義、内部結合 */
extern int i3 = 30; /* 定義、外部結合 */
int i4; /* 仮定義、外部結合 */
static int i5; /* 仮定義、内部結合 */
int main(void) {
/* ... */
return 0;
}
リスク評価
内部結合および外部結合の両方に分類される識別子の使用は、未定義の動作となる。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
DCL36-C |
中 |
中 |
中 |
P8 |
L2 |
自動検出(最新の情報はこちら)
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Astrée |
19.04
|
static-function-declaration static-object-declaration |
Partially checked |
Axivion Bauhaus Suite |
6.9.0 |
CertC-DCL36 | Fully implemented |
Coverity |
2017.07
|
PW.LINKAGE_CONFLICT | 実装済み |
1.2
|
CC2.DCL36 |
実装済み |
|
GCC |
4.3.5
|
|
|
Klocwork |
2018
|
|
|
LDRA tool suite |
9.7.1
|
461 S, 575 S, 2 X |
実装済み |
Splint |
3.1.1
|
|
|
Parasoft C/C++test |
10.4.1
|
CERT_C-DCL36-a | Identifiers shall not simultaneously have both internal and external linkage in the same translation unit |
Polyspace Bug Finder |
R2018a |
Function types shall be in prototype form with named parameters A compatible declaration shall be visible when an object or function with external linkage is defined The static storage class specifier shall be used in all declarations of objects and functions that have internal linkage A function shall not be declared implicitly |
|
PRQA QA-C |
9.5
|
0625 (U) | 実装済み |
RuleChecker |
19.04
|
static-function-declaration static-object-declaration |
一部実装済み |
関連するガイドライン
Taxonomy |
Taxonomy item |
Relationship |
---|---|---|
MISRA C:2012 | Rule 8.2 (required) | Prior to 2018-01-12: CERT: Unspecified Relationship |
MISRA C:2012 | Rule 8.4 (required) | Prior to 2018-01-12: CERT: Unspecified Relationship |
MISRA C:2012 | Rule 8.8 (required) | Prior to 2018-01-12: CERT: Unspecified Relationship |
MISRA C:2012 | Rule 17.3 (mandatory) | Prior to 2018-01-12: CERT: Unspecified Relationship |
参考資料
[Banahan 2003] | Section 8.2, "Declarations, Definitions and Accessibility" |
[ISO/IEC 9899:2011] | 6.2.2, "Linkages of Identifiers" |
[Kirch-Prinz 2002] |
|
翻訳元
これは以下のページを翻訳したものです。
DCL36-C. Do not declare an identifier with conflicting linkage classifications (revision 128)