C attribute

table of Contents

 

C++ attribute

definition

Explanation

Standard attributes 

gnu attribute

Set attributes for variables

aligned(alignment)

packed

Set properties for functions

format (archetype, string-index, first-to-check)

noreturn

weak

weakref,weakref ("target")

hot

unused

used

always_inline


C++ attribute

definition

To set attributes for functions, variables or types, the syntax structure is as follows: [[attribute-list]] (c ++ 11) and [[using attribute-namespace: attribute-list]] (C ++ 17)

Where attribute-list is a comma-separated sequence of zero or more attributes (may end with an ellipsis ... to indicate packet expansion)

Identifier Simple attributes, such as [[noretrun]]
attribute-namespace::Identifier An attribute with a namespace, for example [[gnu :: unused]]
Identifier(argument-list) Attributes with parameters, such as [[deprecated ("because")]]
attribute-namespace::Identifier(argument-list) Attributes with both namespace and parameter list

If using: namespace appears at the beginning of the attribute list, and no other attributes in the attribute list specify a namespace: the namespace specified in using will be applied to all attributes:

[[using CC: opt(1), debug]] // same as [[CC::opt(1), CC::debug]]
[[using CC: CC::opt(1)]] // error: cannot combine using and scoped attribute

Explanation

attribute provides a unified standard syntax for implementation-defined language extensions, such as gnu and ibm language extension __attribute __ ((...)), Microsoft extension __declspec (), etc.

Attributes can be used almost everywhere in C ++ programs and can be applied to almost everything: types, variables, functions, names, code blocks, and entire translation units, although each specific attribute is only valid if the implementation allows: [[expect_true]] can be an attribute that can only be used with if but not with class declarations. [[omp :: parallel ()]] can be applied to code blocks or for loop attributes, but not to int types, etc. (note that these two attributes are fictitious examples, please see the following standard attributes and some non- Standard attribute)              

In a declaration, attributes can appear before the entire declaration or directly after the name of the entity being declared. In this case, they are grouped together. In most other cases, attributes apply to entities that are directly in front.              

alignas specifier is part of the sequence of attribute specifiers, although it has a different syntax. It may appear where the [[…]] attribute appears and may be mixed with them (provided that it is used with alignas allowed)              

