ARR31-C. すべてのソースファイルで一貫した配列表記を用いる
複数のファイルやコンパイル単位で一貫した表記を用いて変数(配列を含む)宣言を行うことは必要不可欠である。一貫性がないと未定義の動作が発生してしまう(C 言語規格 [ISO/IEC 9899:2011] 附属書 J 「未定義の動作」の 15 を参照)。
同じファイルの中であっても、配列は関数に引数として渡される際にポインタに変換される。つまり、次の2つの関数プロトタイプは同じである。
void func(char *a);
および
void func(char a[]);
しかし関数プロトタイプの外で、あるファイルで配列をポインタ表記法を用いて宣言し、別のファイルで配列表記法を用いて宣言した場合、これらは同じではない。
違反コード
変数 a
はファイル main.c
において char
へのポインタとして宣言されている。配列の記憶領域が割り当てられ、insert_a()
関数が呼び出されている。
/* main.c ソースファイル */ #include <stdlib.h> enum { ARRAYSIZE = 100 }; char *a; extern void insert_a(void); int main(void) { a = (char *)malloc(ARRAYSIZE); if (a == NULL) { /* 割当てエラーの処理 */ } insert_a(); return 0; }
一方、ファイル insert_a.c
では、同じ識別子がサイズ指定なしの char
型配列として宣言されている。C では、この宣言は不完全型とみなされ、記憶領域は別の場所で宣言されることになる。a
の定義が一貫していないため、a[0]
への代入は未定義の動作となる。
/* insert_a.c ソースファイル */ char a[]; void insert_a(void) { a[0] = 'a'; }
適合コード
先述のコードを修正するには、両方のファイルで一貫した表記法を使用する。これを実現する最もよい方法は、変数の定義を 1 つのソースファイルで行い、ヘッダファイルで変数を extern
宣言しておき、必要に応じてヘッダをインクルードすることである。この作法により、複数の衝突する宣言を行う可能性を排除し、コードの意図を明確に示すことができる。これは、特にコードを保守するときに重要になる。コードの保守時には、ある宣言を修正しても、ほかの宣言を修正し忘れるかもしれない。
下記の解決法ではコードを3つのファイルに分割している。インクルードファイル insert_a.h
では、insert_a()
関数と変数 a
を宣言している。
/* insert_a.h インクルードファイル */ #ifndef INSERT_A_H #define INSERT_A_H enum { ARRAYSIZE = 100 }; extern char *a; void insert_a(void); #endif
ヘッダファイル insert_a.h
は、ソースファイル insert_a.c
にインクルードされている。insert_a.c
では insert_a()
を定義している。
/* insert_a.c ソースファイル */ #include "insert_a.h" char *a; void insert_a(void) { a[0] = 'a'; }
ソースファイル main.c
では、関数 insert_a()
と変数 a
を定義するヘッダファイル insert_a.h
をインクルードしている。
/* main.c ソースファイル */ #include <stdlib.h> #include "insert_a.h" int main(void) { a = (char *)malloc(ARRAYSIZE); if (a == NULL) { /* 割り当てエラーの処理 */ } insert_a(); return 0; }
リスク評価
複数のソースファイルで異なる配列表記法を使用すると、結果としてシステムメモリが上書きされる可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
ARR31-C |
低 |
低 |
中 |
P2 |
L3 |
自動検出
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
V. 8.5.4 |
1 X |
実装済み | |
PRQA QA-C | 8.1 | 1510 | 実装済み |
関連するガイドライン
CERT C++ Secure Coding Standard | ARR31-CPP. Use consistent array notation across all source files |
ISO/IEC TS 17961 | Declaring the same function or object in incompatible ways [funcdecl] |
参考資料
[ISO/IEC 9899:2011] | Annex J, subclause J.2, "Undefined Behavior" |
[Hatton 1995] | Section 2.8.3 |
翻訳元
これは以下のページを翻訳したものです。
ARR31-C. Use consistent array notation across all source files (revision 87)