6.31 Синтаксис атрибутов
В этом разделе описывается синтаксис, с помощью которого __attribute__ могут быть использованы в языке C, а также конструкции, к которым привязываются спецификаторы атрибутов. Некоторые детали могут отличаться для C++ и Objective-C. Из-за недостатков в грамматике атрибутов некоторые формы, описанные здесь, могут не быть успешно проанализированы во всех случаях.
Существуют некоторые проблемы с семантикой атрибутов в C++. Например, нет манглинга для атрибутов, хотя они могут влиять на генерацию кода, поэтому могут возникнуть проблемы при использовании атрибутированных типов совместно с шаблонами или перегрузкой. Аналогично, typeid не различает типы с различными атрибутами. Поддержка атрибутов в C++ может быть ограничена в будущем только атрибутами в объявлениях, но не вложенными деклараторами.
См. Атрибуты функций для подробностей о семантике атрибутов, применяемых к функциям. См. Атрибуты переменных для подробностей о семантике атрибутов, применяемых к переменным. См. Атрибуты типов для подробностей о семантике атрибутов, применяемых к структурам, объединениям и перечислениям.
Спецификатор атрибута имеет вид __attribute__ ((список-атрибутов)). Список атрибутов — это, возможно, пустая последовательность атрибутов, разделенных запятыми, где каждый атрибут является одним из следующих:
- Пустое значение. Пустые атрибуты игнорируются.
- Слово (которое может быть идентификатором, таким как
unused, или ключевым словом, таким какconst). - Слово, за которым следуют в скобках параметры для атрибута. Эти параметры принимают один из следующих форм:
- Идентификатор. Например, атрибуты
modeиспользуют эту форму. - Идентификатор, за которым следует запятая и список выражений, разделенных запятыми. Например, атрибуты
formatиспользуют эту форму. - Возможный пустой список выражений, разделенных запятыми. Например, атрибуты
format_argиспользуют эту форму со списком из одного целочисленного константного выражения, а атрибутыaliasиспользуют эту форму со списком из одной строковой константы.
- Идентификатор. Например, атрибуты
Список спецификаторов атрибутов — это последовательность одного или нескольких спецификаторов атрибутов, не разделенных другими токенами.
В GNU C список спецификаторов атрибутов может появляться после двоеточия, следующего за меткой, кроме метки case или default. Единственный атрибут, который имеет смысл использовать после метки, — это unused. Эта функция предназначена для кода, генерируемого программой, который может содержать неиспользуемые метки, но который компилируется с -Wall. Обычно его не следует использовать в коде, написанном человеком, хотя он может быть полезен в тех случаях, когда код, который переходит к метке, содержится в #ifdef условном выражении. GNU C++ разрешает атрибуты в метках только в том случае, если спецификатор атрибута непосредственно следует за точкой с запятой (то есть метка относится к пустому оператору). Если точка с запятой отсутствует, атрибуты меток C++ являются неоднозначными, так как в C++ разрешено объявление, которое может начинаться со списка атрибутов, имеющее метку. Объявления не могут быть помечены в C90 или C99, поэтому неоднозначность там не возникает.
Список спецификаторов атрибутов может появляться в качестве части спецификатора 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 имеет вид ( список-спецификаторов-атрибутов D ), и объявление T D задает тип «список-типов-производного-декларатора Type» для ident, то T D1 задает тип «список-типов-производного-декларатора список-спецификаторов-атрибутов Type» для ident.
Если D1 имеет вид *
список-спецификаторов-атрибутов-и-квалификаторов-типа D, и объявление T D задает тип «список-типов-производного-декларатора Type» для ident, то T D1 задает тип «список-типов-производного-декларатора список-спецификаторов-атрибутов-и-квалификаторов-типа указатель на 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-4.9.4/gcc/Attribute-Syntax.html