まだ定義されていない場合にのみマクロを定義するのはなぜですか?


93

Cコードベース全体で、すべてのマクロが次のように定義されているのがわかります。

#ifndef BEEPTRIM_PITCH_RATE_DEGPS
#define BEEPTRIM_PITCH_RATE_DEGPS                   0.2f
#endif

#ifndef BEEPTRIM_ROLL_RATE_DEGPS
#define BEEPTRIM_ROLL_RATE_DEGPS                    0.2f
#endif

#ifndef FORCETRIMRELEASE_HOLD_TIME_MS
#define FORCETRIMRELEASE_HOLD_TIME_MS               1000.0f
#endif

#ifndef TRIMSYSTEM_SHEARPIN_BREAKINGFORCE_LBS
#define TRIMSYSTEM_SHEARPIN_BREAKINGFORCE_LBS       50.0f
#endif

マクロを定義するだけでなく、これらの定義チェックを行う理由は何ですか?

#define BEEPTRIM_PITCH_RATE_DEGPS                   0.2f
#define BEEPTRIM_ROLL_RATE_DEGPS                    0.2f
#define FORCETRIMRELEASE_HOLD_TIME_MS               1000.0f
#define TRIMSYSTEM_SHEARPIN_BREAKINGFORCE_LBS       50.0f

私はこの慣例がウェブのどこにも説明されていないようです。


6
コードのどこかで定数を変更しても、この方法で動作することが保証されています。他の誰かがこれらのマクロの1つを定義した場合、このファイルを解析するときに、プリプロセッサによって上書きされることはありません。
Enzo Ferber

8
これは、WET設計原則の例です。
スターク

例を挙げて回答を投稿し、コンパイルしてみてください。
Enzo Ferber

回答:


141

これにより、コンパイル時にマクロをオーバーライドできます。

gcc -DMACRONAME=value

ヘッダーファイルの定義がデフォルトとして使用されます。


51

コメントで述べたように、この状況を想像してみてください。

foo.h

#define FOO  4

defs.h

#ifndef FOO
#define FOO 6
#endif

#ifndef BAR
#define BAR 4
#endif

bar.c

#include "foo.h"
#include "defs.h"

#include <stdio.h>

int main(void)
{
    printf("%d%d", FOO, BAR);
    return 0;
}

印刷します44

ただし、条件ifndefがそこにない場合、結果はMACRO再定義のコンパイル警告となり、が出力されます64

$ gcc -o bar bar.c
In file included from bar.c:2:0:
defs.h:1:0: warning: "FOO" redefined [enabled by default]
 #define FOO 6
 ^
In file included from bar.c:1:0:
foo.h:1:0: note: this is the location of the previous definition
 #define FOO 4
 ^

1
これはコンパイラ固有です。オブジェクトのようなマクロの再定義は、再定義が「同じ」でない限り、違法です(そのためのより技術的な仕様がありますが、ここでは重要ではありません)。違法なコードには診断が必要であり、診断(ここでは警告)を発行すると、コンパイラーは実装固有の結果を含むコードのコンパイルを含め、何でも自由に実行できます。
ピートベッカー、

7
あなたは同じマクロのDEFSが競合している場合、あなたはいないだろう、むしろ、ほとんどの場合、警告を取得しますか?最初の定義を黙って使用するのではなく(2番目のifdef定義は再定義を回避するためにを使用するため)。
Peter Cordes

@PeterCordesほとんどの場合、#infdefsの下の定義は「フォールバック」または「デフォルト」の値として使用されます。基本的に、「ユーザーが設定した場合は問題ありません。設定していない場合は、デフォルト値を使用します。」
Angewは2015

@Angew:わかりました。#definesライブラリのABIの一部であるライブラリヘッダーに一部がある場合、それらをでラップしないでください#ifndef。(またはより良い、を使用しますenum)。#ifndefあるコンパイルユニットに何かのカスタム定義があり、別のコンパイルユニットには問題がない場合にのみ適切であることを明確にしたかっただけです。がとa.cは異なる順序でヘッダーを含む場合b.c、それらはの異なる定義を取得する可能性がありmax(a,b)、それらの定義の1つはで壊れるmax(i++, x)可能性がありますが、他はGNUステートメント式で一時を使用する可能性があります。少なくとも混乱しています!
Peter Cordes

@PeterCordesその場合に私がしたいのは#ifdef FOO #error FOO already defined! #endif #define FOO x
Cole Johnson

17

コンテキストはわかりませんが、これを使用して、これらのマクロ定義によって設定された値をオーバーライドできるようにすることができます。ユーザーがこれらのマクロのいずれかに異なる値を明示的に定義した場合、ここで使用される値の代わりにその値が使用されます。

たとえばg ++では、-Dコンパイル中にフラグを使用して、マクロに値を渡すことができます。


14

これは、ヘッダーファイルのユーザーが自分のコードまたはコンパイラの-Dフラグからの定義を上書きできるようにするために行われます。


7

Cプロジェクトは複数のソースファイルに存在します。単一のソースファイルで作業する場合、チェックは(実際には)意味がないように見えますが、大規模なCプロジェクトで作業する場合は、定数を定義する前に既存の定義を確認することをお勧めします。アイデアは単純です。その特定のソースファイルに定数が必要ですが、すでに別のファイルで定義されている可能性があります。


2

ユーザーがコンパイルして作業できるようにするデフォルトのプリセットをユーザーに提供するフレームワーク/ライブラリについて考えることができます。これらの定義はさまざまなファイルに分散されており、最終ユーザーはそのconfig.hファイルをインクルードして値を設定できるようにすることをお勧めします。ユーザーがいくつかの定義を忘れた場合、システムは事前設定のために作業を続行できます。


1

使用する

#ifndef BEEPTRIM_PITCH_RATE_DEGPS
#define BEEPTRIM_PITCH_RATE_DEGPS                   0.2f
#endif

ユーザーがコマンドライン引数(gcc / clang / VS内)を使用してマクロの値を定義できるようにします-DBEEPTRIM_PITCH_RATE_DEGPS=0.3f

別の重要な理由があります。プリプロセッサマクロを別の方法で再定義すると、エラーになります。別のSO質問に対するこの回答を参照してください。#ifndefチェックを行わない場合-DBEEPTRIM_PITCH_RATE_DEGPS=0.3f、コンパイラー呼び出しでコマンドライン引数として使用すると、コンパイラーはエラーを生成するはずです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.