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)



