Что означает "зарезервировано для любого использования"?

ПРИМЕЧАНИЕ. Это вопрос , хотя я добавил , если какой-либо эксперт C++ может дать обоснование или историческую причину, по которой C++ использует другую формулировку, чем C.


В стандартной спецификации библиотеки C мы имеем этот нормативный текст, C17 7.1.3 Зарезервированные идентификаторы (выделение мое):

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

Теперь я продолжаю читать ответы на SO различными уважаемыми экспертами С, где они утверждают, что для компилятора или стандартной библиотеки это нормально, чтобы использовать идентификаторы с подчеркиванием + прописным или двойным подчеркиванием.

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

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

В целом, стандарт C написан таким образом, что ожидается, что разработчики компилятора/библиотекаторы станут типичным читателем - не столько программистами приложений.

Примечательно, что C++ имеет совершенно другую формулировку:

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

(См. Каковы правила использования подчеркивания в идентификаторе C++?)

Возможно, это смешение между C и C++, а языки здесь разные?

Ответ 1

В стандарте C значение термина "зарезервировано" определяется 7.1.3p2, непосредственно под списком маркеров, который вы указываете:

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

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

Я не соблюдал стандарт C++ и больше не считаю себя способным его интерпретировать.

Ответ 2

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

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

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

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

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

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

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

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

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

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

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

Ответ 3

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

  • Раннее издание K & R C 1 имеет следующий текст:

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

  • Второе издание K & R добавило Приложение B, в котором рассматривается стандартная библиотека, где мы можем читать

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

  • Ранние проекты ANSI C, а также "C90" ISO 9899: 1990, имеют тот же текст, что и в текущем стандарте ISO.

  • Однако самые ранние C++ черновики имеют другой текст, как указано в @hvd, возможно, разъяснение стандарта C. С ПРОЕКТА: 20 сентября 1994 года:

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

Таким образом, формулировка "зарезервирована для любого использования" была изобретена комитетом ANSI/ISO C90, тогда как комитет C++ несколько лет спустя использовал более четкую формулировку, аналогичную формулировке в предстандартной книге K & R.


Обоснование C99 V5.10 говорит об этом ниже 7.1.3:

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

Это делает намерение комитета совершенно ясным: "зарезервировано для любого использования" означает "зарезервировано для разработчика".


Также следует отметить, что текущий стандарт С имеет следующий нормативный текст в другом месте: 6.2.5:

Могут также существовать расширенные знаковые целочисленные типы с реализацией. 38)

где информативная нотная заметка 38 гласит:

38) Ключевые слова, определенные для реализации, должны иметь форму идентификатора, зарезервированного для любого использования, как описано в 7.1.3.

Ответ 4

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

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

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

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


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

Ответ 5

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

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

int near foo;

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