web-dev-qa-db-ja.com

Cライブラリが同じ名前のマクロと関数を使用するのはなぜですか?

PJ Plaugerによる「The Standard C Library」を読んでいます。この本では、ライブラリの使用方法だけでなく、ライブラリの実装方法についても説明しています。

ctype.hセクションを読み終えました。ヘッダーでは、関数はマクロと関数の両方として宣言されています。例えば

int isdigit(int);

だけでなく

#define isdigit(c) (_Ctype[(int)(c)] & _DI)

両方を使用する理由がわかりません。

また、独自のカスタムctypeヘッダーと実装を再作成しようとした場合、マクロを削除しないと正常にコンパイルできません(defineをコメント化)。

この側面は本では実際には説明されていません。誰かが説明できますか?

20
user619818

マクロは関数呼び出しを含まないため、(推定的に)より効率的です。ポインターオフセットのルックアップが含まれるだけなので、より簡単に最適化できます。

関数呼び出しにより、プログラムがマクロ定義なしでコンパイルされた場合でも、別のヘッダーでコンパイルされた場合でも、ソースファイル内の不正な宣言でコンパイルされた場合でも、同じライブラリに対してリンクできます。たとえば、誰かがctype.hの「改良された」バージョンを備え、マクロを持たないコンパイラがある場合でも、関数は使用時に実行時に存在します。

標準を見ると:

c99

7.1.4ライブラリ関数の使用

ヘッダーで宣言された関数は、ヘッダーで定義された関数のようなマクロとして追加で実装できます。そのため、ヘッダーが含まれているときにライブラリ関数が明示的に宣言されている場合、以下に示す手法のいずれかを使用して、宣言が行われないようにすることができます。このようなマクロの影響を受けます。マクロ関数名の展開を示す左括弧が名前の後に続かないため、関数の名前を括弧で囲むことにより、関数のマクロ定義をローカルで抑制することができます。同じ構文上の理由から、ライブラリ関数がマクロとして定義されている場合でも、ライブラリ関数のアドレスを取得することが許可されています。

つまり、次のように記述した場合、

int b = (isdigit)(c);

または

int (*f)(int) = &isdigit;
int b = f(c);

次に、マクロではなく実際の関数を呼び出します。法的に書くこともできます:

#undef isdigit
int b = isdigit(c);

または(#include <ctype.h>が直接または推移的にないソースファイル内):

extern int isdigit(int);
int b = isdigit(c);
23
ecatmur