Search code examples
cgccgcc-attribute

__attribute__ in definitions of multiple variables


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)?


Solution

  • 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