みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

マクロの展開(C言語)について

#define WRITE_test(TEST) \
do{ \
fprintf(stderr, "%s can't open\n", #TEST); \
}while(0);

というマクロを書いた上で、ソースをコンパイルすると

/**/
if (length_max/*LENGTH_buf_mpi*/ > length+strlen(buf_))
do{
fprintf(stderr, "%s can't open\n", "test");
}while(0);
else if (length_max/*LENGTH_buf_mpi*/ > length+strlen(buf_))
do{
fprintf(stderr, "%s can't open\n", "test");
}while(0);
else
break;

とある箇所にエラーなどは現れず

/**/
if (length_max/*LENGTH_buf_mpi*/ > length+strlen(buf_))
WRITE_test(test);
else if (length_max/*LENGTH_buf_mpi*/ > length+strlen(buf_))
WRITE_test(test);
else
break;

とある箇所で、
「buf_.c:263: error: 構文解析エラー が "else" の前にあります」
というメッセージが現れます。263行とはひとつめのWRITE_test()がある箇所です。

 マクロはテキストが置換、展開されるものと考えると、この結果に納得できないでおります。説明がつくでしょうか?
 コンパイルはgentoo Linux上で/usr/bin/gcc-3.4.4によるものです。

投稿日時 - 2007-05-02 02:58:39

QNo.2966744

GT-

困ってます

質問者が選んだベストアンサー

★原因はセミコロンをマクロ関数につけているからだよ。
if (length_max/*LENGTH_buf_mpi*/ > length+strlen(buf_))
 WRITE_test(test);
else if (length_max/*LENGTH_buf_mpi*/ > length+strlen(buf_))
 WRITE_test(test);
else
 break;
↑上記を展開します↓

if (length_max/*LENGTH_buf_mpi*/ > length+strlen(buf_))
 do {
  fprintf(stderr, "%s can't open\n", #TEST);
 } while(0);;
else if (length_max/*LENGTH_buf_mpi*/ > length+strlen(buf_))
 do {
  fprintf(stderr, "%s can't open\n", #TEST);
 } while(0);;
else
 break;
となりますよね。

すると『while(0);;』でセミコロンが2つになります。
それで if 文に{ }のブロックがありませんので、次のように解釈されます。

if ( … ) do { … } while(0);
;
else if ( … ) do { … } while(0);
;
else break

普通に考えて
if ( … ) 次の1文
;
else break

『else』の前にセミコロンつけるとエラーになりますよ。

よって、
#define WRITE_test(TEST) \
do{ \
fprintf(stderr, "%s can't open\n", #TEST); \
}while(0) ←セミコロンを取ればよい。

その他
・このような場合の為に if、else の{ }のブロックは省略しない方がよいのです。
 もしも、if、else に{ }のブロックをつけて記述すれば、マクロにセミコロンが
 あっても正しく処理されます。
・以上。マクロ関数にセミコロンをつける場合は注意して下さい。

投稿日時 - 2007-05-02 03:17:52

お礼

 一行目でヴィヴィッドに説明が伝わりました。ありがとうございます。たまたまこれまでelse以下をつけてこなかったので気づきませんでした。

 if, elseの{}を省略しないことにするのはよさそうですけれど、この場合マクロのラスト、while(0)の後ろにセミコロンをつけない形にしておいて、ソースの中でマクロを使用する際には一般の関数に似せてセミコロンをつけることにするのがいいかなと考えました。

 ご丁寧な説明、ありがとうございました。

投稿日時 - 2007-05-02 03:48:02

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(5)

ANo.5

★もしかして、複数の処理を1文で記述できるように do-while を使っているとか。
・昔、持っていた本に載っていたような気がした。
 それで『do { 複数の処理 } while(0)』とマクロで定義しているのね。
 私はif、else に{ }文字を省略しないのでマクロ関数に{ }文字のブロックで
 囲んで定義することがあります。
 
 #define MacroName( a, b )\
 {\
  宣言など\
  \
  /* コメント */\
  処理1\
  処理2\
   :
  処理n\
 }\ ←私は、ここにも¥文字を付ける
 ←上に¥を付けているので必ず1行以上空行を空けて定義する。

その他:
・ここでは連続するタブ文字とスペースが1個になるので全角の空白を使って
 インデントなどを表現できます。
・以上。参考に!

投稿日時 - 2007-05-02 22:39:54

お礼

 いくつか流儀があるのですね。
 自分が参考になったところで大きく共通しているのは、{ }(do()while(0)を含めて)をつかうことでマクロの中で関数ライクに複数の処理を行える点、{ }内で各種の宣言が可能である点です。そこそこ本を読みながらプログラムを書いているつもりなのですけれど、このあたりのことは記憶に残る箇所に明らかにはされていませんでした。
 このあたりをもっと早く知っていたら、デバッグの作業も数値計算上の確認もたいへん楽になっていたのに。ここのところで一番ためになった箇所です。

 全角の空白をつかうことには違和感を感じるのですけれど、この場でこだわることはないですね。こちらもたいへん参考になりました。ありがとうございました。

投稿日時 - 2007-05-03 01:22:08

ANo.4

その (do ... while (0) という) 形のマクロを自分で思い付いたんだったらすごいけど....
その形のマクロでは最後にセミコロンを付けないのが普通.
まあ, 1行なのに do ... while(0) ってのも冗長だけど.

投稿日時 - 2007-05-02 21:38:03

お礼

 見かけたことがあって、真似てみました。セミコロンをたぶんそこではつけていなかったのだと思います。細かいところまで把握し切れていなかったのですね。

 こういうコツは、みなさんどこで習得するのでしょう?はじめて目にしたときはびっくりしました。おもに買いだめしたC言語の本とweb上の情報をもとに試行錯誤しているのですけれど、手元の資料にはこういう工夫は載っていません。
 回答を締め切ったつもりでできていなかったのですけれど、この件を書き込んでしまうのでもう少し締め切るのを延長させてください。

 1行であるのは、書いたマクロを本来の形からどんどん削っていって、それでもエラーが残って悩んでしまった結果です。

# トピックを外れていろいろ書くのはよろしくないとは思いますけれど、新しく書き込むほどのことはなさそうなのでもうひとつ。
 投稿した後の画面では半角の空白やタブが消えてしまっていました。これにはどう対処すればよろしいのでしょうか?

投稿日時 - 2007-05-02 22:18:16

ANo.3

# 単純にコンパイルエラーが出てるだけで、表示行数の話ではないのね…orz
# 完全に読み違えてました。ごめんなさい。

投稿日時 - 2007-05-02 03:36:18

お礼

 いえ、こちらも不慣れなもので、きちんと伝わる形で質問できていなかったように思います。
 はじめて知る話も紹介いただいたわけで、調べて勉強にします。ありがとうございました。

投稿日時 - 2007-05-02 03:50:45

ANo.1

gccの実装/仕様を確認したわけではないので憶測ですが…、

そのエラーメッセージは__LINE__に基づいてたり、
複数行に同じ__LINE__が割り当てられてたりはしないのでしょうか。

投稿日時 - 2007-05-02 03:13:36

あなたにオススメの質問