6.39 Синтаксис атрибутов
GCC предоставляет два способа задания атрибутов: стандартный синтаксис C и C++ с использованием двойных квадратных скобок и устаревший синтаксис расширений GNU с использованием ключевого слова __attribute__, который предшествует принятию стандартного синтаксиса и до сих пор широко используется в старом коде.
Стандартный синтаксис атрибутов «[[]]» распознается по умолчанию в языке GCC для C и C++. Более конкретно, этот синтаксис был впервые представлен в стандарте языка C++11 (см. Поддерживаемые GCC стандарты языка), и поддерживается GCC в коде C++ с -std=c++11 или -std=gnu++11 или более поздними версиями. Он также является частью стандарта языка C23 и поддерживается при компиляции кода C с -std=c23 или -std=gnu17 или более поздними версиями.
При использовании атрибутов, специфичных для GNU, в стандартном синтаксисе, необходимо добавлять префикс «gnu::» к их именам, как в gnu::section. Обращайтесь к соответствующим стандартам языка для точных деталей размещения атрибутов «[[]]» в вашем коде, так как они отличаются в некоторых деталях от правил синтаксиса атрибутов GNU.
Остальная часть этого раздела описывает детали синтаксиса расширений GNU __attribute__, и конструкции, к которым привязываются спецификаторы атрибутов, для языка C. Некоторые детали могут отличаться для C++ и Objective-C. Из-за ограничений в грамматике атрибутов некоторые формы, описанные здесь, могут не быть успешно проанализированы во всех случаях.
Существуют некоторые проблемы с семантикой атрибутов в C++. Например, нет преобразований имен для атрибутов, хотя они могут влиять на генерацию кода, поэтому могут возникнуть проблемы при использовании атрибутированных типов совместно с шаблонами или перегрузкой. Аналогично, typeid не различает типы с различными атрибутами. Поддержка атрибутов в C++ может быть ограничена в будущем атрибутами только на объявлениях, но не на вложенных деклараторах.
См. Определение атрибутов функций для деталей семантики атрибутов, применяемых к функциям. См. Задание атрибутов переменных для деталей семантики атрибутов, применяемых к переменным. См. Задание атрибутов типов для деталей семантики атрибутов, применяемых к структурам, объединениям и перечислимым типам. См. Атрибуты меток для деталей семантики атрибутов, применяемых к меткам. См. Атрибуты перечислителей для деталей семантики атрибутов, применяемых к перечислителям. См. Атрибуты операторов для деталей семантики атрибутов, применяемых к операторам.
Спецификатор атрибута имеет вид __attribute__ ((attribute-list)). Список атрибутов — это, возможно, пустая последовательность атрибутов, разделённых запятыми, где каждый атрибут является одним из следующих:
- Пустой. Пустые атрибуты игнорируются.
- Имя атрибута (которое может быть идентификатором, таким как
unused, или ключевым словом, таким какconst). - Имя атрибута, за которым следует список параметров в скобках для атрибута. Эти параметры принимают один из следующих видов:
- Идентификатор. Например, атрибуты
modeиспользуют этот вид. - Идентификатор, за которым следует запятая и непустой список выражений, разделённых запятыми. Например, атрибуты
formatиспользуют этот вид. - Возможный пустой список выражений, разделённых запятыми. Например, атрибуты
format_argиспользуют этот вид со списком, являющимся единственным целочисленным константным выражением, а атрибутыaliasиспользуют этот вид со списком, являющимся единственной строковой константой.
- Идентификатор. Например, атрибуты
Список спецификаторов атрибутов — это последовательность одного или нескольких спецификаторов атрибутов, не разделённых другими лексемами.
Вы можете необязательно задавать имена атрибутов с «__» перед и после имени. Это позволяет использовать их в заголовочных файлах, не беспокоясь о возможной макросе с тем же именем. Например, вы можете использовать имя атрибута __noreturn__ вместо noreturn.
Атрибуты меток
В GNU C, список спецификаторов атрибутов может появиться после двоеточия, следующего за меткой, отличной от case или default метки. GNU C++ допускает атрибуты на метках только в том случае, если спецификатор атрибута сразу же следует за точкой с запятой (т. е. метка применяется к пустому оператору). Если точка с запятой отсутствует, атрибуты C++ к меткам являются неоднозначными, так как в C++ допустимо объявление, которое может начинаться со списка атрибутов, помеченное меткой. Объявления не могут быть помечены меткой в C90 или C99, поэтому неоднозначность там не возникает.
Атрибуты перечислителей
В GNU C, список спецификаторов атрибутов может появиться как часть перечислителя. Атрибут идёт после константы перечисления, перед =, если она присутствует. Необязательный атрибут в перечислителе относится к константе перечисления. Невозможно поместить атрибут после константного выражения, если оно присутствует.
Атрибуты операторов
В GNU C, список спецификаторов атрибутов может появиться как часть пустого оператора. Атрибут предшествует точке с запятой.
Атрибуты типов
Список спецификаторов атрибутов может появиться как часть спецификатора struct, union или enum. Он может идти сразу после ключевого слова struct, union или enum, или после закрывающей фигурной скобки. Первый синтаксис предпочтительнее. В случаях, когда спецификаторы атрибутов следуют за закрывающей фигурной скобкой, они считаются относящимися к определённой структуре, объединению или перечислимому типу, а не к любому окружающему объявлению, в котором появляется спецификатор типа, и тип не считается полным до спецификаторов атрибутов.
Все остальные атрибуты
В противном случае спецификатор атрибута появляется как часть объявления, считая объявления безымянных параметров и имён типов, и относится к этому объявлению (которое может быть вложено в другое объявление, например, в случае объявления параметра), или к определённому декларатору в объявлении. Если атрибут спецификатор применяется к параметру, объявленному как функция или массив, он должен применяться к функции или массиву, а не к указателю, в который параметр неявно преобразуется, но это ещё не реализовано корректно.
Любой список спецификаторов и квалификаторов в начале объявления может содержать спецификаторы атрибутов, независимо от того, может ли в данном контексте такой список содержать спецификаторы класса хранения. (Некоторые атрибуты, однако, по своей сути являются спецификаторами класса хранения и имеют смысл только там, где могут использоваться спецификаторы класса хранения; например, section.) Существует одно необходимое ограничение этого синтаксиса: первое объявление параметра старого стиля в определении функции не может начинаться со спецификатора атрибута, так как такой атрибут применяется к функции, а не к параметру, что описывается синтаксисом ниже (который, однако, ещё не реализован в этом случае).
В некоторых других случаях спецификаторы атрибутов разрешены этой грамматикой, но пока не поддерживаются компилятором. Все спецификаторы атрибутов в этом месте относятся ко всему объявлению. В устаревшем использовании, когда тип int подразумевается отсутствием спецификаторов типа, такой список спецификаторов и квалификаторов может быть списком спецификаторов атрибутов без других спецификаторов или квалификаторов.
В настоящее время первый параметр в прототипе функции должен иметь какой-либо спецификатор типа, который не является спецификатором атрибута; это устраняет неоднозначность в толковании void f(int
(__attribute__((foo)) x)), но может быть изменено. В настоящее время, если скобки декларатора функции содержат только атрибуты, эти атрибуты игнорируются, а не вызывают ошибку или предупреждение или не подразумевают единственный параметр типа int, но это может быть изменено.
Список спецификаторов атрибутов может появиться непосредственно перед декларатором (кроме первого) в списке деклараторов, разделённых запятыми, в объявлении более одного идентификатора, использующего один список спецификаторов и квалификаторов. Такие спецификаторы атрибутов применяются только к идентификатору, перед декларатором которого они появляются. Например, в
__attribute__((noreturn)) void d0 (void),
__attribute__((format(printf, 1, 2))) d1 (const char *, ...),
d2 (void);
атрибут noreturn применяется ко всем объявленным функциям; атрибут format применяется только к d1.
Список спецификаторов атрибутов может появиться непосредственно перед запятой, = или точкой с запятой, завершающей объявление идентификатора, отличного от определения функции. Такие спецификаторы атрибутов применяются к объявленному объекту или функции. Там, где указано имя ассемблера для объекта или функции (см. Управление именами, используемыми в ассемблерном коде), атрибут должен следовать за спецификацией asm.
В будущем список спецификаторов атрибутов может быть разрешён после декларатора в определении функции (перед любыми объявлениями параметров старого стиля или телом функции).
Спецификаторы атрибутов могут быть смешаны с квалификаторами типов, появляющимися внутри [] декларатора параметра массива, в конструкции C99, с помощью которой такие квалификаторы применяются к указателю, в который неявно преобразуется массив. Такие спецификаторы атрибутов применяются к указателю, а не к массиву, но в настоящее время это не реализовано и они игнорируются.
Список спецификаторов атрибутов может появиться в начале вложенного декларатора. В настоящее время существуют некоторые ограничения в этом использовании: атрибуты правильно применяются к декларатору, но для большинства отдельных атрибутов подразумеваемая семантика не реализована. Когда спецификаторы атрибутов следуют за * декларатора указателя, они могут быть смешаны с любыми присутствующими квалификаторами типа. Следующее описывает формальную семантику этого синтаксиса. Это имеет наибольший смысл, если вы знакомы с формальным описанием деклараторов в стандарте ISO C.
Рассмотрим (как в подпункте 6.7.5 пункте 4 C99) объявление T
D1, где T содержит спецификаторы объявления, которые задают тип Type (такой как int) и D1 — это декларатор, содержащий идентификатор ident. Тип, заданный для ident для производных деклараторов, тип которых не включает спецификатор атрибута, такой же, как в стандарте ISO C.
Если D1 имеет вид ( attribute-specifier-list D ), и объявление T D задаёт тип «derived-declarator-type-list Type» для ident, то T D1 задаёт тип «derived-declarator-type-list attribute-specifier-list Type» для ident.
Если D1 имеет вид *
type-qualifier-and-attribute-specifier-list D, и объявление T D задаёт тип «derived-declarator-type-list Type» для ident, то T D1 задаёт тип «derived-declarator-type-list type-qualifier-and-attribute-specifier-list указатель на Type» для ident.
Например,
void (__attribute__((noreturn)) ****f) (void);
задаёт тип «указатель на указатель на указатель на указатель на функцию без возвращаемого значения, возвращающую void». В качестве другого примера,
char *__attribute__((aligned(8))) *f;
задаёт тип «указатель на 8-байтовый выровненный указатель на char». Обратите внимание, что это не работает со многими атрибутами; например, использование атрибутов ‘aligned’ и ‘noreturn’, приведённых выше, пока не поддерживается.
Для совместимости со существующим кодом, написанным для версий компилятора, в которых атрибуты не были реализованы для вложенных деклараторов, допускается некоторое послабление в размещении атрибутов. Если атрибут, который применяется только к типам, применяется к объявлению, он рассматривается как применяемый к типу этого объявления. Если атрибут, который применяется только к объявлениям, применяется к типу объявления, он рассматривается как применяемый к этому объявлению; и, для совместимости с кодом, размещающим атрибуты непосредственно перед идентификатором, объявляемым, такой атрибут, применённый к возвращаемому типу функции, рассматривается как применяемый к типу функции, и такой атрибут, применённый к типу элемента массива, рассматривается как применяемый к типу массива. Если атрибут, который применяется только к типам функций, применяется к типу указателя на функцию, он рассматривается как применяемый к типу целевого указателя; если такой атрибут применяется к возвращаемому типу функции, который не является типом указателя на функцию, он рассматривается как применяемый к типу функции.
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/Attribute-Syntax.html