C の場合、タグ名だけでは型名になれず、struct, union, enum を付けなければなりません。そのため、使い勝手を向上するために typedef 名を付けることが多いのではないでしょうか? 一方、C++ ではクラスや列挙体のタグ名だけで型名になりますので、そうした typedef 名はあまり使う機会がないかもしれません。
というわけで、C では次のような型定義がよく行われます。
typedef struct _FOO {
...
} FOO;
ところが、このコードの動作は未定義だということをご存知でしょうか? _FOO のように、下線(アンダースコア、アンダーバー)で始まり、下線または大文字が続く識別子は「予約済み識別子」です。予約済み識別子というのは、規格がライブラリに使うか、処理系が作業用または拡張用に使うために予約されている識別子で、それらをユーザープログラムで使用することはできません(使用した場合の動作は未定義です)。
未定義の動作ですから、実行時におかしな振る舞いをするかもしれませんし、コンパイル時にエラーになったり、コンパイラがフリーズしたりするかもしれません。そうはいっても、現実には問題ないだろうと思われるかもしれません。確かに、実際に問題に直面する機会は少ないのですが、多くの場合、この問題に直面するのは異なる環境(同じコンパイラの将来のバージョンを含む)に移植する際なのです。
仮に問題に直面しないとしても、わざわざ未定義の動作を引き起こすような記述をしなければならない理由は一切ありません。ゴキブリを 1 匹発見すると 100 匹はいるといわれますが、ソースコードの中にこのような記述が見つかると、他の箇所にも多数の怪しい箇所が存在すると考えた方がよいでしょう。
いずれにせよ、こうしたコードは百害あって一利無しです。多くの場合、typedef 名を付けるのであればタグ名は不要です。つまり、
typedef struct {
...
} FOO;
上のように記述すれば済むはずです。線形リストなど、自己参照構造体の場合にはタグ名が不可欠ですが、その場合は、
typedef struct FOO {
...
} FOO;
とでもしておけば十分です。タグ名と型名では名前空間が異なりますから、同じ名前を付けても衝突することがありません。C++ ではタグ名と型名の名前空間は分かれていませんが、C との互換性のために、上のような typedef 名を定義しても問題ないことになっています。ですから、C++ との互換性の面でも問題ありません。
予約済み識別子は、タグ名だけでなく、いろいろなところでうっかり使ってしまいがちです。何が予約済み識別子であるかのルールは結構煩雑ですので、下線で始まる識別子は一律使わないことに決めてしまった方が無難です。
ブックナビゲーション
- 技術情報
- Boost C++ Libraries メモ
- C++と組込み環境
- C++サンプル集
- C++テンプレート集
- C++プログラマのためのC言語入門
- C/C++迷信集
- [迷信] 'A'~'Z' の値は連続している
- [迷信] 0xe-0xe はゼロ
- [迷信] 1 バイトは 8 ビット
- [迷信] 2の累乗による割り算と右シフトは等価
- [迷信] FILE 型は構造体
- [迷信] abs は常に非負の値を返す
- [迷信] argv[0] はプログラム名
- [迷信] char 型は符号付き
- [迷信] double の出力書式は "%lf"
- [迷信] fflush で入力バッファをクリア
- [迷信] free でメモリを開放する
- [迷信] free に NULL を渡すとクラッシュする
- [迷信] gets は単純に fgets に置き換えられる
- [迷信] isalpha 関数の引数は char 型
- [迷信] new に失敗すると NULL が返る。
- [迷信] scanf ではバッファオーバーランを防げない
- [迷信] scanf でキーボードから入力
- [迷信] setjmp マクロの返却値は変数に代入できる
- [迷信] sizeof は定数式
- [迷信] void main(void)
- [迷信] とりあえず memset で初期化
- [迷信] アルゴリズム関数内で関数オブジェクトはコピーされない
- [迷信] オブジェクトの動的生成に失敗するとメモリリークする
- [迷信] コンストラクタから例外を送出してはならない
- [迷信] コンストラクタで自身をゼロクリア
- [迷信] コンパイラはプログラマの心を察してくれる
- [迷信] コンパイルエラーが出るのでアクセス指定子を修正
- [迷信] ソースコード中の即値を全廃せよ
- [迷信] ソースファイルの末尾に }
- [迷信] データ列のソートには qsort 関数を使うべし
- [迷信] プログラムは必ず main から始まる
- [迷信] 一重引用符の中には一文字しか書けない
- [迷信] 今どき int が 16 ビットの処理系なんて無い
- [迷信] 入力データ格納用配列のサイズは BUFSIZ
- [迷信] 割付けたメモリはプログラマが自分で解放しなければならない
- [迷信] 実数型とは浮動小数点型のことである
- [迷信] 引用符で囲んだヘッダ名はカレントディレクトリから探索する
- [迷信] 文字列から整数への変換には atoi
- [迷信] 構造体のタグ名は下線で始める
- [迷信] 構造体はクラスではない
- [迷信] 識別子に使える文字は英数字と下線のみ
- [迷信] 非局所オブジェクトは外部結合
- C99関数・マクロ・前処理スクリプト集
- C言語再入門
- C言語徹底入門
- Drupal メモ
- TOPPERS 情報
- ベターCとしてのC++
- マイコン メモ
- ライブラリ開発入門
- 分割コンパイルをきわめる
- 擬似プロセッサを作る
- 象の卵を探して...
- 車輪の再発明
- 過去の情報


命名にちなんで補足的な話を少々。 Bjarne
命名にちなんで補足的な話を少々。
Bjarne StroustrupによるC++のスタイルとテクニックに関するFAQ
http://www.libjingu.jp/trans/bs_faq2-j.html#Hungarian
(マクロ以外に)"すべての文字を大文字にした名前(例: BEGIN_TRANSACTION)は、慣例的にマクロでの 使用に予約されているので、絶対に使用しないでください。"
仕様にはなっていないはずですが、言語作者からすれば、マクロ以外の識別子に小文字のない名前を使用することはタブーです。例え列挙型やconst値でも使ってはいけません。
ご存知だとは思いますが、マクロ同士の衝突では警告が発生しますが、マクロとそれ以外の識別子では衝突が検知されません。上手くコンパイルエラーになってくれれば幸いですが、例えばconst int XXX_LIMIT = 100;というような定数が、#define XXX_LIMIT 50 という形で上書きされると、バッファーオーバーランが発生するまで気づかない可能性もあり非常に危険です。
C++について追記ですが、global
C++について追記ですが、global namespaceにおいて aq_aq で始まる識別子はその後が大文字なのかによらず実装用に予約済みです。
なのでどこかのnamespace下なら問題ないでしょうが、using文が存在するのでusing ::boost::lambda::_1;のような使い方は未定義動作となります。
やはり使わないのが無難ですね。
typo
いつも興味深く拝読させていただいています。
先頭から2つ目の文、typoでしょうか。
typede名 → typedef名
Re:typo
ご指摘ありがとうございます。修正しておきました。