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

DCL37-C. 予約済み識別子の宣言や定義をしない

C 標準のセクション 7.1.3 には次のように記載されている [ISO/IEC 9899:2011]。

下線に続き大文字 1 字又は下線に続きもう一つの下線で始まるすべての識別子は、いかなる使用に対しても常に予約済みとする。

一つの下線で始まるすべての識別子は、通常の名前空間及びタグ名前空間の双方におけるファイル有効範囲をもつ識別としての使用に対して、常に予約済みとする。

7.(今後のライブラリの方針を含む。)で規定する各マクロ名は、それに関連するヘッダのいずれかを取り込んだ場合、この規格で明示的に異なる規定を行わない限り(7.1.4 参照)、規定された使用法に対して予約済みとする。

7.(今後のライブラリの方針を含む。)で規定する外部結合をもつすべての識別子は、外部結合をもつ識別子としての使用に対して常に予約済みとする。

7.(今後のライブラリの方針を含む。)で規定するファイル有効範囲をもつ各識別子は、それに関連するヘッダのいずれかを取り込んだ場合、マクロ名としての使用に対して、及び、同じ名前空間においてファイル有効範囲をもつ識別子としての使用に対して予約済みとする。

さらに 7.31 においても、予約済みとされている識別子が列挙されている。

これ以外に予約済みの識別子はない。(POSIX 標準は、C 標準 によって予約されている識別子の集合を、POSIX 固有の変更可能な集合を含むように拡張していることに注意。Portable Operating System Interface [POSIX®], Base Specifications, Issue 7Section 2.2 「The Compilation Environment」 [IEEE Std 1003.1-2013] を参照。) プログラムが予約済みの識別子を宣言または定義する場合、または予約済みの識別子をマクロ名として定義する場合、その動作は未定義とする。(C 標準の附属書 J 「未定義の動作」の 106 も参照。)

違反コード (インクルードガード)

ヘッダファイルの複数回のインクルードを防ぐインクルードガードを設ける際のマクロに予約済みの名前を使用する、という慣習があるが、これは本ガイドラインに違反する。 (「PRE06-C. ヘッダファイルはインクルードガードで囲む」も参照。) 予約済みの名前を使ったマクロ定義を行うと、C 言語標準ライブラリの実装によっては、ヘッダ中で定義されている予約済みの名前と衝突する可能性がある。また、C 言語標準ライブラリヘッダがインクルードされていなくても、コンパイラが内部的に使用している予約済みの名前と衝突する可能性もある。

#ifndef _MY_HEADER_H_
#define _MY_HEADER_H_

/* <my_header.h>の内容 */

#endif /* _MY_HEADER_H_ */
適合コード (インクルードガード)

次の適合コードでは、インクルードガードに使うマクロ名の先頭や末尾に下線文字を使わないようにしている。

#ifndef MY_HEADER_H
#define MY_HEADER_H

/* <my_header.h>の内容 */

#endif /* MY_HEADER_H */
違反コード (ファイルスコープのオブジェクト)

以下のコード例では、ファイルスコープのオブジェクト _max_limit_limit はどちらも下線で始まる名前になっている。_max_limitstatic と宣言されているため、処理系が定義している名前と衝突することはないように見えるかもしれない。しかし、size_t を定義するためにヘッダ <stddef.h> がインクルードされているため、名前が衝突する可能性はある。(また、規格に適合しているコンパイラは、内部的に予約済みの名前を宣言して使用している可能性もある)。

また、_limit は外部結合であるため、実行時ライブラリの中で同じ名前のシンボルが定義されていると、それがヘッダで宣言されていなくても衝突する可能性がある。ゆえに、識別子に下線から始まる名前を使うのは、ファイルスコープであっても安全ではない。

#include <stddef.h>

static const size_t _max_limit = 1024;
size_t _limit = 100;

unsigned int getValue(unsigned int count) {
  return count < _limit ? count : _limit;
}
適合コード (ファイルスコープのオブジェクト)

次の適合コードでは、ファイルスコープオブジェクトの名前の先頭に下線文字を使っていない。

#include <stddef.h>

static const size_t max_limit = 1024;
size_t limit = 100;

unsigned int getValue(unsigned int count) {
  return count < limit ? count : limit;
}
違反コード (予約済みマクロ)

以下の違反コード例では、C 言語標準ライブラリヘッダ <inttypes.h> をインクルードしている。<inttypes.h> は内部で <stdint.h> をインクルードしているため、名前 SIZE_MAX は、標準ライブラリで size_t の上限を示すために使われているマクロ名と衝突する。また、名前 INTFAST16_LIMIT_MAX は標準ライブラリで定義されている識別子ではないが、INT で始まり _MAX で終わるパターンの識別子は C 標準で予約されている。(C 標準のセクション 7.31.10 を参照。)

