2

ここで、ちょっとした好奇心が働いています。危険なことに取り組んでいる間、さまざまなコンパイラとそれに関連する標準ライブラリの実装について考えるようになりました。以下、私の考えの進行です。

  1. 一部のクラスの識別子は、C++ および C での実装用に予約されています。

  2. コンパイラは、コンパイルの段階 (前処理、コンパイル、リンク) を順番に実行するかのように実行する必要があります。

  3. C プリプロセッサは、識別子の予約状態を認識しません。

  4. したがって、次の場合に限り、プログラムは予約済み識別子を使用できます。

    1. 使用される予約済みの識別子はすべてプリプロセッサ シンボルです。

    2. 前処理の結果には、予約済みの識別子は含まれません。

    3. 識別子は、コンパイラによって事前定義されたシンボルと競合しません (GNUCその他)。

これは有効ですか?ポイント3と4.3については不明です。また、それをテストする方法はありますか?

4

5 に答える 5

6

(質問に対するコメントは、C99 セクション 7.1.3 の意味で予約済みの識別子について話していることを説明しています。つまり、ファイル スコープ内、外部リンケージなどの/^_[A-Z_]/任意の場所に一致する識別子です。あなたが求めていることの...)/^_//^str[a-z]/

コンパイラ (の特定のフェーズ) がそれらの誤用を診断することが期待されているという意味で、それらは予約されていません。むしろ、それらを自分で (誤用) 使用するほど愚かである場合、後でプログラムが動作しなくなったり、コンパイルが停止したりしても、文句を言うことができないという点で予約されています。

危険な量の知識しか持っていない人がシステム ヘッダーの内部を調べて、独自のヘッダー ガードを作成するとどうなるかを見てきました。

#ifndef _MYHEADER_H
#define _MYHEADER_H
// ...
#endif

彼らは未定義の動作を呼び出していますが、これを「エラー: エンドユーザー コードで使用される予約済み識別子」と診断するものは何もありません。代わりに、ほとんどの場合、彼らは幸運で、すべてが順調です。しかし、時折、実装にとって重要な識別子と衝突し、紛らわしいことが起こります。

同様に、次のような名前の外部から見える関数がよくありますstrip()

char *strip(char *s) {
  // remove leading whitespace
  }

C99 の 7.1.3、7.26、および 7.26.11 を読んだところ、これは未定義の動作を引き起こします。しかし、私はこれを気にしないことにしました。識別子は、今日何か悪いことが起こると予想されるという点で予約されているわけではありませんが、標準str-ip()は将来の改訂で新しい標準ルーチンを発明する権利を留保しているためです。そして私は、文字列-ipは、それが何であれ、将来追加される文字列操作の名前としてはありそうもない- と考えることに決めた.それ。技術的には、未定義の動作を呼び出していますが、噛まれるとは思っていません。

最後に、ポイント4の反例:

#include <string.h>
#define memcpy(d,s,n)  (my_crazy_function((n), (s)))
void foo(char *a, char *b) {
  memcpy(a, b, 5);  // intends to invoke my_crazy_function
  memmove(a, b, 5); // standard behaviour expected
}

これは、4.1、4.2、4.3 に準拠しています (最後の 1 つの意図を理解している場合)。ただし、memmoveを で記述したマクロ (7.1.4/1 経由) として追加実装memcpyすると、問題が発生します。

于 2010-09-28T21:09:24.747 に答える
2

C プリプロセッサは、識別子の予約状態を認識しません。

「認識している」とはどういう意味かわかりませんが、必ずしもこれを想定できるとは思いません-7.1.3は言います

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

プリプロセッサ (またはコンパイラ) の実装は、これらの予約済み識別子を目的に応じて使用できます。これらの識別子を誤用しても警告する必要はありません。

「プログラムは、標準(たとえば、事前定義されたマクロのセット)または実装がそのドキュメントでそう述べている場合にのみ、予約済み識別子を使用することができる」ことをお勧めします。

