C / C ++学習日記:C言語マクロ定義##コネクタと#シンボルの使用

はじめに:C言語でマクロを使用する方法C(およびC ++)のマクロは、コンパイラの前処理のカテゴリに属し、(ランタイムの概念ではなく)コンパイル時間の概念に属します。以下は、頻繁に発生するマクロ使用の問題の概要です。

#と##について

 

C言語マクロでは、#の機能は、それに続くマクロパラメータを文字列化することです。簡単に言えば、参照するマクロ変数を置き換えた後、その左側と右側に二重引用符を追加ます。たとえば、次のコードのマクロ:

#define WARN_IF(EXP)    do{ if (EXP)    fprintf(stderr, "Warning: " #EXP "\n"); }  while(0)

次に、実際の使用で次の交換プロセスが表示されます。

WARN_IF(divider == 0);被払い换是do {if(divider == 0)fprintf(stderr、 "Warning" "divider == 0" "\ n");} while(0);

このようにして、除算器(除算器)が0になるたびに、プロンプトメッセージが標準エラーストリームに出力されます。

また、##は連結子と呼ばれ、2つのトークンを1つのトークンに接続するために使用されます。ここで接続されているオブジェクトはトークンであり、必ずしもマクロ変数はないことに注意してくださいたとえば、メニュー項目のコマンド名と関数ポインターで構成される構造の配列を作成し、関数名とメニュー項目のコマンド名の間に直感的な名前の関係があることを期待します。次に、次のコードは非常に実用的です。

struct command

{

char * name;

void (*function) (void);

};

#define COMMAND(NAME) { NAME, NAME ## _command }

// 然后你就用一些预先定义好的命令来方便的初始化一个command结构的数组了:

struct command commands[] = {

COMMAND(quit),

COMMAND(help),

...

}

COMMANDマクロは、ここではコードジェネレーターとして機能し、コード密度をある程度減らすことができます。また、不注意によって引き起こされるエラーを間接的に減らすこともできます。n +1トークンをn ##シンボルで接続することもできますが、これは#シンボルでは使用できません。といった:

#define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d

typedef struct _record_type LINK_MULTIPLE(name,company,position,salary);

// 这里这个语句将展开为:

// typedef struct _record_type name_company_position_salary;

の使用について

...可変パラメータマクロであるCマクロではVariadicMacroと呼ばれます。といった:

#define myprintf(templt、...)fprintf(stderr、templt、__ VA_ARGS__)

//または

#define myprintf(templt、args ...)fprintf(stderr、templt、args)

最初のマクロでは変数パラメーターに名前が付けられていなかったため、デフォルトのマクロ__VA_ARGS__に置き換えました。2番目のマクロでは、変数パラメーターにargsという明示的な名前を付けたため、argsを使用してマクロ定義の変数パラメーターを参照できます。C言語のstdcallと同様に、変数パラメーターはパラメーターリストの最後の項目として表示される必要があります。上記のマクロで最初のパラメーターtempltしか提供できない場合、C標準では次のように記述します。

myprintf(templt、);

形。この時点での交換プロセスは次のとおりです。

 

myprintf( "Error!\ n"、);次のように置き換えます: fprintf(stderr、 "Error!\ n"、);

これは構文エラーであり、正常にコンパイルできません。この問題には一般に2つの解決策があります。まず、GNU CPPが提供するソリューションでは、上記のマクロ呼び出しを次のように記述できます。

myprintf(templt);

そしてそれは次のように置き換えられます:

fprintf(stderr、 "エラー!\ n"、);

明らかに、コンパイルエラーは引き続き発生します(この例以外の場合、コンパイルエラーは発生しません)。このメソッドに加えて、c99とGNUCPPの両方が次のマクロ定義メソッドをサポートします。

#define myprintf(templt、...)fprintf(stderr、templt、## __ VAR_ARGS__)

現時点では、##リンク記号の役割は、__ VAR_ARGS__が空のときに前のコンマを削除することです。この場合の翻訳プロセスは次のとおりです。

myprintf(templt);は次のように変換されます: fprintf(stderr、templt);

したがって、templtが正当な場合、コンパイルエラーは生成されません。以下は、マクロの使用においてエラーが発生しやすい領域と適切な使用方法のリストです。

間違ったネスティング-ミスネスティング

マクロ定義は完全に一致する括弧を持つ必要はありませんが、エラーを回避し、読みやすさを向上させるために、そのような使用を避けることが最善です。

オペレーターの優先順位によって引き起こされる問題-オペレーターの優先順位の問題

マクロは単なる置換であるため、マクロパラメータが複合構造である場合、置換後、さまざまなパラメータ間のオペレータの優先度は、そうでない場合、単一のパラメータの内部部分間の相互作用のオペレータの優先度よりも高くなる可能性があります。ブラケットは各マクロパラメータを保護するため、予期しない状況が発生する可能性があります。といった:

 

詳細については、上の写真をご覧になるか、ペンギンサークルにご参加ください。無料のオープンソースプロジェクトやコースが他にもあります。

#define ceil_div(x、y)(x + y-1)/ y

次に

a = ceil_div(b&c、sizeof(int));

に変換されます:

a =(b&c + sizeof(int)-1)/ sizeof(int);

// +/-の優先度は&の優先度よりも高いため、上記の式は次と同等です。

a =(b&(c + sizeof(int)-1))/ sizeof(int);

これは明らかに発信者の本来の意図ではありません。これを回避するには、さらにいくつかの括弧を記述する必要があります。

#define ceil_div(x、y)(((x)+(y)-1)/(y))

不要なセミコロンを排除する-セミコロンを飲み込む

通常、関数のようなマクロを表面上で通常のC言語呼び出しのように見せるために、通常、次のパラメーター付きマクロのように、マクロの後にセミコロンを追加します。

MY_MACRO(x);

しかし、それが次の状況である場合:

#define MY_MACRO(x) { /* line 1 */ /* line 2 */ /* line 3 */ }

//...

if (condition())

MY_MACRO(a);

else

{...}

これにより、余分なセミコロンが原因でコンパイルエラーが発生します。MY_MACRO(x);をこのように記述しながらこの状況を回避するには、マクロを次の形式で定義する必要があります。

#define MY_MACRO(x)do {

/ * 1行目* / / * 2行目* / / * 3行目* /} while(0)

常にセミコロンを使用している限り、問題はありません。

副作用の重複

ここでの副作用とは、マクロが展開されたときにマクロパラメータが複数回(つまり値)評価される可能性があることを意味しますが、マクロパラメータが関数の場合は、一貫性のない結果を達成するために複数回呼び出される可能性があります。より深刻なエラーが発生します。といった:

#define min(X、Y)((X)>(Y)?(Y):( X))

//..。

 

c = min(a、foo(b));

このとき、foo()関数が2回呼び出されます。この潜在的な問題を解決するには、min(X、Y)マクロを次のように記述する必要があります。

#define min(X、Y)({typeof(X)x_ =(X); typeof(Y)y_ =(Y);(x_ <y _)?x_:y_;})

({...})の機能は、いくつかの内部ステートメントの最後の値を返すことです。また、変数を内部で宣言することもできます(中括弧でローカルスコープを形成するため)。

 

おすすめ

転載: blog.csdn.net/weixin_45713725/article/details/109358846