50
extern int ether_hostton (__const char *__hostname, struct ether_addr *__addr)
 __THROW;

Linuxボックスの/usr/include/netinet/ether.hで上記の関数定義を見つけました。

const(キーワード)、addr(識別子)、そして最後に__THROWの前にある二重アンダースコアの意味を誰かが説明できますか?

4

4 に答える 4

74

C では、アンダースコアで始まり、その後に大文字または別のアンダースコアが続くシンボルは、実装用に予約されています。C のユーザーは、予約済みシーケンスで始まるシンボルを作成しないでください。C++ では、制限がより厳しくなります。ユーザーは二重アンダースコアを含むシンボルを作成できません。

与えられた:

extern int ether_hostton (__const char *__hostname, struct ether_addr *__addr)
__THROW;

この__const表記法は、このコードが使用されるコンパイラーがプロトタイプ表記法をサポートしているが、C89 標準キーワードを正しく理解していない可能性 (ややありそうもない) を考慮に入れるためにありますconstautoconfマクロは、コンパイラが をサポートしているかどうかを引き続きチェックできますconst。このコードは、そのサポートを持たない壊れたコンパイラで使用される可能性があります。

__hostnameとの使用は__addr、ヘッダーのユーザーであるあなたを保護する手段です。GCC と-Wshadowオプションを使用してコンパイルすると、コンパイラは、ローカル変数がグローバル変数をシャドウするときに警告を発します。関数が のhostname代わりに使用され、__hostnameという関数があった場合hostname()、シャドーイングが発生します。実装用に予約された名前を使用することで、正当なコードと競合することはありません。

を使用__THROWするということは、状況によっては、ある種の「スロー仕様」でコードを宣言できることを意味します。これは標準 C ではありません。それはC++に似ています。__THROWただし、ヘッダーの 1 つ (またはコンパイラ自体)が空に定義されているか、標準 C 構文のコンパイラ固有の拡張が定義されている限り、コードは C コンパイラで使用できます。


C 標準 (ISO 9899:1999) のセクション 7.1.3 には、次のように記載されています。

7.1.3 予約済み識別子

各ヘッダーは、関連する副次節にリストされているすべての識別子を宣言または定義し、オプションで、関連する将来のライブラリ指示の副次節にリストされている識別子と、任意の使用またはファイルスコープ識別子として使用するために常に予約されている識別子を宣言または定義します。

— アンダースコアと大文字または別のアンダースコアで始まるすべての識別子は、常に使用のために予約されています。

— アンダースコアで始まるすべての識別子は、通常の名前空間とタグ名空間の両方で、ファイル スコープの識別子として使用するために常に予約されています。

— 以下の節 (将来のライブラリの指示を含む) のいずれかの各マクロ名は、関連するヘッダーのいずれかが含まれている場合、指定されたとおりに使用するために予約されています。別段の明示的な記載がない限り(7.1.4 を参照)。

— 以下の節のいずれかにある外部リンケージを持つすべての識別子 (将来の図書館の指示を含む) は、常に外部リンケージを持つ識別子として使用するために予約されています。154)

— 次の節のいずれかにリストされているファイル スコープを持つ各識別子 (将来のライブラリの指示を含む) は、マクロ名として、および関連するヘッダーのいずれかが含まれている場合、同じ名前空間内のファイル スコープを持つ識別子として使用するために予約されています。

他の識別子は予約されていません。プログラムが予約されているコンテキストで識別子を宣言または定義する場合 (7.1.4 で許可されている場合を除く)、または予約された識別子をマクロ名として定義する場合、動作は未定義です。

#undefプログラムが上記の最初のグループの識別子のマクロ定義を( で) 削除した場合、動作は未定義です。

脚注 154) 外部リンケージを持つ予約済み識別子のリストには、、、、および errnoが含まれます。math_errhandlingsetjmpva_end


C++ 識別子でのアンダースコアの使用に関する規則は何ですかも参照してください。この回答の冒頭で述べたように、埋め込まれた二重アンダースコアのルールは C++ のみに適用されますが、多くの同じルールが C と C++ の両方に適用されます。


C99 根拠

C99の根拠は次のように述べています。

7.1.3 予約済み識別子

ライブラリ関数をファイルにパックする際に実装者に最大の自由度を与えるために、ライブラリによって定義されたすべての外部識別子は、ホストされた環境で予約されています。つまり、ユーザー関数の仕様が同じであっても、ユーザーが指定した外部名がライブラリ名と一致することはありません。したがって、たとえば、strtodを と同じオブジェクト モジュールで定義してもprintf、リンク時の競合が発生する心配はありません。同様に、何らかの理由で を呼び出すか、または を呼び出す可能性があります。間違った関数が呼び出される心配はありstrtodませprintfprintfstrtod

