web-dev-qa-db-ja.com

Cで常に定義を括弧で囲む正当な理由はありますか?

明らかに、#defineステートメントには、次のように括弧が必要です。

#define WIDTH 80+20

int a = WIDTH * 2; // expect a==200 but a==120

したがって、たとえそれが1つの数値であっても、常に括弧で囲みます。

#define WIDTH (100)

Cを初めて使用する人から、なぜこれを行うのかと尋ねられたので、1つの数字に括弧が付いていないEdgeケースを見つけようとしました#defineは問題を引き起こしますが、私には考えられません。

そのようなケースは存在しますか?

48
weston

はい。プリプロセッサ連結演算子(##)は、次のような問題を引き起こします。

#define _add_penguin(a) penguin ## a
#define add_penguin(a) _add_penguin(a)

#define WIDTH (100)
#define HEIGHT 200    

add_penguin(HEIGHT) // expands to penguin200
add_penguin(WIDTH)  // error, cannot concatenate penguin and (100) 

文字列化(#)についても同様です。明らかにこれは例外的なケースであり、おそらくWIDTHがどのように使用されるかを考慮しても問題にはなりません。それでも、それはプリプロセッサについて覚えておくべきことです。

(2番目のペンギンの追加が失敗する理由は、C99の前処理ルールの微妙な詳細です-iirc2つの非プレースホルダー前処理トークンに連結するため、失敗します常に単一の前処理トークンを生成する必要があります。ただし、連結が許可されていても、括弧なしの#define!とは異なる結果が得られるため、これは重要ではありません。

他のすべての応答は、実際には数値がアトミックであるため、C++スキャナーの観点から問題ではない場合にのみ正しいものです。ただし、私の質問を読んで、プリプロセッサをさらに拡張しないケースのみを検討する必要があるという兆候はないため、他の応答は、そこに含まれるアドバイスに完全に同意しても、間違っています。

38

時々あなたは現在の警告を念頭に置いてではなく、の警告をもってコードを書かなければならない 次に編集するとき

現在、マクロは単一の整数です。誰かが将来編集することを想像してみてください。彼らはあなたではなく、注意深くない、または急いでいる人だとしましょう。括弧は、変更を括弧に入れるように注意するためにあります。

この種の考え方はCでの良い習慣です。私は一部の人々が "冗長"と感じるようなスタイルでコードを個人的に書いています。このようなこと、特にエラー処理に関しては。冗長性は、将来の編集の保守性と構成可能性のためです。

26
asveikau

Blagovest Buyukliev が言ったように:

定義は単一のトークン(1つのオペランドのみ、演算子なし)で構成され、単一のトークン(100など)は字句解析および解析時に分割できないatomであるため、括弧は不要です。

しかし、マクロに関しては、次のルールをお勧めします。

  1. マクロのような関数は避けてください。Lundinのコメントを参照してください。

マクロのような関数を使用したい場合は、次の2つのルールをよく考慮してください:

  1. マクロの引数には常に角かっこを使用します
  2. マクロ引数を1回だけ使用する

なぜルール1.? (操作の順序を正しく保つため)

#define quad(x) (x*x)
int a = quad(2+3);

次のように展開されます:

int a = (2+3*2+3);

なぜルール2.? (副作用が1回だけ適用されるようにするため)

#define quad(x) (x*x)
int i = 1;
int a = quad(i++);

次のように展開されます:

int a = i++ * i++;
8
Nicoretti

定義が単一のトークン(1つのオペランドのみ、演算子なし)で構成される場合は常に、単一のトークン(100など)は分割できないatomであるので、括弧は不要です。と解析。

8

100は単一のトークンです。かっこが重要なコーナーケースは見つかりません(単一のトークンの場合!)

複数のトークンが関係する場合に問題になる可能性があるため、これは依然として良い習慣のIMOです。

6
NPE

いいえ。#define WIDTH 100は、明確または「驚くべき」拡張をもたらす可能性があります。これは、単一のトークンによって単一のトークンが置き換えられることになるためです。

ご存知のように、単一のトークン(例:WIDTH)が複数のトークン(例:80 + 20)。私が推測できる限り、これはonlyが置換で括弧を使用する原因であり、最初の段落で説明したように、ここでは当てはまりません。

ただし、この技術的な事実は別として、それはまだ良い方法かもしれません。それは習慣を促進し、そのマクロがより複雑なものに変更される場合の注意としても役立ちます。

コードが数値のみを定義する場合、 @ Alexander Gessler は質問に十分に答えます。

しかし、多くのコーディング担当者は、以下の単項演算子に気づきません。

#define TEMPERATURE1M (-1)
#define TEMPERATURE1P (+1)

コードで演算子を使用する#defineを使用する場合、()を囲むと、予期される数値結果と優先順位が保証されます。

#define TEMPERATURE_WITH  (-1)
#define TEMPERATURE_WITHOUT -1

// Consider how these will compile
int w  = 10-TEMPERATURE_WITH;
int wo = 10-TEMPERATURE_WITHOUT;  // May not compile

コードの最後の行は、与えられたC99セマンティックの変更をコンパイルするかもしれません @ Olaf

4
chux

時には正当な理由があります。

単一の数値の場合、正当な理由はありません。

他の場合については、あなたが示したように、正当な理由があります。

一部の人々は、特に注意して、常に括弧を使用することを好みます(@aixが推奨します。私はしませんが、難しい答えはありません)。

3
ugoren

それは確かに害を及ぼすことはなく、それは良い習慣です。ただし、数値計算では(100)100の間に違いはありません。

2
Jack Edmonds