__attribute__ в определениях нескольких переменных - PullRequest
0 голосов
/ 04 августа 2020

У меня вопрос, который лучше всего пояснить на примере. Пожалуйста, обратите внимание на следующий код:

unsigned char a,
              b;

Это, очевидно, определяет две переменные типа unsigned char.

Если я хочу, чтобы переменные были выровнены по 16-байтовым границам, мой первый наивный подход был бы следующим:

 __attribute__((aligned(16))) unsigned char a,
                                            b;

Моя проблема в том, что я не уверен, всегда ли компилятор применяет __attribute__((aligned(16))) к обеим переменным.

Я особенно обеспокоен, потому что все следующее код компилируется без ошибок или предупреждений:

unsigned char a __attribute__((aligned(16)));
unsigned char __attribute__((aligned(16))) b;
__attribute__((aligned(16))) unsigned char c;

Согласно моим исследованиям, __attribute__((aligned(16))) делает то же самое с соответствующей переменной в трех строках выше. Но такой слабый синтаксис был бы необычным для C, поэтому я как-то недоверчив.

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

 __attribute__((aligned(16))) unsigned char a;
 __attribute__((aligned(16))) unsigned char b;

или, возможно,

 unsigned char a __attribute__((aligned(16))),
               b __attribute__((aligned(16)));

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

Конечно, этот вопрос относится ко всем атрибутам (не только к атрибуту aligned).

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

Ответы [ 2 ]

3 голосов
/ 04 августа 2020

Да; оба

__attribute__((aligned(16))) unsigned char   a, b;

и

unsigned char __attribute__((aligned(16)))    a, b;

выравнивают a и b по 16-байтовой границе. g cc обрабатывает __attribute__ как часть типа (например, модификаторы const и volatile), так что возможны смешанные вещи вроде

char * __attribute__((__aligned__(16))) *  a;

.

https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html#Attribute -Syntax говорит:

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

Вот почему

unsigned char   a __attribute__((aligned(16))), b;

будет применяться только к a, но не к b.

В другом случае, например,

unsigned char   a, __attribute__((aligned(16))) b;

, выравнивается только b. Здесь

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

от { ссылка } применяется.

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

typedef char __attribute__((__aligned__(16)))   char_aligned_t;
char_alignedt d, d1;

В этом примере и ваш

unsigned char a __attribute__((aligned(16))), a1;
unsigned char __attribute__((aligned(16))) b, b1;
__attribute__((aligned(16))) unsigned char c, c1;

g cc создает (gcc -c), а readelf показывает описанные выравнивания

     8: 0000000000000010     1 OBJECT  GLOBAL DEFAULT  COM a
     9: 0000000000000001     1 OBJECT  GLOBAL DEFAULT  COM a1     <<< not aligned!
    10: 0000000000000010     1 OBJECT  GLOBAL DEFAULT  COM b
    11: 0000000000000010     1 OBJECT  GLOBAL DEFAULT  COM b1
    12: 0000000000000010     1 OBJECT  GLOBAL DEFAULT  COM c
    13: 0000000000000010     1 OBJECT  GLOBAL DEFAULT  COM c1
    14: 0000000000000010     1 OBJECT  GLOBAL DEFAULT  COM d
    15: 0000000000000010     1 OBJECT  GLOBAL DEFAULT  COM d1
1 голос
/ 05 августа 2020

Все кредиты go @ ens c потому что 1) его ответ правильный и 2) он направил меня на правильный путь в отношении документации.

Однако в цитированных им цитатах говорится, что атрибут не применяется ко всему объявлению, а только к соответствующему декларатору. Затем он привел несколько примеров, в которых атрибут применялся ко всему объявлению.

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

Обратите внимание на раздел «Все остальные атрибуты» на этой странице G CC документация. Он содержит следующий абзац (сокращенный и выделенный мной):

Любой список спецификаторов и квалификаторов в начале объявления может содержать спецификаторы атрибутов, независимо от того, есть ли такой список может в этом контексте содержать спецификаторы класса хранения. [...] Все спецификаторы атрибутов в этом месте относятся к объявлению в целом. [...]

Объединение приведенной выше цитаты и цитат из @ens c, ситуация на удивление проста:

  • Если __attribute__ появляется в начале объявления, это относится ко всему объявлению, то есть ко всем деклараторы / объявленные объекты.

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

Единственное, что может ввести в заблуждение в приведенной выше цитате, - это термин «начало объявления». В руководстве G CC не объясняется, что именно начинается с объявления.

Возможно, этот термин заимствован из одной из многих C и связанных спецификаций, но я не нашел краткого определения пока.

По результатам тестирования в

__attribute__((aligned(16))) unsigned char a,
                                           b;

и

unsigned char __attribute__((aligned(16))) a,
                                           b;

атрибут считается частью списка спецификаторов и квалификаторов в начале декларации.

Напротив, в

unsigned char a __attribute__((aligned(16))),
              b;

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

Для меня, как говорящего на английском sh, это очень беспокоит:

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

Пожалуйста, не воспринимайте это как дополнительный вопрос - это скорее дополнительный аспект в этом ответе. Возможно, люди GNU однажды прочтут это и прояснят документы: -)

...