Почему контролируемая группа в условном включении должна быть лексически допустимой, если условное ложно? - PullRequest
0 голосов
/ 18 сентября 2018

Компилируется следующая программа:

// #define WILL_COMPILE 
#ifdef WILL_COMPILE
int i = 
#endif

int main()
{   
    return 0;
}

GCC Live demo здесь .

Но следующее выдаст предупреждение:

//#define WILL_NOT_COMPILE
#ifdef WILL_NOT_COMPILE
char* s = "failure
#endif

int main()
{   
    return 0;
}

GCC Live demo здесь .

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

Но почему во втором примере требуется лексическая валидность, когда контролируемая группа не будет включена?

Поиск в Интернете Я нашел эту цитату :

Даже если условное условие не выполнено, контролируемый текст внутри него все равно проходит первоначальные преобразования и токенизацию.Следовательно, все это должно быть лексически допустимым C. Обычно единственный способ, которым это имеет значение, заключается в том, что все комментарии и строковые литералы внутри ошибочной условной группы все равно должны быть правильно завершены.

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

Я что-то здесь упустил?

Ответы [ 3 ]

0 голосов
/ 19 сентября 2018

"Почему [что-то о С] так и есть?"Обычно на вопросы нельзя ответить, потому что никто из тех, кто написал стандарт С 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 считает это имя забавным.

0 голосов
/ 19 сентября 2018

Описание Этап трансляции 3 (C11 5.1.1.2/3), который происходит до того, как действуют директивы предварительной обработки:

Исходный файл разлагается на токены предварительной обработки ипоследовательности символов пробела (включая комментарии).

И грамматика для токена предварительной обработки :

заголовок-имя
идентификатор
номер pp
символьная константа
строковый литерал
пунктуатор
каждый символ, не являющийся пробелом, который не может быть одним из перечисленных выше

Обратите внимание, в частности, что string-literal один токен предварительной обработки.Последующее описание (C11 6.4 / 3) поясняет, что:

Если символ ' или " соответствует последней категории, поведение не определено.

Таким образом, ваш второй код вызывает неопределенное поведение на этапе перевода 3.

0 голосов
/ 18 сентября 2018

На этапе перевода 3 препроцессор сгенерирует токены препроцессора и будет иметь " в конце для всех непробельных символов, которые не могут быть одним из указанных выше неопределенное поведение.См. C11 6.4 Лексические элементы p3 :

Токен - это минимальный лексический элемент языка в фазах перевода 7 и 8. Категории токенов: ключевые слова, идентификаторы, константы, строковые литералы и знаки препинания. Токен предварительной обработки является минимальным лексическим элементом языка в фазах перевода с 3 по 6 .Категории токенов предварительной обработки: имена заголовков, идентификаторы, числа предварительной обработки, символьные константы, строковые литералы, знаки пунктуации и одиночные непробельные символы, которые лексически не соответствуют другим категориям токенов предварительной обработки. 69) Если a 'или символ "соответствует последней категории, поведение не определено. ....

Для справки маркер предварительной обработки :

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

, из которых непревзойденный " во втором примере соответствует non-white-space character that cannot be one of the above.

, поскольку это неопределенное поведение, а не ограничение, компилятор не являетсяобязан диагностировать его, но это, безусловно, разрешено, и, используя -pedantic-errors, он даже становится ошибкой сеанс Godbolt . Как указывает Ричи, это становится только нарушением альтаЕсли токен остается в процессе предварительной обработки.

Документ gcc, который вы цитируете , в основном говорит то же самое:

... Даже если условие не выполнено, управляемыйтекст внутри него все еще проходит через начальные преобразования и токенизацию .Следовательно, все это должно быть лексически допустимым C. Обычно единственный способ, которым это имеет значение, состоит в том, что все комментарии и строковые литералы внутри ошибочной условной группы все еще должны быть правильно завершены ....

...