2015-09-04 17 views
89

In tutta la nostra base di codice C, vedo ogni macro definita nel seguente modo:Perché definire una macro solo se non è già definita?

#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 

Qual è la logica di fare questi controlli definiscono anziché definire soltanto le macro?

#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 

Non riesco a trovare questa pratica spiegata da nessuna parte sul web.

+6

Cambiare le costanti da qualche altra parte nel codice è garantito che funzioni in questo modo. Se da qualche parte qualcuno definisce una di queste macro, non verrà sovrascritto dal preprocessore quando analizza questo file. –

+8

È un esempio del principio di progettazione WET. – stark

+0

Inserita una risposta con un esempio, prova a compilarla. –

risposta

132

Questo consente di ignorare le macro quando si sta compilando:

gcc -DMACRONAME=value 

Le definizioni nel file di intestazione vengono utilizzati come impostazioni predefinite.

17

Non conosco il contesto ma questo può essere utilizzato per fornire all'utente la disponibilità a sostituire i valori impostati da quelle definizioni di macro. Se l'utente definisce esplicitamente un valore diverso per una di queste macro, verrà utilizzato al posto dei valori utilizzati qui.

Ad esempio in g ++ è possibile utilizzare il flag -D durante la compilazione per passare un valore a una macro.

14

Questo viene fatto in modo che l'utente del file di intestazione possa sovrascrivere le definizioni dal proprio codice o dal flag -d del compilatore.

7

Qualsiasi progetto C risiede su più file sorgente. Quando si lavora su un singolo file sorgente i controlli sembrano (e in realtà) non hanno senso, ma quando si lavora su un grande progetto C, è una buona pratica controllare le definizioni esistenti prima di definire una costante. L'idea è semplice: hai bisogno della costante in quel specifico file sorgente, ma potrebbe essere già stata definita in un'altra.

51

Come ho detto nel commento, immaginare questa situazione:

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; 
} 

Stampa 44.

Tuttavia, se il condizionale ifndef non era presente, il risultato sarebbe un avviso di compilazione di ridefinizione MACRO e verrà stampato 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

Questo è specifico del compilatore. La ridefinizione di una macro simile ad un oggetto è illegale a meno che la ridefinizione non sia "la stessa" (c'è una specifica più tecnica per questo, ma qui non è importante). Il codice illegale richiede una diagnostica e, dopo aver emesso una diagnostica (qui un avvertimento), il compilatore è libero di fare qualsiasi cosa, inclusa la compilazione del codice con risultati specifici dell'implementazione. –

+7

Se si dispone di difetti in conflitto per la stessa macro, non si otterrebbe * piuttosto * l'avviso nella maggior parte dei casi? Piuttosto che usare silenziosamente la prima definizione (perché la seconda usa un 'ifdef' per evitare la ridefinizione). –

+0

@PeterCordes La maggior parte delle volte, le definizioni in '# infdef's vengono utilizzate come valori di" fallback "o" default ". Fondamentalmente, "se l'utente l'ha configurato, bene, se no, usiamo un valore predefinito." – Angew

2

Si potrebbe pensare ad un quadro/libreria che fornisce all'utente un preset di default che consentono all'utente di compilare e lavorare su di esso. Queste definizioni sono distribuite in diversi file e si consiglia all'utente finale di includere il suo file config.h in cui può configare i suoi valori. Se l'utente ha dimenticato alcuni, il sistema può continuare a funzionare a causa del preset.

1

Uso

#ifndef BEEPTRIM_PITCH_RATE_DEGPS 
#define BEEPTRIM_PITCH_RATE_DEGPS     0.2f 
#endif 

permette all'utente di definire il valore della macro utilizzando la linea di comando (in gcc/clang/VS) -DBEEPTRIM_PITCH_RATE_DEGPS=0.3f.

C'è un altro motivo importante. È un errore definire nuovamente una macro del preprocessore in modo diverso. Vedi this answer to another SO question. Senza il controllo #ifndef, il compilatore dovrebbe produrre un errore se -DBEEPTRIM_PITCH_RATE_DEGPS=0.3f viene utilizzato come argomento della riga di comando nel richiamo del compilatore.

Problemi correlati