このようなコードがどのように機能するかを推論するのは簡単です。
#include <string.h>
#define strcmp my_strcmp
int my_strcmp(const char *, const char *)
...
strcmp(str1, str2);
...
しかし、この問題は、これが技術的に正しいかどうかです。
C11から:
7.1.3.1(予約名):
..。
- 次の副次句(将来のライブラリの方向を含む)の各マクロ名は、関連するヘッダーのいずれかが含まれている場合に指定されたとおりに使用するために予約されています。特に明記されていない限り(7.1.4を参照)。
- 次の副節(将来のライブラリの方向を含む)およびerrnoのいずれかで外部リンケージを持つすべての識別子は、外部リンケージを持つ識別子として使用するために常に予約されています。184)
- 次の副節(将来のライブラリの方向を含む)のいずれかにリストされているファイルスコープを持つ各識別子は、マクロ名として、および関連するヘッダーのいずれかが含まれている場合は同じ名前空間内のファイルスコープを持つ識別子として使用するために予約されています。
184外部リンケージを持つ予約済み識別子のリストには、math_errhandling、setjmp、va_copy、およびva_endが含まれます。
つまり、含まれてstrcmp
いるので、これは予約語ですstring.h
。
7.1.3.2:
...プログラムが予約されているコンテキスト(7.1.4で許可されている場合を除く)で識別子を宣言または定義する場合、または予約された識別子をマクロ名として定義する場合、動作は未定義です。
strcmp
これは、7.1.4でどういうわけか許可されていることを除いて、再定義は未定義の動作であると言っているようです。
7.1.4の関連する可能性のある内容は次のとおりです。
7.1.4.1:
...ヘッダーで宣言された関数は、ヘッダーで定義された関数のようなマクロとして追加で実装できます。そのため、ヘッダーが含まれているときにライブラリ関数が明示的に宣言されている場合は、以下に示す手法の1つを使用して宣言はそのようなマクロの影響を受けません。関数の名前の後にマクロ関数名の展開を示す左括弧が続かないため、関数の名前を括弧で囲むことにより、関数のマクロ定義をローカルで抑制することができます。同じ構文上の理由から、ライブラリ関数がマクロとしても定義されている場合でも、ライブラリ関数のアドレスを取得することは許可されています。185)#undefを使用してマクロ定義を削除すると、実際の関数が確実に参照されます。..。
185これは、実装が各ライブラリ関数の実際の関数を提供する必要があることを意味します。その関数のマクロも提供する場合でも同様です。
7.1.4.2:
ライブラリ関数は、ヘッダーで定義されたタイプを参照せずに宣言できる場合は、関数を宣言して、関連するヘッダーを含めずに使用することもできます。
残りの条項は関係ありません。関数と同じヘッダー、つまり標準ヘッダーでのライブラリ関数のマクロとしての定義を除いて、7.1.3.2が「7.1.4で許可されている」と呼んでいるものがわかりません。
要約すると、上記のコードは技術的に未定義の動作ですか?string.h
含まれていなかったらどうですか?