12

#defineラベルの範囲を制限し、不当なトークンの衝突を回避するための正しい戦略は何ですか?

次の構成では:

Main.c

# include "Utility_1.h"
# include "Utility_2.h"
# include "Utility_3.h"
VOID Main() { ... }

Utility_1.h

# define ZERO "Zero"
# define ONE  "One"
BOOL Utility_1(); // Uses- ZERO:"Zero" & ONE:"One"

Utility_2.h

# define ZERO '0'
# define ONE  '1'
BOOL Utility_2(); // Uses- ZERO:'0' & ONE:'1'

Utility_3.h

const UINT ZERO = 0;
const UINT ONE = 1;
BOOL Utility_3(); // Uses- ZERO:0 & ONE:1

注: Utility _1Utility_2およびUtility_3は独立して記述されています


エラー:マクロの再定義とトークンの衝突
また、最も心配なこと:コンパイラは、トークンの置き換えの場合に何が何を置き換えたかを示しません

{編集}注:これは一般的な質問であるため、提案しないでください。enumconst

すなわち、いつ何をすべきか:私は使用しなければなりません#define&_以下の私の提案された解決策についてコメントしてください..__

4

9 に答える 9

11

#defineマクロには次の 2 種類があります。

  1. 単一のファイルでのみ必要なもの。Private #defines
    それらを例えばと呼びましょう。PI 3.14この場合:

    標準的な慣行によると、正しい戦略は#defineラベルを配置することです-実装のみ、つまり. c、ファイルであり、ヘッダーhファイルではありません。

  2. 複数のファイルで必要とされるもう 1 つのファイル: これらを呼び出しましょうShared #defines
    EXIT_CODE 0x0BADこの場合:

    そのような共通#defineラベルのみをヘッダーhファイルに配置します。

さらに、衝突の可能性が減るようにラベルの前にeg:を付けるなどの慣習または同様の規則を使用して、ラベルに一意の名前を付けるようにしてください。False NameSpacesMACRO_#define MACRO_PI 3.14

于 2012-09-28T19:11:52.333 に答える
11

正しい戦略は、使用しないことです

#define ZERO '0'
#define ONE  '1'

まったく。定数値が必要な場合はconst char、名前空間にラップされた代わりに、この場合は a を使用します。

于 2012-09-28T13:38:42.360 に答える
8

#defines には C++ コードに対応するスコープがありません。制限することはできません。これらは単純なテキスト置換マクロです。「テキストを grep に置き換えるときにスコープを制限するにはどうすればよいですか?」という質問を想像してみてください。

可能な限りそれらを避け、代わりに実際の C++ 型付けを使用することをお勧めします。

マクロを適切に使用すると、命名規則によって、この問題はほとんど自然に解消されます。マクロにオブジェクトのような名前が付けられている場合、それはオブジェクトであるべきです(マクロではありません)。問題が解決しました。マクロの名前が関数のように付けられている場合 (動詞など)、関数である必要あります。

これは、リテラル値、変数、式、ステートメントに適用されます...これらはすべてマクロであってはなりません。そして、これらはあなたを噛むことができる場所です.

ある種の構文ヘルパーのようなものを使用している場合、マクロ名が他の命名規則に適合しないことはほぼ確実です。それで、問題はほとんどなくなりました。しかし、最も重要なことは、マクロである必要があるマクロは、名前が衝突したときにコンパイル エラーを引き起こすことです。

于 2012-09-28T13:40:24.170 に答える
7

#define の範囲を制限し、不当なトークンの衝突を回避するための正しい戦略は何ですか?

  1. 本当に必要でない限り、マクロは避けてください。C++ では、通常、代わりに定数変数とインライン関数を使用できます。これらには、型付けされているという利点があり、名前空間、クラス、またはコード ブロック内でスコープを設定できます。C では、マクロがより頻繁に必要になりますが、導入する前に代替手段についてよく考えてください。

  2. どのシンボルがマクロで、どのシンボルが言語レベルの識別子であるかを明確にする命名規則を使用してください。ALL_CAPITALSマクロ専用に名前を予約するのが一般的です。これを行うと、マクロは他のマクロとしか衝突できなくなります。これにより、バグが潜んでいる可能性が高いコードの部分にも注意が向けられます。

  3. 各マクロ名に「疑似名前空間」プレフィックスを含めて、異なるライブラリ/モジュール/その他のマクロ、および異なる目的のマクロが衝突する可能性を低くします。したがって、数字のゼロの文字定数を定義したい危険なライブラリを設計している場合は、 のような名前を付けてDODGY_DIGIT_ZEROください。ただZERO多くのことを意味する可能性があり、別の危険なライブラリによって定義されたゼロ値の定数と衝突する可能性があります。

