12

このようなコードがどのように機能するかを推論するのは簡単です。

#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含まれていなかったらどうですか?

4

3 に答える 3

8

UB である理由の少なくとも 1 つは、string.hマクロを導入できることです。strcmp内部実装上の理由から、これらのマクロは、「実際の」 strcmp 関数であるという仮定に基づいて記述されている可能性があります。strcmp他のものとして定義してからそれらのマクロを使用すると、 がマクロ内strcmpに展開さmy_strcmpれ、予期しない結果が生じます。

標準は、どのコードが で OK で、何が OK でないかを正確に判断しようとするので...はなく、あなたの悪ふざけを早期に阻止します。

また、標準的なフラットアウトで禁止されているという事実は別として、マクロの再定義になる可能性があることにも注意して#define strcmp my_strcmpください。したがって、一部の準拠する実装では、コードの形式が正しくありません。string.h#define strcmp __strcmp

于 2013-02-08T11:37:22.857 に答える
5

予約済み識別子を宣言または定義するプログラムは、厳密には準拠していませんが (C 2011 4 5)、準拠している可能性があります (C 2011 4 7)。

この疑問が生じた原因となった論争は、予約済み識別子の宣言または定義が C によって定義されていない動作であるかどうかではなく、特定の C 実装のドキュメントなど、他の手段によって動作が定義される可能性があるかどうかに関するものでした。プログラムの作成者はそれを行うことができました。

一部の人々は、「未定義の動作」を「あなたはこれを行ってはならない」という意味として扱います。これは、「未定義の動作」の誤った解釈です。未定義の動作は、標準で回避する必要があるものではありません。これは、C 標準では役に立たないことです。

C 標準では、未定義の動作に要件を課さないことが明示的に述べられています。特に、これは、それを行ってはならないという要件や、別の仕様で動作が定義されていないという要件がないことを意味します。ほとんどすべての実用的なプログラムは、オペレーティング システムのドキュメントで定義されているシステム コールやライブラリのドキュメントで定義されているライブラリ呼び出しを行うとき、または設計されている特定の C 実装で定義されているデータ型の形式に依存するときに、C 標準で定義されていない動作を使用します。為に。

C では、「未定義の動作」は、C 標準によって設定されたルールの終わりにすぎません。他の手段を使用して移動できるオープン フィールドであり、進行を妨げる壁ではありません。

于 2013-02-08T16:45:33.533 に答える
0

はい、未定義の動作です。それが機能する可能性は高いですが、確実に知ることはできません。

この理由は、関数がどのようにstr機能するかについてコンパイラーが組み込みの知識を持てるようにするためだと思います。そしてもちろん、strcmpすでにマクロmemcmp(s1, s2, strlen(s1))かそのようなものである可能性があります [私はそのように言っているのではなく、そうなる可能性があるだけです.

「string.hを含めない」ことが実際に役立つとは思いません。

ソース内で検索して置換することをお勧めしstrcmpますmy_strcmp。いつでも逆方向に移動でき、#define my_strcmp(s1, s2) strcmp(s1, s2)「戻る」場合は を使用できます。

于 2013-02-08T11:24:21.683 に答える