Only when the attribute specifier is introduced or within the attribute parameter, two consecutive left square bracket marks ([[).

void f() {
  int y[3];
  y[[] { return 0; }()] = 1;    // error
  int i [[cats::meow([[]])]]; // OK
}

In addition to the standard attributes listed below, the implementation can also support any non-standard attributes with implementation-defined behavior. Ignore all properties whose implementation is unknown without causing errors. (C ++ 17)

Standard attributes 

Only the following attributes are defined by the C ++ standard.

[[noreturn]] Instruct the function not to return
[[carries_dependency]] Instruct to release the use of the dependency chain in std :: memory_order to propagate inside and outside the function

[[deprecated]]

[[deprecated]]

Indicates that the name or entity declared with this attribute is allowed, but it is discouraged for some reasons
[[fallthrough]] Indicate that the fall-through in the previous case tag is intentional, and the compiler should not issue a warning when it falls
[[nodiscard]] Encourage the compiler to issue a warning when giving up the return value 
[[maybe_unused]] Disable the compiler from warning about unused entities (if any)

[[likely]]

[[unlikely]]

Instructs the compiler to optimize for the possibility that the execution path through the statement is greater or smaller than any other execution path
[no_unique_address]] Indicates that the non-static data member does not need to have a different address than all other non-static data members of its class  
[[optimize_for_synchronized]] Indicates that the function definition should be optimized to be called from synchronized statements

gnu attribute

Since __attribute __ ((...)) is more widely used in gnu, we will focus on __attribute __ ((...)) below

Set attributes for variables

aligned(alignment)

The aligned attribute specifies the minimum alignment (in bytes) of the variable or structure field. When specified, the alignment must be an integer constant power of 2. Specifying the misalignment parameter means the maximum alignment of the target, which is usually, but not always, 8 or 16 bytes. E.g,

#include <stdio.h>

int x __attribute__ ((aligned (16))) = 0;
int y __attribute__ ((aligned (16))) = 0;

int main()
{
  x=1;
  y=2;
}

Our dynamic debugging can find that the variables are aligned with 16 bits

   0x5555555545fb <main+1>:	mov    rbp,rsp
   0x5555555545fe <main+4>:	mov    DWORD PTR [rip+0x200a18],0x1        # 0x555555755020 <x>
=> 0x555555554608 <main+14>:	mov    DWORD PTR [rip+0x200a1e],0x2        # 0x555555755030 <y>

packed

The packed attribute specifies that the structure members should have the smallest possible alignment. The bit field is one bit, otherwise it is one byte, unless a larger value is specified using the aligned attribute. This property does not apply to non-member objects.

For example, in the following structure, the member array x is packed so that it follows immediately after a, without intermediate padding. In the following example, we can see the obvious difference

#include <stdio.h>

struct foo
{
char a;
int x[2] __attribute__ ((packed));
} f;

struct fooo
{
char a;
int x[2];} fo;

int main()
{
  printf("%ld\n",sizeof(f));
  printf("%ld\n",sizeof(fo));
}

Set properties for functions

format (archetype, string-index, first-to-check)

The format attribute specifies that the function accepts printf, scanf, strftime, or strfmon style parameters. These parameters should be type-checked according to the format string. For example, the statement:

void Printf(const char *Fmt, ...) __attribute__ ((format (printf, 1, 2)));

Make the compiler check whether the parameters when calling Printf are consistent with the printf style format string parameter Fmt. Here we give an example:

#include <stdio.h>
#include <stdarg.h>

void Printf(const char *Fmt, ...) __attribute__ ((format (printf, 1, 2)));

static FILE *OutputFile = stdout;

void Printf(const char *Fmt, ...) {
  va_list ap;
  va_start(ap, Fmt);
  vfprintf(OutputFile, Fmt, ap);
  va_end(ap);
  fflush(OutputFile);
}


int main ()
{
   Printf("i=%d\n",6);
   Printf("i=%s\n",6);
   Printf("i=%s\n","abc");
   Printf("%s,%d,%d\n",1,2);
   return 0;
}

noreturn

Some standard library functions, such as exceptions and exits, cannot return. gcc knows this automatically. Some programs define their own functions, and these functions never return. You can declare them as noreturn to tell the compiler this fact. E.g,

void fatal () __attribute__ ((noreturn));
void
fatal (/* . . . */)
{
/* . . . */ /* Print error message. */ /* . . . */
exit (1);
}

The noreturn keyword tells the compiler to assume that fatal cannot return. Then, it can be optimized regardless of what happens if the deadly weapon really comes back. This makes the code slightly better. More importantly, it helps to avoid false warnings of uninitialized variables.

The noreturn keyword does not affect the exception path when applied: functions marked with noreturn can still return to the caller by raising an exception or calling longjmp.

It is meaningless for the return type of the noreturn function to not be void. Let ’s take an example

Noreturn is not used:

#include<stdio.h>
#include<stdlib.h>


void myexit(int i) 
{
	exit(i);
}

int main()
{
	myexit(2);
	printf("yes\n");
	return 0;
}

Open with ida

Use noreturn

#include<stdio.h>
#include<stdlib.h>

void myexit(int i) __attribute__((noreturn));

void myexit(int i) 
{
	exit(i);
}

int main()
{
	myexit(2);
	printf("yes\n");
	return 0;
}

Open with ida

The constructor, destructor, constructor (priority), and destructor (priority)
constructor function properties are automatically called before execution enters main (). Similarly, the destructor function attribute will automatically call the function after calling main () or exit (). Functions with these properties are very useful for initializing data that is implicitly used during program execution.

 On some targets, the property also accepts an integer parameter to specify the priority to control the running order of the constructor and destructor. The constructor function with a lower priority number runs before the constructor function with a larger priority number; the reverse relationship of the destructor function holds. Therefore, if there is a constructor that allocates resources and a destructor that releases the same resources, these two functions usually have the same priority. The priority of the constructor and destructor functions is the same as the priority specified by the namespace scope C ++ object. However, the order in which the constructors of C ++ objects with static storage duration and decorated with attribute constructors are currently called is unspecified. In mixed declarations, the attribute init_priority can be used to force the order.

 The parameter form of using constructor and destructor function attributes on targets that do not support this function will be rejected with an error. Below we give an example

#include <stdio.h>
#include <stdlib.h>
 
__attribute__((constructor(101))) void before_1()
{
    printf("\n%d before_1\n",__LINE__);
}
 
__attribute__((destructor(101))) void after_1()
{
    printf("\n%d after_1\n",__LINE__);
}

__attribute__((constructor(102))) void before_2()
{ 
    printf("\n%d before_2\n",__LINE__);
}
 
__attribute__((destructor(102))) void after_2()
{
    printf("\n%d after_2\n",__LINE__);
}
 
int main(int args,char ** argv)
{
    printf("\n%d main\n",__LINE__);
    return EXIT_SUCCESS;
}

Note that when the priority in constructor (priority) and destructor (priority) is [0.100], there will be the following warning:

weak

Weak attributes cause declarations to be issued as weak symbols rather than global symbols. This is mainly used to define library functions, which can be rewritten in user code, but can also be used for non-function declarations. When using the GNU assembler and linker, both the ELF target and the A.OUT target support weak symbols. Detailed usage can enter https://blog.csdn.net/zhang14916/article/details/98486959 View

weakref,weakref ("target")

The weakref attribute marks the declaration as a weak reference. If there are no parameters, it should be accompanied by an alias attribute, which names the target symbol. Alternatively, you can use the target as a parameter of weakref itself. In both cases, weakref implicitly marks the statement as weak. If there is no target as a parameter of weakref or alias, weakref is equivalent to a weak symbol.  

static int x() __attribute__ ((weakref ("y")));
/* is equivalent to... */
static int x() __attribute__ ((weak, weakref, alias ("y")));
/* and to... */
static int x() __attribute__ ((weakref));
static int x() __attribute__ ((alias ("y")));

 The weak reference is an alias, which itself does not need to provide a definition for the target symbol. If the target symbol is only referenced by a weak reference, it will become a weak undefined symbol. However, if directly quoted, such strong references shall prevail, and the symbols need to be defined, not necessarily in the same translation unit. Below we give a few examples

//example11.c
#include <stdio.h>
#include <stdlib.h>
static int x() __attribute__ ((weakref ("y")));
static int xx() __attribute__ ((weakref, alias ("yy")));
static int xxx() __attribute__ ((weakref));
static int xxxx() __attribute__ ((alias ("yyyy")));
int yy()
{
	printf("%s\n",__FUNCTION__);
}

int y()
{
	printf("%s\n",__FUNCTION__);
}

int yyyy()
{
	printf("%s\n",__FUNCTION__);
	return 0;
}
int main()
{
    if (x){
	x();
        y();
    }
    else{
        printf("x=%p\n", x);
    }
    if (xx){
	xx();
        yy();
    }
    else{
        printf("y=%p\n", xx);
    }
    if (xxx){
        xxx();
    }
    else{
        printf("xxx=%p\n", xxx);
    }
    if (yyyy){
	xxxx();
        yyyy();
    }
    else{
        printf("yyyy=%p\n", yyyy);
    }
    return 0;
}
//example12.c
#include <stdio.h>
#include <stdlib.h>
int xxx()
{
	printf("%s\n",__FUNCTION__);
}

hot

The hot attribute on the label is used to inform the compiler that the path behind the label is more likely than the path without comments. This attribute is used when the built-in expectation cannot be used, for example, with a calculated goto or asm goto.

unused

This function is used for code generated by the program. These codes may contain unused tags, but are compiled with '-wall'. It is usually not appropriate to use manually written code in it, but it may be useful if the code that jumps to the tag is included in the ifdef condition. Let's take an example

//example.c
#include <stdio.h>
int main(void)
{
    printf("main\n");
}

static void a(void)
{
    printf("a\n");
}

When we compile with gcc example.c -Wall -o example warning: 'a' defined but not used, when we add attribute __attribute __ ((unused)) to static void a (void), this will be eliminated caveat.

used

This property attached to a function means that code must be generated for the function, even if the function does not appear to be referenced. For example, this is useful when the function is only referenced in an inline assembly.

When applied to a member function of a C ++ class template, this property also means that if the class itself is instantiated, the function is instantiated. Below we give a small example to demonstrate

//example11.c
#include "example11.h"

int main()
{
	a();
	printf("%d %s()\n",__LINE__,__FUNCTION__);
	return 0;
}
#include <stdio.h>
static void a();
static void b();
static void c() __attribute__((used));
void a()
{
	printf("%d %s()\n",__LINE__,__FUNCTION__);
}
void b()
{
	printf("%d %s()\n",__LINE__,__FUNCTION__);
}
void c()
{
	printf("%d %s()\n",__LINE__,__FUNCTION__);
}

 We use -O0 to optimize compilation, use ida to open, we found

always_inline

Normally, unless optimization -O2 is specified, the function is not inlined. For functions declared inline, this attribute is inlined without any restrictions on inlining. Failure to inline such a function is diagnosed as an error. Note that if such a function is called indirectly, the compiler may or may not inline it, depending on the optimization level, and inline indirect call failure may or may not be diagnosed. Here we give an example:

//example.c
#include <stdio.h>
inline void a(){
	printf("%d %s()\n",__LINE__,__FUNCTION__);
}

int main()
{
	a();
	printf("%d %s()\n",__LINE__,__FUNCTION__);
	return 0;
}

 

//example11.c
#include <stdio.h>
inline __attribute__((always_inline)) void a(){
	printf("%d %s()\n",__LINE__,__FUNCTION__);
}

int main()
{
	a();
	printf("%d %s()\n",__LINE__,__FUNCTION__);
	return 0;
}

 

 cleanup(cleanup_function)

When the variable is out of range, the cleanup attribute runs the function. This attribute can only be applied to automatic function scope variables; it cannot be applied to parameters or variables with static storage duration. The function must take one parameter, a pointer to a type compatible with the variable. The return value of the function (if any) will be ignored. If '-fexceptions' is enabled, the clear function is run during stack expansion that occurs during exception handling. Note that the cleanup attribute does not allow catching exceptions, only operations. If the cleanup function does not return normally, what happens will be undefined.

(To be continued)

Published 43 original articles · Like 23 · Visits 30,000+

Guess you like

Origin blog.csdn.net/zhang14916/article/details/102624748