于 2012-09-28T13:51:31.350 に答える
7

いくつかのオプション:

  1. マクロと通常の識別子には、異なる大文字表記規則を使用してください。

    const UINT ゼロ = 0;

  2. モジュール名をマクロの先頭に追加して名前空間を偽装します。

    #定義 UTIL_ZERO '0'
     #UTIL_ONE '1' を定義

  3. 利用可能な場合 (C++)、マクロを完全に捨てて、実際の名前空間を使用します。

    名前空間ユーティリティ {
         const char ZERO = '0';
         const char ONE = '1';
     };

于 2012-09-28T13:54:38.220 に答える
1

#define の範囲を制限し、不当なトークンの衝突を回避するための正しい戦略は何ですか?

いくつかの簡単なルール:

  1. プリプロセッサ トークンの使用を最小限に抑えます。
    一部の組織は、この道をたどり、プリプロセッサ シンボルを#includeガードのみに制限しています。ここまではいきませんが、プリプロセッサ シンボルを最小限に抑えることをお勧めします。
    • 名前付き整数定数ではなく列挙型を使用します。
    • const static名前付き浮動小数点定数ではなく変数を使用します。
    • マクロ関数ではなくインライン関数を使用します。
    • #defined 型名ではなく、typedef を使用します。
  2. 衝突を防ぐ命名規則を採用します。
    例えば、
    • プリプロセッサ シンボルの名前は、大文字とアンダースコアのみで構成する必要があります。
    • 大文字とアンダースコアのみで構成される名前を他の種類の記号に付けることはできません。

const UINT ZERO = 0; // Programmer not aware of what's inside Utility.h

まず最初に、プログラマーが Utility.h の内容を理解していないのであれば、なぜプログラマーはその#includeステートメントを使用したのでしょうか? 明らかにそれUINTはどこかから来ました...

第二に、プログラマーは変数に名前を付けてトラブルを求めていますZERO。プリプロセッサ シンボルのキャップ名はすべてそのままにしておきます。ルールに従えば、Utility.h の中身を知る必要はありません。Utility.h がルールに従っていると仮定します。その変数の名前を作成しますzero

于 2012-09-28T13:53:52.317 に答える
0

何が含まれているのかを本当に知っておく必要があると思います。これは、windows.h をインクルードしてから、WM_KEYDOWN という名前の変数を宣言しようとするようなものです。衝突が発生した場合は、変数の名前を変更するか、(ややハックのように) #undef する必要があります。

于 2012-09-28T13:41:12.873 に答える
-2

C は構造化プログラミング言語です。それには限界があります。それこそが、オブジェクト指向システムが 1 位になった理由です。C では、ヘッダー ファイルの変数が _VARIABLE 表記で始まることを理解する方法が他にないようです。これにより、上書きされる可能性が少なくなります。

in header file 
_ZERO 0

in regular file

ZERO 0
于 2012-09-28T17:18:32.863 に答える
-3
  1. 正しい戦略は、ラベルを配置することだと思います#define-実装のみ、つまり。c、ファイル
  2. さらに、すべて#defineを別々にさらに別のファイルに入れることができます-たとえば、 (Microsoftの:Win32 api関数のエラーコード定義とUtility_2_Def.h
    まったく同じです) WinError.h

    オーバーヘッド:

    1. 余分なファイル
    2. 追加の#includeステートメント

    利益:

    1. 抽象化:ZEROは:0'0'または"Zero"どこで使用するか
    2. モジュール全体のすべての静的パラメータを変更するための1つの標準的な場所

Utility_2.h

BOOL Utility_2();

Utility_2_Def.h

# define ZERO '0'
# define ONE  '1'

Utility_2.c

# include "Utility_2.h"
# include "Utility_2_Def.h"

BOOL Utility_2()
{
    ...
}
于 2012-09-28T13:36:33.893 に答える