0

Каков смысл фразы "зарезервировано для любого использования"?

9

Описание проблемы:

Я столкнулся с некоторыми несоответствиями в стандарте C и C++, касающимися зарезервированных идентификаторов. В спецификации стандартной библиотеки C, в разделе C17 7.1.3 «Зарезервированные идентификаторы», указано, что:

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

На StackOverflow я прочитал мнения различных экспертов по C, которые утверждают, что компиляторы или стандартные библиотеки могут использовать идентификаторы с подчеркиванием и заглавной буквой, или двойным подчеркиванием. Но не означает ли «зарезервированы для любого использования», что они зарезервированы для любого — кроме будущих расширений самого языка C? То есть реализация не вправе использовать их?

Вторая фраза, касающаяся одиночного ведущего подчеркивания, кажется, предназначена именно для реализации.

В общем, стандарт C написан с учетом того, что его читателями будут в первую очередь поставщики компиляторов и разработчики библиотек, а не прикладные программисты.

Замечу, что в C++ wording выглядит совершенно иначе:

  • Каждое имя, содержащее двойное подчеркивание (__) или начинающееся с подчеркивания, за которым следует заглавная буква, зарезервировано для реализации для любого использования.

Может быть, это недоразумение между C и C++, и языки отличаются в этом аспекте? Кто-нибудь может прояснить эту ситуацию?

5 ответ(ов)

0

В стандарте C термин «запрещённый» определён в разделе 7.1.3p2, который следует сразу после списка пунктов, который вы цитируете:

Никакие другие идентификаторы не являются запрещёнными. Если программа объявляет или определяет идентификатор в контексте, в котором он запрещён (за исключением случаев, разрешённых в 7.1.4), или определяет запрещённый идентификатор как имя макроса, поведение будет неопределённым.

Подчёркиваю: запрещённые идентификаторы накладывают ограничения на программу, а не на реализацию. Таким образом, общее толкование – что запрещённые идентификаторы могут использоваться реализацией для любых целей – является верным для C.

Что касается стандарта C++, я не следил за его изменениями и больше не чувствую себя квалифицированным для его интерпретации.

0

Стандарт в первую очередь написан для того, чтобы ориентировать разработчиков, но он также служит описанием того, что делает программу корректной и каковы ее последствия. Это происходит оттого, что базовое определение компилятора, соответствующего стандарту, — это тот, который делает правильные вещи для любой программы, соответствующей стандарту:

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

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

Однако стандарт продолжает:

Соответствующая реализация может иметь расширения (включая дополнительные функции библиотеки), при условии, что они не изменяют поведение ни одной строго соответствующей программы.

Это и есть ключевой момент. Расширения компилятора должны быть разработаны таким образом, чтобы они влияли на несоответствующие программы (которые содержат неопределенное поведение или которые не должны компилироваться вовсе), позволяя им компилироваться и делать интересные дополнительные вещи.

Таким образом, цель определения "резервируемых идентификаторов", когда язык на самом деле не нуждается в этих идентификаторах, заключается в том, чтобы предоставить реализациям некоторую свободу, предложив им некоторые элементы, которые делают программу несоответствующей. Причина, по которой компилятор может распознавать, скажем, __declspec как часть объявления, заключается в том, что использование __declspec в объявлении в противном случае незаконно, поэтому компилятор имеет право делать все, что захочет!

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

Стандарт C++ работает аналогичным образом, хотя он немного более явно описывает эту стратегию:

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

Я подозреваю, что различие в формулировках связано с тем, что стандарт C++ просто яснее объясняет, как должны функционировать расширения. Тем не менее, ничего в стандарте C не запрещает реализации делать то же самое. (И в основном мы игнорируем требование, чтобы компилятор предупреждал вас каждый раз, когда вы используете __declspec.)

0

В языке C существует несколько контекстов, в которых символ может иметь определение:

  • Пространство макроновых имен,
  • Пространство формальных имен аргументов макроса (это пространство специфично для каждого макроса),
  • Пространство обычных идентификаторов,
  • Пространство имен типов (tag names),
  • Пространство меток (это пространство специфично для каждой функции),
  • Пространство членов структур/объединений (это пространство специфично для каждой структуры/объединения).

Термин "зарезервировано для любого использования" означает, что пользовательский код в соответствии с стандартом не может использовать символы, которые начинаются с подчеркивания, за которым следует заглавная буква или другое подчеркивание, в любом из вышеперечисленных контекстов. В отличие от идентификаторов, которые начинаются с единственного подчеркивания, но за которыми следует строчная буква или цифра. Эти идентификаторы попадают во вторую категорию идентификаторов, начинающихся с подчеркивания. Пользовательский код может использовать эти идентификаторы как имена аргументов макросов, как метки или как имена членов структур/объединений.

Фраза "зарезервировано для любого использования" не означает, что реализация не может использовать такие символы. Цель резервирования заключается в том, чтобы предоставить пространство имен, которое реализации могут использовать без опасений, что имена, определенные реализацией, будут конфликтовать с именами, определенными в коде пользователя в соответствии с стандартом.


1Стандарт не совсем означает "не может использовать". Стандарт поощряет программное использование небольшого количества имен, которые начинаются с двойного подчеркивания. Например, в соответствии с стандартом реализация обязана определять __STDC_VERSION__, __FILE__, __LINE__, и __func__. Версия стандарта 2011 года даже приводит пример предположительно совместимой программы, которая ссылается на __func__.

0

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

#ifdef __ACME_COMPILER
#define near __near
#else
#define near
#endif

int near foo;

В данном случае, если код обрабатывается компилятором Acme (который, предположительно, поддерживает подобный функционал), будет объявлен идентификатор foo с использованием квалификатора __near. При этом код также будет совместим с другими компиляторами, которые не требуют или не получают выгоды от использования данной директивы. Ничто не запрещает соответствующей реализации определить __ACME_COMPILER и интерпретировать __near как "запустить ядерные ракеты", но качественная реализация не должна стремиться нарушать работу кода, подобного приведенному выше. Если реализация не знает, что должно означать __ACME_COMPILER, трактовать его как любой другой неизвестный идентификатор позволит поддерживать полезные конструкции, подобные этой.

0

Это может быть несколько запоздало, но один момент остается неосвещенным другими.

Ваш вопрос можно рассмотреть с другой стороны. Стандарт позволяет реализации (как вы заметили) использовать символ вроде _Foo, но, что более важно, он запрещает реализации использовать foo. Последний зарезервирован для вашего использования.

Чтобы разобраться в этом, предположим, что в будущем стандарте C был введен новый ключевое слово _Foo. Гипотетическая реализация уже использовала этот символ, что в этом случае произойдет?

Ответ:

  1. Сначала реализация еще не внедрила новый стандарт. Пока он не будет внедрен, новый стандарт не имеет практического эффекта.
  2. Позже, в процессе внедрения нового стандарта, реализация тихо изменяет каждое вхождение _Foo на _Bar.

Без проблем.

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь