Intereting Posts
преобразовать цифру в строку в макрос Области связи IPv6 Многоадресные пакеты внезапно не маршрутизируются на MacBook Pro? Удаление новой строки не работает после попытки ее проверки Печать типа int с помощью% lu – C + XINU Может ли сокет быть закрыт из другого streamа, когда происходит отправка / recv в одном и том же сокете? Почему компилятор видит несоответствие между char * и спецификатором преобразования printf «s», когда char * typedef’d и доступен через структуру? Удаление узла из связанного списка по индексу Какие методы / страtagsи используют люди для создания объектов в C (а не C ++)? В каких версиях C есть блок внутри скобок, используемый для возврата значения? Почему start_routine для pthread_create возвращает void * и принимает void * Просмотр строки за строкой на C и C ++? получить текущее время в секундах Использование sqlite3_exec возможная уязвимость переполнения буфера для va_list в C? Интервью: указатели функций и случай переключения

Как разрешено несколько предыдущих деклараций для новой декларации с использованием extern?

О чем должен обратиться третий x :

 #include  static char x = '1'; int main(void) { char x = '2'; { extern char x; printf("%c\n", x); } } 

Это возникло в этом ответе и:

  • В Apple LLVM 9.1.0 clang-902-0.39.2 x extern char x относится к первому x , а «1» печатается.
  • GCC 8.2 не принимает этот исходный текст. , жалуясь: «error: переменная, ранее объявленная« статическая », переопределенная« extern ».

C 2018 6.2.2 4 говорит:

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

Поскольку существует два предшествующих объявления x , условие каждого из следующих «if» предложений истинно, первое для первого предшествующего объявления и второе для второго предшествующего объявления:

  • … если предыдущее объявление указывает внутреннюю или внешнюю связь, связь идентификатора с более поздним объявлением совпадает с привязкой, указанной в предыдущем объявлении.
  • … если в предыдущем объявлении не указана привязка, тогда идентификатор имеет внешнюю связь.

Поведение Клана здесь согласуется с использованием первого предложения, так что третий x имеет внутреннюю связь и относится к тому же объекту, что и первый x . Поведение GCC здесь согласуется с использованием второго предложения, так что третий x имеет внешнюю связь и конфликтует с первым x , который имеет внутреннюю связь.

Предоставляет ли стандарт C нам возможность решить, какой из них должен иметь место?

Третья декларация extern char x должна объявить x с внешней связью на основе C 2018 6.2.2 4, в которой говорится:

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

В объявлении extern char x первое объявление x не видно, поскольку оно было скрыто вторым объявлением. Следовательно, он не может претендовать на «предварительное объявление этого идентификатора». Второе объявление x является видимым, поэтому оно является «предшествующим заявлением» для целей вышеуказанного абзаца.

Тогда последнее предложение должно контролировать: в предыдущем объявлении не указывается ссылка (6.2.2 6, идентификатор области блока без extern не имеет привязки), поэтому третий x имеет внешнюю связь.

Тогда нарушение 6.2.2 7 нарушается, поскольку первый x имеет внутреннюю связь, а третий x имеет внешнюю связь:

Если внутри единицы перевода появляется один и тот же идентификатор с внутренней и внешней связью, поведение не определено.

Поскольку никакое синтаксическое правило или ограничение не нарушено, реализация стандарта не требуется стандартом для сообщения о диагностике. Поскольку поведение не определено, оно может сделать что угодно, в том числе принять этот код и сделать третий x ссылкой на тот же объект, что и первый x . Поэтому ни поведение Кланг, ни поведение GCC не нарушают стандарт в этом отношении. Однако, поскольку нарушение 6.2.27, может быть предпочтительным, и его отсутствие может рассматриваться как дефект Клана.

(Отклик на Paul Ogilvie и TC за то, что он сообщил мне об этом со своими комментариями.)