実用C言語テクニック

7365 ワード

C言語は、表現できるものが限られていると思われることが多い.第1レベルの関数やモードマッチングのような高度な機能はありません.しかし、Cは非常に簡単で、非常に有用な文法技術と機能がありますが、あまり知られていません.
指定した初期化
このようにして配列を静的に初期化することは多くの人が知っています.
int fibs[] = {1, 1, 2, 3, 5};

C 99規格は実際には、構造体、連合体、配列などの様々な異なる集合クラスデータを初期化するためのより直感的で簡単な方法をサポートしている.
はいれつ
初期化のために配列の要素を指定できます.これは、特に、マッピング関係の同期更新を1組のdefineに従って維持する必要がある場合に便利です.エラーコードの定義のセットを見てみましょう.
/* Entries may not correspond to actual numbers. Some entries omitted. */
#define EINVAL 1
#define ENOMEM 2
#define EFAULT 3
/* ... */
#define E2BIG  7
#define EBUSY  8
/* ... */
#define ECHILD 12
/* ... */

次に、エラーコードごとにエラー記述の文字列を提供したいと仮定します.配列が最新の定義を保持していることを確認するために、ヘッダファイルが変更されても補完されても、この配列で指定された構文を使用できます.
char *err_strings[] = {
         [0] = "Success",
    [EINVAL] = "Invalid argument",
    [ENOMEM] = "Not enough memory",
    [EFAULT] = "Bad address",
    /* ... */
    [E2BIG ] = "Argument list too long",
    [EBUSY ] = "Device or resource busy",
    /* ... */
    [ECHILD] = "No child processes"
    /* ... */
};

これにより、十分なスペースを静的に割り当てることができ、最大のインデックスが合法であることを保証し、特殊なインデックスを指定した値に初期化し、残りのインデックスを0に初期化することができます.
マクロリスト
Cの慣用的な方法の1つは、名前付きエンティティのリストがあり、それぞれに関数を確立し、それぞれを初期化し、異なるコードモジュールで名前を拡張する必要があるということです.これはMozillaのソースコードでよく使われています.私はその時にこのテクニックを学びました.たとえば、私が去年の夏に働いていたプロジェクトには、コマンドごとにマークされたマクロのリストがあります.次のように動作します.
#define FLAG_LIST(_)                   \
    _(InWorklist)                      \
    _(EmittedAtUses)                   \
    _(LoopInvariant)                   \
    _(Commutative)                     \
    _(Movable)                         \
    _(Lowered)                         \
    _(Guard)

FLAG_を定義しますLISTマクロ、このマクロには_というパラメータがあります.このパラメータ自体はマクロであり、リスト内の各パラメータを呼び出すことができます.実際に使用した例を挙げると、問題をより直感的に説明できるかもしれません.マクロDEFINEを定義したとしますFLAG、例えば:
#define DEFINE_FLAG(flag) flag,
   enum Flag {
       None = 0,
       FLAG_LIST(DEFINE_FLAG)
       Total
   };
#undef DEFINE_FLAG

対FLAG_LIST(DEFINE_FLAG)を拡張すると、次のコードが得られます.
enum Flag {
        None = 0,
        DEFINE_FLAG(InWorklist)
        DEFINE_FLAG(EmittedAtUses)
        DEFINE_FLAG(LoopInvariant)
        DEFINE_FLAG(Commutative)
        DEFINE_FLAG(Movable)
        DEFINE_FLAG(Lowered)
        DEFINE_FLAG(Guard)
        Total
    };

次に、各パラメータについてDEFINE_を拡張するFLAGマクロ、これで私たちはenumを以下のように得ました.
enum Flag {
        None = 0,
        InWorklist,
        EmittedAtUses,
        LoopInvariant,
        Commutative,
        Movable,
        Lowered,
        Guard,
        Total
    };

次に、flagリストをよりよく使用するには、いくつかのアクセス関数を定義する必要があります.
#define FLAG_ACCESSOR(flag) \
bool is##flag() const {\
    return hasFlags(1 << flag);\
}\
void set##flag() {\
    JS_ASSERT(!hasFlags(1 << flag));\
    setFlags(1 << flag);\
}\
void setNot##flag() {\
    JS_ASSERT(hasFlags(1 << flag));\
    removeFlags(1 << flag);\
}
 
FLAG_LIST(FLAG_ACCESSOR)
#undef FLAG_ACCESSOR

一歩一歩その過程を示すのは非常に啓発的で、もしその使用にまだ理解できないならば、gcc-Eの上でいくつかの時間を使うことができます.