Зачем определять макрос, если он еще не определен?

Всю нашу базу кода 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

Я не могу найти эту практику в любом месте в Интернете.

Ответ 1

Это позволяет вам переопределять макросы при компиляции:

gcc -DMACRONAME=value

Определения в файле заголовка используются как значения по умолчанию.

Ответ 2

Как я сказал в комментарии, представьте эту ситуацию:

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
 ^

Ответ 3

Я не знаю контекста, но это можно использовать, чтобы дать пользователю возможность переопределить значения, установленные этими макроопределениями. Если пользователь явно определяет другое значение для любого из этих макросов, он будет использоваться вместо значений, используемых здесь.

Например, в g++ вы можете использовать флаг -D во время компиляции, чтобы передать значение макросу.

Ответ 4

Это делается для того, чтобы пользователь файла заголовка мог переопределить определения из своего кода или из флага компилятора -D.

Ответ 5

Любой проект C находится в нескольких исходных файлах. При работе с одним исходным файлом проверки, кажется, (и фактически) не имеют смысла, но при работе над крупным проектом C рекомендуется проверять существующие определения перед определением константы. Идея проста: вам нужна константа в этом конкретном исходном файле, но она, возможно, уже определена в другом.

Ответ 6

Вы можете подумать о структуре/библиотеке, которая предоставляет пользователю предустановленную по умолчанию пресет, позволяющую пользователю компилировать и работать с ней. Эти определения распространяются в разных файлах, и конечному пользователю рекомендуется включить его в файл config.h, где он может настроить свои значения. Если пользователь забыл некоторые, то система может продолжать работать из-за пресета.

Ответ 7

Использование

#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 используется как аргумент командной строки при вызове компилятора.