I have a question which is best explained by example. Please consider the following code:
unsigned char a,
b;
This obviously defines two variables of type unsigned char
.
If I would like to make the variables aligned to 16-byte-boundaries, my first naive approach would be this:
__attribute__((aligned(16))) unsigned char a,
b;
My problem is that I am not sure whether the compiler always applies __attribute__((aligned(16)))
to both variables.
I am particularly worried because all of the following code is compiled without errors or warnings:
unsigned char a __attribute__((aligned(16)));
unsigned char __attribute__((aligned(16))) b;
__attribute__((aligned(16))) unsigned char c;
According to my research, __attribute__((aligned(16)))
does the same to the respective variable in the three lines above. But such a weak syntax would be unusual for C, so I am somehow mistrustful.
Returning to my original problem, I am aware that I easily could avoid the uncertainty by something like
__attribute__((aligned(16))) unsigned char a;
__attribute__((aligned(16))) unsigned char b;
or perhaps
unsigned char a __attribute__((aligned(16))),
b __attribute__((aligned(16)));
But I really would like to know whether it is sufficient to add the __attribute__
decoration once when declaring multiple variables which all should have the attribute.
Of course, that question relates to all attributes (not only the aligned
attribute).
As a bonus question, is it considered good style to add such attributes not only to the variable definitions, but also to the variable declarations (e.g. in header files)?
Yes; both
__attribute__((aligned(16))) unsigned char a, b;
and
unsigned char __attribute__((aligned(16))) a, b;
align a
and b
to 16 byte boundary. gcc handles __attribute__
as part of the type (like const
and volatile
modifiers) so that mixed things like
char * __attribute__((__aligned__(16))) * a;
are possible too.
https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html#Attribute-Syntax says:
An attribute specifier list may appear immediately before the comma, = or semicolon terminating the declaration of an identifier other than a function definition. Such attribute specifiers apply to the declared object or function
That is why
unsigned char a __attribute__((aligned(16))), b;
would apply to a
only but not to b
.
In another case like
unsigned char a, __attribute__((aligned(16))) b;
only b
is aligned. Here
An attribute specifier list may appear immediately before a declarator (other than the first) in a comma-separated list of declarators ... Such attribute specifiers apply only to the identifier before whose declarator they appear
from https://stackoverflow.com/a/31067623/5639126 applies.
To avoid all the ambiguities, it would be better to create a new type and use this. E.g.
typedef char __attribute__((__aligned__(16))) char_aligned_t;
char_alignedt d, d1;
With this example and your
unsigned char a __attribute__((aligned(16))), a1;
unsigned char __attribute__((aligned(16))) b, b1;
__attribute__((aligned(16))) unsigned char c, c1;
gcc creates (gcc -c
) and readelf
shows the described alignments
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