Разные версии для предотвращения двойного включения заголовочного файла, что правильно? - PullRequest
1 голос
/ 11 апреля 2020

Если у меня есть файл с именем test.h и я хочу предотвратить его двойное включение (чтобы предотвратить ошибку компоновщика), мне сказали набрать:

#ifndef TEST_H_INCLUDED
#define TEST_H_INCLUDED

Но я видел несколько версий этого, например, один сказал мне написать:

#ifndef TEST_H
#define TEST_H

В чем разница и почему я не могу просто использовать оригинальное имя моего файла, например:

#ifndef test.h
#define test.h

Редактировать: Как препроцессор узнал, что я пытаюсь предотвратить двойное включение заголовочного файла с именем test.h, если имя после #ifndef не важно и может быть чем-то вроде: #ifndef Dummy, и что даже смысл использования имени, если я не собираюсь его использовать, если они облегчат его и позволят нам только набрать:

#ifndef
#define

Ответы [ 4 ]

2 голосов
/ 11 апреля 2020

В чем разница [..]

Разницы как таковой нет. Имя макроса может быть любым, и условием является использование имени заголовка, разделенного подчеркиванием (TEST_H).

, почему я не могу просто использовать исходное имя моего файла, например

Имя макроса не может содержать .. Так что это не разрешено. То же правило для идентификаторов применимо и к именам макросов. См. 6.4.2 Идентификаторы .

Вместо этого вы можете иметь:

#ifndef test_h
#define test_h
#endif

или:

#ifndef test_header
#define test_header
#endif

Но опять же, соглашение заключается в используйте все заглавные буквы с точками, замененными подчеркиванием и, возможно, с начальными / конечными подчеркиваниями.


Возьмите этот пример кода (не эквивалентный макросам - только для демонстрационных целей):

int flag = 0; 
if (flag == 0) {
    /* include this content */;
    flag = 1;
}
else {
    /* not included */
}

Здесь flag является эквивалентом имени макроса. Вот почему само название не имеет никакого значения. Все, что имеет значение, это то, был ли установлен flag или нет.

, если они облегчат и позволят нам только набрать:
# ifndef
#define

К сожалению, это неверный синтаксис.

Я понимаю общий вопрос и согласен, что это должно было быть лучше / проще. Но это историческое. Препроцессор - древняя вещь (по крайней мере, с начала 70-х), и он не изменится сейчас:)

2 голосов
/ 11 апреля 2020

Первые два варианта семантически эквивалентны, а то, что «лучше», субъективно (первый вариант несколько понятнее, но требует большего набора текста).

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

1 голос
/ 11 апреля 2020

Суть вопроса, кажется, была хорошо изложена в кратком изложении, которое было отредактировано:

Редактировать : Как препроцессор узнал, что я пытаюсь предотвратить двойное включение заголовочного файла с именем test.h, если имя после #ifndef не имеет значения и может иметь следующий вид: #ifndef Dummy,

Препроцессор не Знайте, что вы специально пытаетесь предотвратить многократное включение или что-либо предпринимаемое в отношении имени файла заголовка, который вы пытаетесь защитить. Это приложение механизма условной компиляции общего назначения препроцессора к конкретной задаче c размещения (не фактически предотвращающей) многократного включения.

и в чем даже смысл используя имя, если я не собираюсь его использовать [...]

Но вы действительно используете его. В директиве #ifndef. То, что упомянутое имя макроса совпадает с именем #define d, крайне важно для правильной работы этого механизма.

Механизм использует тот факт, что препроцессор может проверять, заданы ли c имена макросов определены и используют результат в качестве условия для того, компилируется ли связанный раздел исходного файла. Вот что такое #ifndef ... #endif. #define гарантирует, что при компиляции раздела между ними будет задан указанный макрос препроцессора, так что условие #ifndef оценивается как ложное при последующих включениях того же заголовка. Таким образом, указанный c выбор имени макроса не имеет значения, но тот факт, что он отличается от всех имен макросов, используемых другими заголовками (или что это не так, если это делается целенаправленно), равен значительно.

если они облегчат задачу и позволят нам только набрать:

#ifndef
#define

Опять же, это конкретное c приложение общего назначения механизм. Механизм общего назначения предназначен для определения макросов препроцессора и проверки того, определены ли они, которые являются частью более крупного средства макросов препроцессора. Это средство работает в терминах заданных c имен макросов. Более того, даже если бы оно было разумным, пустое имя макроса не сработало бы для этой цели, потому что (если оно используется в общем случае) оно не удовлетворяло бы необходимому критерию каждого заголовка, использующего отдельное имя макроса.

Однако некоторые * Реализации 1054 * согласны с вами, что защита заголовков может быть проще. Как общее (но не универсальное!) Расширение , некоторые реализации распознают

#pragma once

, появляющийся в начале заголовка, в качестве замены средств защиты на основе условной компиляции.

1 голос
/ 11 апреля 2020

Ничто не отличается для первых двух опций определения. Последняя опция не принимается, потому что препроцессор не принимает (.) как часть идентификатора.

Иногда необходимо выбрать один из нескольких различных заголовочных файлов, которые будут включены в вашу программу:

#if SYSTEM_1
   # include "test1.h"
#elif SYSTEM_2
   # include "test2.h"
#endif

вы просто вводите имя макроса:

#define SYSTEM_1 "test1.h"
...
#include SYSTEM_1

ОБНОВЛЕНИЕ:

, вы не можете использовать:

#ifndef
#define

Препроцессору нужно имя макроса. Это как заявление if ... else. Вы не можете if () {} else {}.

Вы можете определить все, что хотите, чтобы событие не использовалось.

#ifndef NOTUSE
#define NOTUSE

Например, когда вы хотите отладить, вы определяете DEBUG и можете использовать это когда хотите.

#ifdef DEBUG
printf(" debug here\n");
#endif /* DEBUG */

Если вы не определили DEBUG, функция printf не запускается.

...