Согласно стандарту 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.
Так что же такое поведение, указанное в стандарте?
Если вы читаете, например, это C ++ #include
ссылка включенный файл сначала запускается этапы перевода от одного до четырех и фаза четвертая работает препроцессор (рекурсивно).
Это означает, что файл, который вы включаете, должен быть заполнен #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.