#include <inttypes.h>
#include <stdio.h>

static const int_fast16_t INTFAST16_LIMIT_MAX = 12000;

void print_fast16(int_fast16_t val) {
  enum { SIZE_MAX = 80 };
  char buf[SIZE_MAX];
  if (INTFAST16_LIMIT_MAX < val) {
    sprintf(buf, "The value is too large");
  } else {
    snprintf(buf, SIZE_MAX, "The value is %" PRIdFAST16, val);
  }
}
適合コード (予約済みマクロ)

以下の適合コードでは、予約済みの名前の再定義や、予約済みのパターンの識別子の使用を避けている。

#include <inttypes.h>
#include <stdio.h>

static const int_fast16_t MY_INTFAST16_UPPER_LIMIT = 12000;

void print_fast16(int_fast16_t val) {
  enum { BUFSIZE = 80 };
  char buf[BUFSIZE];
  if (MY_INTFAST16_UPPER_LIMIT < val) {
    sprintf(buf, "The value is too large");
  } else {
    snprintf(buf, BUFSIZE, "The value is %" PRIdFAST16, val);
  }
}
違反コード (外部結合をもつ識別子)

以下の違反コード例は、C 言語標準ライブラリ関数の malloc()free() を再定義している。この手法は、UNIX の伝統的な実装の多く(たとえば Dmalloc ライブラリなど)で認められているが、C 標準ではこれは未定義の動作を引き起こす。malloc() の置き換えが可能なシステムであっても、aligned_alloc()calloc()realloc() も一緒に置き換えなければ問題が生じる可能性がある。

#include <stddef.h>

void *malloc(size_t nbytes) {
  void *ptr;
  /* 専用の領域から記憶域を割り当て、ptr を設定 */
  return ptr;
}

void free(void *ptr) {
  /* 記憶域を専用の領域に返却 */
}
適合コード (外部結合をもつ識別子)

より可搬性の高い次の適合コードでは、外部結合をもつ C 言語標準ライブラリ識別子の再定義を避けている。また、メモリ割り当てに関するすべての関数に対応する独自関数を定義している。

#include <stddef.h>

void *my_malloc(size_t nbytes) {
  void *ptr;
  /* 専用の領域から記憶域を割り当て、ptr を設定 */
  return ptr;
}

void *my_aligned_alloc(size_t alignment, size_t size) {
  void *ptr;
  /* 専用の領域から適切なアラインメントの記憶域を割り当て、ptr を設定 */
  return ptr;
}

void *my_calloc(size_t nelems, size_t elsize) {
  void *ptr;
  /* 専用の領域から記憶域を割り当て、ゼロ初期化し、ptr を設定 */
  return ptr;
}

void *my_realloc(void *ptr, size_t nbytes) {
  /* 専用の領域からの再割り当てを行い、ptr を設定 */
  return ptr;
}

void my_free(void *ptr) {
  /* 記憶域を専用の領域に返却 */
}
違反コード (errno)

外部結合をもつ識別子としては、各 C 言語標準ライブラリヘッダで関数として定義されているシンボルに加えて、errnomath_errhandling などがある。C 標準のセクション 7.5 パラグラフ 2 によれば、以下の場合は未定義の動作となる[ISO/IEC 9899:2011]。

実際のオブジェクトにアクセスするために errno のマクロ定義を無効にした場合、またはプログラムが名前 errno をもつ識別子を定義した場合。

undefined behavior 114 を参照のこと。

errno 識別子は、型 int をもつ変更可能な左辺値に展開されるが、オブジェクトの識別子である必要はない。それは関数呼び出し(たとえば、*errno())によって得られる変更可能な左辺値であってもよい。errno をマクロ定義するか、外部結合で宣言される識別子とするかは、未規定である。実際のオブジェクトにアクセスするためにマクロ定義を無効にした場合、またはプログラムが名前 errno をもつ識別子を定義した場合、その動作は未定義である。

レガシーコードは、次のように誤った宣言を含みやすい。

extern int errno;
適合コード (errno)

errno を正しく宣言する方法は、ヘッダ <errno.h> をインクルードすることである。

#include <errno.h>

C 標準に適合する処理系は、ヘッダ <errno.h>errno を宣言する必要がある。いくつかの古い処理系はこのようになっていない。

例外

DCL37-C-EX1: ヘッダで定義されている型を参照せずに宣言できる標準ライブラリ関数については、ヘッダをインクルードする代わりに独自に元の関数宣言と適合する形の関数宣言を行ってもよい。

/* stdlib.h をインクルードしない */
void free(void *);

