Кросс-файл #if и #endif - это должно быть законно?
Согласно стандарту C11,
Директива предварительной обработки вида
# включить новую строку "q-char-sequence"
вызывает замену этой директивы всем содержимым исходного файла, идентифицируемого указанной последовательностью между "разделителями".
Так что, если у меня есть файл заголовка test.h
содержащий:
#endif
И исходный файл test.c
содержащий:
#if 1
#include "test.h"
Не должен ли он пройти этап предварительной обработки в соответствии со стандартом путем замены содержимого test.h
на месте?
Но я не могу сделать это с clang
, который говорит:
In file included from test.c:2:
./test.h:1:2: error: #endif without #if
#endif
^
test.c:1:2: error: unterminated conditional directive
#if 1
^
2 errors generated.
Так что же такое поведение, указанное в стандарте?
2 ответа
Если вы читаете, например, этот C++ #include
ссылка на включенный файл сначала выполняется на этапах трансляции с 1 по 4, а на этапе 4 выполняется препроцессор (рекурсивно).
Это означает, что файл, который вы включаете, должен быть полным в отношении #if
а также #endif
,
Это происходит и в Си.
После прочтения спецификации C11 (ISO/IEC 9899:2011 [2012]), я думаю, происходит следующее:
Компилятор находится в фазе препроцессора ( фаза 4) и оценивает #if 1
директива препроцессора. Условие оценивается как истинное, поэтому оно входит в блок внутри условия. Есть песня #include "test.h"
директивы.
Когда компилятор обрабатывает директиву include, он временно прекращает обработку текущего файла для обработки включенного файла. Эта обработка включенного файла проходит фазу компиляции с 1 по 4 (включительно) перед продолжением работы с текущим исходным файлом.
Когда обработка включенного файла заголовка переходит к фазе 4 и начинается обработка #endif
директива, то она не поднимается в стеке рекурсивного включения, чтобы найти соответствующий #if
препроцессор просматривает только текущий кадр стека (текущий файл). Поэтому вы получаете первую ошибку о нет #if
, Стандарт на самом деле ничего не говорит об этом. Все это говорит, в основном, что это #if
должен иметь соответствующий #endif
, То, что компилятор не идет вверх по стеку включения, чтобы найти соответствие #if
кажется, больше деталей реализации.
В любом случае, препроцессор заканчивает обработку файла заголовка с шага 3 на этапе 4, который
В конце этого этапа все директивы препроцессора удаляются из источника.
Поэтому, когда элемент управления возвращается к предварительной обработке исходного файла, файл, который он фактически включает, не содержит никаких директив предварительной обработки. Все, что включено, это в основном пустой файл. И это приводит ко второй ошибке, что нет #endif
для #if
потому что там действительно нет.
clang
правильно: такой раскол #if/#endif
в разных файлах не соответствует. Это потому, что препроцессор сначала сканирует остальную часть файла, чтобы найти соответствующий #endif
и только затем разрешает другие директивы препроцессора, которые присутствуют в условной части, которая сохраняется.
Это раздел 6.10.1 p6 стандарта C.