33

Does the __attribute__ directive apply to all the members declared on one line?


int a, b, c;

Declares three int variables.


int *a, b, c;

Declares variable "a" as a pointer to int, and b and c as int.


int __attribute__((used)) a, b, c;

Does the used attribute apply to all variables or only to a?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
jbl
  • 582
  • 4
  • 14
  • 12
    You could probably test this. attributes are not part of the C standard, so every compiler can do whatever they want. Just create a simple program like you have, link it and do a dump of the symbols. – Mark Lakata Jun 26 '15 at 07:26
  • 6
    For the same reason the pointer declaration is vague (i.e. `int* a,b;` does not declare b as a pointer), it is recommended in general to NEVER declare more than one variable per line. Then there is no ambiguity. Same applies to `__attributes__` – Mark Lakata Jun 26 '15 at 07:28
  • Well the question is if the `__attribute__` effects the type (left side) or the variable (right side). Since `used` is variable attribute [https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#Common-Variable-Attributes] and not a type attribute [https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#Common-Type-Attributes] it should affect only the variable `a`. – harper Jun 26 '15 at 07:33
  • @MarkLakata For virtually any of my colleagues it is obvious that b is not a pointer. I know it was discussed too many times and what arguments are, but I think it has to be something with local education, not "common sense". That's why no one except minor monkeys around would write ambiguous `int* a, b`. – user3125367 Jun 26 '15 at 07:54
  • In any case it needs to be said that source lines are irrelevant; what could have been relevant was not “on one line” but “up to the next semicolon”. – PJTraill Jun 26 '15 at 10:22
  • [Here](https://godbolt.org/z/oo987r) are some examples on godbolt. Note that they contradict several of the given answers. – Nate Eldredge Aug 04 '20 at 16:10

4 Answers4

26

From GCC: Attribute-Syntax:

An attribute specifier list may appear immediately before a declarator (other than the first) in a comma-separated list of declarators in a declaration of more than one identifier using a single list of specifiers and qualifiers. Such attribute specifiers apply only to the identifier before whose declarator they appear. For example, in

__attribute__((noreturn)) void d0 (void),
     __attribute__((format(printf, 1, 2))) d1 (const char *, ...),
      d2 (void);

the noreturn attribute applies to all the functions declared; the format attribute only applies to d1.


Correction: As the comment points out, my previous conclusion is incorrect. I didn't notice the other than the first part.

Modified conclusion:

In both

int __attribute__((used)) a, b, c;

and

__attribute__((used)) int a, b, c;

The attribute applies to all a, b, and c.

But if it were:

int a, __attribute__((used)) b, c;

The attribute would apply to b only.

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
  • 2
    the conclusion is wrong; there is no difference between `__attribute__ int var` and `int __attribute__ var` because "var" is not an "other than the first" . – ensc Aug 04 '20 at 13:28
  • I'm somewhat shocked to realize how quickly legit questions are closed in SO, while accepted answers which are incorrect (like this one) are not detected, and even upvoted. For the correct answer, which shows why this one is wrong, read: https://stackoverflow.com/questions/63245625/attribute-in-definitions-of-multiple-variables – cesss Feb 20 '21 at 10:56
6

gcc documentation (6.36 Attribute Syntax) says it only applies to the identifier before whose declarator they appear:

An attribute specifier list may appear immediately before a declarator (other than the first) in a comma-separated list of declarators in a declaration of more than one identifier using a single list of specifiers and qualifiers. Such attribute specifiers apply only to the identifier before whose declarator they appear. For example, in

__attribute__((noreturn)) void d0 (void),
     __attribute__((format(printf, 1, 2))) d1 (const char *, ...),
      d2 (void);

So in your example:

int __attribute__((used)) a, b, c;

the attribute only applies to a.

ouah
  • 142,963
  • 15
  • 272
  • 331
  • 1
    As with Yu Hao's answer, since `a` is the *first* declarator in the list, and this paragraph only applies when the attribute specifier list applies immediately before a declarator *other than the first*, it does not apply here, and this logic is incorrect. See https://stackoverflow.com/questions/63245625/attribute-in-definitions-of-multiple-variables. – Nate Eldredge Aug 04 '20 at 15:39
2

Referring the GCC document,

The keyword __attribute__ allows you to specify special attributes when making a declaration. This keyword is followed by an attribute specification inside double parentheses. Nine attributes, noreturn, const, format, no_instrument_function, section, constructor, destructor, unused and weak are currently defined for functions. Other attributes, including section are supported for variables declarations (see section 4.29 Specifying Attributes of Variables) and for types (see section 4.30 Specifying Attributes of Types).

Section 4.29: Attributes of Variables

unused:
This attribute, attached to a variable, means that the variable is meant to be possibly unused. GNU CC will not produce a warning for this variable.

Section 4.30: Attributes of Types

unused:
When attached to a type (including a union or a struct), this attribute means that variables of that type are meant to appear possibly unused. GNU CC will not produce a warning for any variables of that type, even if the variable appears to do nothing. This is often the case with lock or thread classes, which are usually defined and then not referenced, but contain constructors and destructors that have nontrivial bookkeeping functions

WedaPashi
  • 3,561
  • 26
  • 42
1

The answer is that the attribute probably doesn't do anything.

$ cat f.c
int foo_f1;
int __attribute__((used)) foo_f2;

main()
{
}

and

$ cat g.c
int foo_g1;
int __attribute__((used)) foo_g2;

build f as obj, g as library

$ gcc    -c -o g.o g.c
$ ar rs libg.a g.o
$ gcc -O3 f.c -lg

$ objdump.exe -t a.exe  | grep foo
[532](sec  6)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000100 _foo_f1
[599](sec  6)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000104 _foo_f2

Basically, the linker didn't remove any symbols from f.c and removed everything from g.c, even with the __attribute__((used)).

Mark Lakata
  • 19,989
  • 5
  • 106
  • 123
  • 1
    But that isn't what `__attribute__((used))` is supposed to do. It only controls whether a `static` variable that's otherwise unused may be optimized out from the assembly output. It has no effect on non-`static` variables, and no effect on what the linker decides to do. – Nate Eldredge Aug 04 '20 at 15:45
  • @NateEldredge that's good to know, but I was just addressing the OP's example that was not static. – Mark Lakata Aug 05 '20 at 21:43