"Почему [что-то о С] так и есть?"Обычно на вопросы нельзя ответить, потому что никто из тех, кто написал стандарт С 1989 года, здесь не для того, чтобы отвечать на вопросы [насколько я знаю, во всяком случае], и если они были здесь, прошло бы почти тридцать летназад, и они, вероятно, не помнят.
Однако я могу подумать о вероятной причине, по которой содержимое пропущенных условных групп должно состоять из действительной последовательности токенов предварительной обработки.Обратите внимание, что комментарии , а не должны состоять из действительной последовательности токенов предварительной обработки:
/* this comment's perfectly fine even though it has an unclosed
character literal inside */
Также обратите внимание, что поиск конца комментария действительно прост./*
вы ищете следующий */
, //
вы ищете конец строки.Единственное осложнение состоит в том, что триграфы и обратный слеш-перевод строки должны быть преобразованы первыми.Маркировка содержимого комментариев была бы лишним кодом без какой-либо полезной цели.
Напротив, сканировать конец пропущенной условной группы непросто, потому что условные группы вкладываются.Вы должны искать #if
, #ifdef
и #ifndef
, а также #else
и #endif
и считать свою глубину.И все эти директивы лексически определены в терминах токенов препроцессора, потому что это наиболее естественный способ искать их, когда вы не в пропущенной условной группе.Требование, чтобы пропущенные условные группы были токенизируемыми, позволяет препроцессору использовать тот же код для обработки директив в пропущенных условных группах, как и в других местах.
По умолчанию GCC выдает только предупреждение, когда встречает строку, не маркируемую внутри.пропущенная условная группа, ошибка в другом месте:
#if 0
"foo
#endif
"bar
дает мне
test.c:2:1: warning: missing terminating " character
"foo
^
test.c:4:1: error: missing terminating " character
"bar
^~~~
Это преднамеренное снисхождение, возможно, я ввел себя (это было только двадцать с тех пор, как я написал треть нынешнего препроцессора GCC, но я до сих пор забыл много деталей).Видите ли, оригинальный C препроцессор, который написал K и R, сделал разрешил произвольную ерунду внутри пропущенных условных групп, потому что он не был построен вокруг концепции токенов в первомместо;это преобразовало текст в другой текст.Таким образом, люди помещали бы комментарии между #if 0
и #endif
вместо /*
и */
, и, естественно, эти комментарии иногда содержали апострофы.Итак, когда Пер Ботнер, Нил Бут и Чиаки Ишикава и я заменили оригинальный GCC-совместимый препроцессор компилятора 1 на интегрированный, полностью совместимый со стандартами «cpplib», около GCC 3.0, мы почувствовали, что нам нужночтобы немного ослабить совместимость.
1 Поднимите руку, если вы достаточно взрослый, чтобы понять, почему RMS считает это имя забавным.