void func(void *ptr) {
  free(ptr);
}

このコードでは、stdlib.h で提供される関数宣言と同じ形で、free() 関数の宣言を行っている。free() 関数の再定義を行っているわけではない。

DCL37-C-EX2: 他のコンパイラや対応言語モードとの互換性を考慮し、その動作がべき等性のある場合には予約済み識別子のマクロ定義を認める。

/* autoconf などのツールで生成されることがあるコード例 */
#define const const
 
/* コンパイラの独自拡張機能でC標準の機能を実現する例 */
#define inline __inline

DCL37-C-EX3: コンパイラ製作者や標準ライブラリ製作者が、予約済み識別子を使うことは許される。予約済み識別子は、コンパイラの処理のなかで定義されたり、標準ライブラリヘッダのなかで定義されたり、また、標準ライブラリヘッダにインクルードされるヘッダのなかで定義されたりする。以下は、C 標準ライブラリの実装例 glibc のコードである。

/*
  glibc の <stdio.h> で予約済み識別子を宣言している例.
  元のソースコードは以下を参照:
  https://sourceware.org/git/?p=glibc.git;a=blob_plain;f=include/stdio.h;hb=HEAD
*/

#  define __need_size_t
#  include <stddef.h>
/* Generate a unique file name (and possibly open it).  */
extern int __path_search (char *__tmpl, size_t __tmpl_len,
			  const char *__dir, const char *__pfx,
			  int __try_tempdir);
リスク評価

予約済み識別子を使用すると、正しくないプログラム動作が引き起こされる可能性がある。

ルール

深刻度

可能性

修正コスト

優先度

レベル

DCL37-C

P3

L3

自動検出

ツール

バージョン

チェッカー

説明

Astrée
19.04

future-library-use

language-override

language-override-c99

reserved-declaration

reserved-declaration-c99

reserved-identifier

Partially checked
Axivion Bauhaus Suite

6.9.0

CertC-DCL37 Fully implemented. Reserved identifiers, as in DCL37-C-EX3, are configurable.
CodeSonar
5.0p0

LANG.STRUCT.DECL.RESERVED

Declaration of reserved name
Compass/ROSE




Coverity
2017.07

MISRA C 2004 Rule 20.1

MISRA C 2004 Rule 20.2

MISRA C 2012 Rule 21.1

MISRA C 2012 Rule 21.2

実装済み
ECLAIR
1.2
CC2.DCL37 実装済み
Klocwork
2018
MISRA.DEFINE.WRONGNAME.UNDERSCORE
MISRA.STDLIB.WRONGNAME.UNDERSCORE
MISRA.STDLIB.WRONGNAME

LDRA tool suite
9.7.1

86 S, 218 S, 219 S, 580 S, 626 S

実装済み

Parasoft C/C++test 10.4.1

CERT_C-DCL37-a

Do not #define or #undef identifiers with names which start with underscore
Polyspace Bug Finder

R2018a

MISRA C:2012 Rule 21.1

MISRA C:2012 Rule 21.2

#define and #undef shall not be used on a reserved identifier or reserved macro name

A reserved identifier or macro name shall not be declared

PRQA QA-C
9.5

0602, 0603, 4600, 4601, 4602,

4603, 4604, 4605, 4606, 4607,

4608, 4620, 4621, 4622, 4623,

4624, 4640, 4641, 4642, 4643,

4644, 4645


SonarQube C/C++ Plugin
3.11
S978
RuleChecker
19.04

future-library-use

language-override

language-override-c99

reserved-declaration

reserved-declaration-c99

reserved-identifier

Partially checked
関連するガイドライン

(テーブルの説明)

Taxonomy

Taxonomy item

Relationship

CERT C Secure Coding Standard PRE00-C. Prefer inline or static functions to function-like macros Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard PRE06-C. Enclose header files in an include guard Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C Secure Coding Standard PRE31-C. Avoid side effects in arguments to unsafe macros Prior to 2018-01-12: CERT: Unspecified Relationship
CERT C DCL51-CPP. Do not declare or define a reserved identifier Prior to 2018-01-12: CERT: Unspecified Relationship
ISO/IEC TS 17961 Using identifiers that are reserved for the implementation [resident] Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 21.1 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
MISRA C:2012 Rule 21.2 (required) Prior to 2018-01-12: CERT: Unspecified Relationship
参考資料
[IEEE Std 1003.1-2013] Section 2.2, "The Compilation Environment"
[ISO/IEC 9899:2011] 7.1.3, "Reserved Identifiers"
7.31.10, "Integer Types <stdint.h>"
翻訳元

これは以下のページを翻訳したものです。

DCL37-C. Do not declare or define a reserved identifier (revision 126)

Top へ

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