Perché definire una macro solo se non è già definita?


93

In tutta la nostra base di codice C, vedo ogni macro definita nel modo seguente:

#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 eseguire questi controlli di definizione invece di definire solo 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
È garantito che la modifica delle costanti da qualche altra parte nel codice funzioni in questo modo. Se da qualche altra parte qualcuno definisce una di quelle macro, non verranno sovrascritte dal preprocessore quando analizza questo file.
Enzo Ferber

8
È un esempio del principio di progettazione WET.
netto

Ha pubblicato una risposta con un esempio, prova a compilarlo.
Enzo Ferber

Risposte:


141

Ciò ti consente di sovrascrivere le macro durante la compilazione:

gcc -DMACRONAME=value

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


51

Come ho detto nel commento, immagina 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;
}

Verrà stampato 44 .

Tuttavia, se il condizionale ifndefnon fosse presente, il risultato sarebbe un avviso di compilazione della ridefinizione della 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. Ridefinire una macro simile a un oggetto è illegale a meno che la ridefinizione non sia "la stessa" (c'è una specifica più tecnica per questo, ma non è importante qui). Il codice illegale richiede una diagnostica e, dopo aver emesso una diagnostica (qui un avviso), il compilatore è libero di fare qualsiasi cosa, inclusa la compilazione del codice con risultati specifici dell'implementazione.
Pete Becker

7
Se hai definizioni in conflitto per la stessa macro, non preferiresti ricevere l'avviso nella maggior parte dei casi? Piuttosto che usare silenziosamente la prima definizione (perché la seconda usa un ifdefper evitare di ridefinire).
Peter Cordes,

@PeterCordes La maggior parte delle volte, le definizioni sotto #infdefs vengono utilizzate come valori "fallback" o "predefiniti". Fondamentalmente, "se l'utente lo ha configurato, va bene. In caso contrario, utilizziamo un valore predefinito".
Angew non è più orgoglioso di SO

@Angew: Ok, quindi se avete un po ' #definesin un colpo di testa libreria che fanno parte della ABI della biblioteca, si dovrebbe non avvolgerli in #ifndef. (O meglio, usa un enum). Volevo solo chiarire che #ifndefè appropriato solo quando si ha una definizione personalizzata per qualcosa in un'unità di compilazione, ma non un'altra va bene. Se a.cinclude intestazioni in un ordine diverso da b.c, potrebbero ottenere definizioni diverse di max(a,b), e una di queste definizioni potrebbe interrompersi max(i++, x), ma l'altra potrebbe usare temporanee in un'espressione-istruzione GNU. Almeno ancora confuso!
Peter Cordes

@PeterCordes Quello che mi piace fare in questo caso è#ifdef FOO #error FOO already defined! #endif #define FOO x
Cole Johnson

17

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

Ad esempio in g ++ è possibile utilizzare il -Dflag 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 suo codice o dal flag -D del compilatore.


7

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


2

Si potrebbe pensare a un framework / libreria che fornisca all'utente un preset predefinito che consenta all'utente di compilare e lavorarci. Queste definizioni sono distribuite in diversi file e si consiglia all'utente finale di includere il suo file config.h dove può configurarne i valori. Se l'utente ha dimenticato alcune definizioni il sistema può continuare a funzionare grazie al preset.


1

Utilizzando

#ifndef BEEPTRIM_PITCH_RATE_DEGPS
#define BEEPTRIM_PITCH_RATE_DEGPS                   0.2f
#endif

consente all'utente di definire il valore della macro utilizzando l'argomento della riga di comando (in gcc / clang / VS) -DBEEPTRIM_PITCH_RATE_DEGPS=0.3f .

C'è un altro motivo importante. È un errore ridefinire una macro del preprocessore in modo diverso. Vedi questa risposta a un'altra domanda SO . Senza il #ifndefcontrollo, il compilatore dovrebbe produrre un errore se -DBEEPTRIM_PITCH_RATE_DEGPS=0.3fviene utilizzato come argomento della riga di comando nella chiamata del compilatore.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.