c/c++マクロ定義##コネクタと#記号の使用

4972 ワード

C言語でのマクロC(およびC++)の使用方法のマクロ(Macro)は、コンパイラの前処理の範疇に属し、実行期間の概念ではなくコンパイル期間の概念に属する.以下、よくあるマクロの使用問題について簡単にまとめた.C言語のマクロでは、#の機能は、その後ろのマクロパラメータを文字列化することである(Stringfication)とは、簡単に言えば、参照するマクロ変数を置換した後、左右に二重引用符を付けます.たとえば、次のコードのマクロ:#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);これにより、divider(除数)が0のたびに標準エラーストリームにプロンプト情報が出力されます.(concatenator)は、2つのTokenを1つのTokenに接続するために使用されます.ここで接続されているオブジェクトは、マクロの変数ではなくTokenであることに注意してください.たとえば、メニュー項目のコマンド名と関数ポインタからなる構造体の配列を作成し、関数名とメニュー項目のコマンド名の間に直感的で名前的な関係を持つことを望んでいます.次のコードは非常に実用的です.struct command{char * name;void (*function) (void);};#define COMMAND(NAME){NAME,NAME##_command}//そして、予め定義されたコマンドでcommand構造の配列を容易に初期化します.struct command commands[]={COMMAND(quit)、COMMAND(help),...}COMMANDマクロは、ここでコードジェネレータとして機能し、コード密度をある程度低減し、間接的に不注意によるエラーを低減することができる.n個の##記号をn+1個のTokenに接続することもでき、この特性も#記号には備わっていない.例:#define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##dtypedef struct _record_type LINK_MULTIPLE(name,company,position,salary);//ここでこの文は//typedef struct_に展開されます.record_type name_company_position_salary;についての使用...CマクロではVariadic Macro,すなわち変参マクロと呼ぶ.例:#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,“Error!/n”);ここでもコンパイルエラーが発生することは明らかです(この例ではない場合、コンパイルエラーは発生しません).この方法に加えて、c 99およびGNU CPPは、次のマクロ定義方法をサポートしています:#define myprintf(templt,...)fprintf(stderr,templt, ##__VAR_ARGS__)このとき、この接続記号が果たす役割は、_VAR_ARGS__空の場合は、前のカンマを消します.では、このときの翻訳過程は以下の通りです.myprintf(templt);変換:fprintf(stderr,templt);これによりtempltが合法であれば、コンパイルエラーは発生しません.ここでは、マクロの使用中にエラーが発生しやすい場所と、適切な使用方法を示します.誤ったネスト-Misnestingマクロの定義には、必ずしも完全なカッコが必要ではありませんが、エラーを回避し、可読性を向上させるためには、このような使用は避けたほうがいいです.オペレータ優先度に起因する問題-Operator Precedence Problemマクロは単純な置換であるため、マクロのパラメータが複合構造である場合、置換後、各パラメータ間のオペレータ優先度が単一パラメータ内部の各部分間で相互作用するオペレータ優先度よりも高い可能性があり、各マクロパラメータをカッコで保護しない場合、予期せぬ事態が発生する可能性があります.例:#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)-(y)-(y)/(y))余分なセミコロンを消す-Semicolon Swallowing通常、関数模様のマクロを表面的に通常の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{/*line 1*/*line 2*/*line 3*/}while(0)は、常にセミコロンを使用することを保証すれば、何の問題もありません.Duplication of Side EffectsここでのSide Effectとは、マクロが展開されている間にそのパラメータに対して複数回Evaluation(つまり値をとる)を行うことがあるが、このマクロパラメータが1つの関数であれば、複数回呼び出されて不一致の結果に達する可能性があり、さらに深刻なエラーが発生する可能性があることを意味する.例えば、#define min(X,Y)>(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_例は次のとおりです.
 
 1 #include <stdio.h>

 2 

 3 #define LINK_MULTIPLE(a) int a##_##index;    \

 4 int print_##a(){ a##_##index = 123; return a#_##index;}

 5 

 6 int main()

 7 {

 8     LINK_MULTIPLE(A);

 9 

10     printf("value = %d
", print_A() ); 11 return 0; 12 }