もちろん、かなりの数のケースで予約されている識別子を使用して問題を解決できると思います-実装が邪魔になって問題を引き起こすことはありません。非常に多くのコードが予約済みの名前を使用しており、十分な理由がない限り、実装はそのコードを壊したくないと思います。ただし、コンパイラ ツールチェーンを実装していない場合は、その名前空間を完全に回避することをお勧めします。

于 2010-09-28T20:28:55.713 に答える
2

少なくともif and only ifについては、話はそれよりも複雑だと思います。C99から思い出したこと:

例 3. は false です。トークンは前処理フェーズでも予約されており、definedなどの疑似マクロも再定義できません。__LINE____func__

次に、識別子の予約はスコープに依存します。

  • 一部の識別子は、外部シンボル用に明示的に予約されています setjmp
  • アンダースコアで始まり、別のアンダースコアまたは大文字で始まる識別子は、C のあらゆる場所で予約されています。プリプロセッサであっても、それらに触れてはなりません。
  • アンダースコアと小文字で始まる識別子は、外部シンボルを参照する可能性があるため、ファイル スコープでは禁止されています。関数の範囲内で自由に使用できます。

4.2も完全に正しいわけではありません。まず、次の条件の下で名前としてキーワードを持つマクロを定義することは、未定義の動作(別名非常に悪い) にすぎません。

マクロがキーワードと同じ名前で定義されている間、標準ヘッダーが含まれます (7.1.2)。

次に、展開が再帰的でないことが保証されているため、展開に独自の名前を含むマクロは「安全」です。次のようなものは有効ですが、推奨されません。

#define if(...)                                         \
for(int _i = 0; _i < 1; ++_i)                           \
  for(int _cond = (__VA_ARGS__);                        \
      _i < 1;                                           \
      printf("line %d val %d\n", __LINE__, _cond),      \
        ++_i)                                           \
    if(_cond)

(ところで、誰もそのマクロを使用しないでください。それはコンパイルして、それがどのように見えるかについて実行しますが、それを爆発させるコーナーケースがあります。)

于 2010-09-28T20:24:08.307 に答える
1

識別子は_UNDERSCORE_CAPdouble__underscore適切と思われる実装で使用するために予約されています。たとえば、_File識別子やマクロを に持つなど、実装がそれらを使用する場合は問題ありません。それ<stdio.h>が予約の目的です。ユーザーが使用する場合、これは潜在的な問題です。

したがって、これを診断するために、コンパイラは識別子がどこから来たのかを追跡する必要があります。にないコードをチェックするだけでは十分ではありません。<angle_bracket_files.h>それらは、使用される可能性のあるマクロを定義でき、実装予約語を使用して何かに展開される可能性が高いからです。たとえば、次のようisupperに定義できます。<ctype.h>

#define isupper(x) (_UPPER_BIT & _CHAR_TRAITS[x])

またはそのようなもの。(上記に基づいた定義を見てから長い時間が経ちました。)

したがって、これを追跡するために、プリプロセッサは、とりわけ、そこから来たマクロに関するレコードを維持する必要があります。これを追跡すると、プリプロセッサがかなり複雑になり、コンパイラの作成者は対応する利益がないと考えているようです。

于 2010-09-28T21:45:08.903 に答える
0

#define if whileコードを読み取れないようにすることができるかどうかを尋ねている場合は、はい。これは、難読化された C の競合では一般的な方法でした。ただし、これは実際には4.2に反します。

GNUC などの場合、これらは事前定義されていますが、通常は再定義して undef にすることができます。これを行うのはあまり良い考えではありませんが、可能です。もっと興味深いのは、そのような__LINE____FILE__、およびプリプロセッサ シンボルを再定義または未定義にすることです (b/c それらは自動的に変更されます)。

于 2010-09-28T19:42:31.580 に答える