また、アンダースコアで始まるすべての外部識別子と、アンダースコアで始まり、その後に大文字またはアンダースコアが続く他のすべての識別子も、実装者のために予約されています。これにより、ライブラリが適切にジョブを実行するために必要な、舞台裏の多数の非外部マクロと関数を記述するための名前空間が提供されます。

これらの例外により、標準は、プログラムをある実装から別の実装に移動するときに予期しない衝突が発生する恐れがなく、他のすべての識別子が利用可能であることをプログラマーに保証します5。特に、アンダースコアで始まる内部識別子の名前空間の一部がユーザーに利用可能であることに注意してください。「隠し」名前の使用法を見つけたのは、トランスレータの実装者だけではありません。C は多くの点で非常に移植性の高い言語であるため、「名前空間汚染」の問題は、完全に移植性のあるコードを作成する上での主要な障害の 1 つです。typedefしたがって、標準では、関連付けられたヘッダーが明示的に含まれている場合にのみ、マクロと名前が予約されることが保証されています。

5実装者がこの約束を守るために取るべき予防策のいくつかの議論については、§6.2.1 を参照してください。<time.h>およびで定義された構造体の実装定義のメンバー名は<locale.h>、これらの構造体の他の名前のパターンに従うのではなく、アンダースコアで始まる必要があることにも注意してください。

そして、 §6.2.1 識別子のスコープの論理的根拠の関連部分は次のとおりです。

関数プロトタイプ内の識別子のスコープはその宣言で始まり、その関数の宣言子の最後で終わりますが、このスコープはプリプロセッサによって無視されます。したがって、既存のマクロの名前と同じ名前を持つプロトタイプの識別子は、そのマクロの呼び出しとして扱われます。例えば:

    #define status 23
    void exit(int status);

前処理後のプロトタイプは

   void exit(int 23);

おそらくもっと驚くべきことは、ステータスが定義されている場合に何が起こるかです

   #define status []

次に、結果のプロトタイプは

   void exit(int []);

これは構文的には正しいですが、意味的には意図とはかなり異なります。

実装のヘッダー プロトタイプをそのような誤解から保護するために、実装者はこれらの予期せぬ事態を回避するためにそれらを作成する必要があります。考えられる解決策には、プロトタイプで識別子を使用しないこと、または予約済みの名前空間 (__statusまたは など_Status) で名前を使用することが含まれます。

名前空間のルールとライブラリの実装に関する広範な議論については、PJ Plauger The Standard C Library (1992) も参照してください。この本は、標準のそれ以降のバージョンではなく C90 に言及していますが、その中の実装に関するアドバイスのほとんどは今日まで有効です。

于 2009-09-19T19:28:56.177 に答える
19

二重のアンダースコアが付いた名前は、実装で使用するために予約されています。これは、それらがしばしば内部的であるとしても、必ずしもそれ自体が内部的であることを意味するわけではありません。

アイデアは、で始まる名前を使用することは許可されていない__ため、実装はマクロ展開などの場所で、または構文拡張の名前でそれらを自由に使用できます(たとえば__gcnew、C ++の一部ではありませんが、Microsoftは追加できますC ++ / CLIは、既存のコードint __gcnew;にコンパイルを停止するようなものが含まれていてはならないことを確信しています)。

これらの特定の拡張機能が何を意味するかを知るには、つまり__const、特定のコンパイラ/プラットフォームのドキュメントを参照する必要があります。この特定のケースでは、ドキュメント( http://www.kernel.org/doc/man-pages/online/pages/man3/ether_aton.3.htmlなど)のプロトタイプを関数のインターフェイスと見なす必要があります。実際のヘッダーに表示される装飾__constと装飾は無視してください。__THROW

于 2009-09-19T19:03:54.620 に答える
3

一部のライブラリでは、慣例により、これは特定のシンボルが内部使用のためのものであり、ライブラリのパブリックAPIの一部となることを意図していないことを示しています。

于 2009-09-19T18:35:33.220 に答える
2

__const のアンダースコアは、このキーワードがコンパイラ拡張機能であり、それを使用すると移植性がないことを意味します ( const キーワードは、後のリビジョンで C に追加されたと思います)。__THROW もある種の拡張機能です。gcc が使用されている場合は、何らかの __attribute__(何か) に定義されると思いますが、それについてはわかりませんし、チェックするのが面倒です。__addr は、プログラマーが意味したいことなら何でも意味します。これは単なる名前です。

于 2009-09-19T18:50:04.643 に答える