JPCERT コーディネーションセンター

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

自動検出

ツール

バージョン

チェッカー

説明

LDRA tool suite

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)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter