Skip to main content
 首页 » 编程设计

C语言gcc的编译属性和选项

2022年07月19日32wayfarer

1.指定内存默认对其参数:

__attribute__((packed)):按一字节对其
__attribute__((aligned(n))):从此之后默认按n字节对其
例如:

struct stu 
{ 
  int a; 
  char b; 
}__attribute__((packed)); 
struct stu 
{ 
  int a __attribute__((aligned(16))); 
  char b; 
};

例子

#include <stdio.h> 
 
struct ss 
{ 
    char a __attribute__((aligned(16))); 
    int b;//① __attribute__((aligned(16))); 
};//②__attribute__((aligned(16))); 
 
void main() 
{ 
    int i; 
    printf("%d\n", sizeof(struct ss)); 
    struct ss s1 = {0x11, 0x55443322}; 
     
    unsigned char *p = (unsigned char *)&s1; 
 
    for(i=0; i<sizeof(s1); i++) 
    { 
        printf("0x%x\n", *(p+i)); 
    } 
} 
输出: 
16 
0x11 
0xd1 
0x98 
0x0 
0x22 
0x33 
0x44 
0x55 
0xe0 
0xfc 
0x98 
0x0 
0xf4 
0xef 
0x98 
0x0

可以看出:

__attribute__((aligned(16)))在哪个域后面修饰哪个域(注意仅对此域有效,对其它域无效),更改了这个域的实际对齐参数,实际对齐参数决定了此域的起始存储位置,
再结合结构体的总大小必须能整除每一个域的最大对齐参数,因此可以推出来结构体的大小和内存的存储关系。
将结构体中注释掉掉的部分加上结构体大小就是32字节。

若只要③处,设定的只是结构体间对齐,结构体成员的存储顺序不变,只是结构体变长了。

1.1、结构体中含有位段的情况

#include <stdio.h> 
 
struct S3_1 
{ 
    char a; 
    short b : 1; 
    short c : 2; 
    short d : 1; 
    int e : 4; 
    int f : 6; 
};  
 
struct S3_1 s3 = {0x11, 1, 3, 1, 0xf, 0x3f}; 
 
void main() 
{ 
    int i; 
 
    printf("sizeof(s3)= %d\n", sizeof(s3)); 
 
    unsigned char *p = (char *)&s3; 
 
    for(i=0; i<sizeof(s3); i++) 
    { 
        printf("0x%x\n", *(p+i)); 
    } 
} 
①GNU的gcc编译器和Windows里的编译器的结果不一样,GNU的gcc输出: 
sizeof(s3)= 4 
0x11 
0xff 
0x3f 
0x0 
即最后面的int型的位段页归并到short里面一起用了。  
 
②在Window上gcc软件编译出输出:  
sizeof(s3)= 8 
0x11 
0x0  
0xf 
0x0 
0xff 
0x3 
0x0 
0x0 
和一般结构体对齐类似,只是其中的位是不是都占用而已。

2.__attribute__((weak))指定一个函数是若函数,消除链接时的multiple definition

(1) 修饰函数

例如:main.c 
#include <stdio.h> 
#define __weak __attribute__((weak)) 
void __weak func(void) 
{ 
  printf("%s\n", "void __atribute__(weak) func(void)"); 
} 
void main() 
{ 
  func(); 
} 
test.c中 
#include <stdio.h> 
void func(void) 
{ 
  printf("%s\n", "void func(void)"); 
}

参考kernel中的 sched_clock(),内核是不允许有两个同名的全局符号的,一个使用了__weak宏进行修饰了。

(2) 修饰变量

weak_test.c

#include <stdio.h> 
 
#define __weak __attribute__((weak)) 
 
extern int __weak i; //防止编译比较严格时报错,这里也要加__weak 
int __weak i = 3; 
 
void main() 
{ 
    printf("i=%d\n", i); 
}

another.c

int i = 2;

两个文件一起编译,打印 i=2,若只编译 weak_test.c,打印 i=3。不会报警告。

其次还可以到处弱引用符号,弱源文件中定义了,包含这个头文件也没问题,kernel中的使用见 kernel/sched/wait.h,其定义:

extern unsigned int __weak walt_rotation_enabled;

3.__attribute__((regparm(0))):告诉gcc编译器该函数不需要通过任何寄存器来传递参数,参数只是通过堆栈来传递。
__attribute__((regparm(3))):告诉gcc编译器这个函数可以通过寄存器传递多达3个的参数,x86PC上,这3个寄存器依次为EAX、EDX 和 ECX。更多的参数才通过堆栈传递。这样可以减少一些入栈出栈操作,因此调用比较快。 ARM核默认是多于4个参数的会被保存在栈中

4.函数没有使用的参数后加__unused可防止编译器报警告

/*加上"__unused"可以防止编译器报警告:warning: unused parameter 'id' [-Wunused-parameter]*/ 
int led_module_open(const struct hw_module_t* module, const char* id __unused,  struct hw_device_t** device) { 
    ......   
}

5. 定义的变量若不使用需要加 __maybe_unused 以防止编译器报警告

static const u32 runnable_avg_yN_inv[] __maybe_unused = { 
    ... 
};

6. 使用 __percpu 将结构体成员定义成 per-cpu 的

struct s_data { 
    struct sched_domain * __percpu *sd; 
    struct root_domain *rd; 
};

7. 内核中对于不能inline的函数使用 noinline 宏修饰

//include/linux/compiler_attributes.h 
#define   noinline    __attribute__((__noinline__)) 
 
static noinline int tracing_mark_write(const char *buf) 
{ 
    ... 
}

注:内核中的gcc编译宏定义在 compiler_attributes.h 中。

8. Linux-5.10 内核中 fallthrough 属性

gcc添加這個屬性原因是以免 switch-case语音漏写break, 使能 gcc 的 __fallthrough__ 属性后,没有写break的位置会报警告,在不需要break的位置需要加上 fallthrough 来显示的说明不需要break,这样就不会报警告了。

/* 
 * 添加伪关键字“fallthrough”,因此 case 语句块必须以以下任何关键字结尾: 
 *   break; 
 *   fallthrough; 
 *   goto <label>; 
 *   return [expression]; 
 * 
 *  gcc: https://gcc.gnu.org/onlinedocs/gcc/Statement-Attributes.html#Statement-Attributes 
 */ 
//include/linux/compiler_attributes.h 
#if __has_attribute(__fallthrough__) 
# define fallthrough                    __attribute__((__fallthrough__)) 
#else 
# define fallthrough                    do {} while (0)  /* fallthrough */ 
#endif

例子:

long do_futex(int cmd, ...) { 
    ... 
    switch (cmd) { 
    case FUTEX_WAIT: 
        val3 = FUTEX_BITSET_MATCH_ANY; 
        fallthrough; 
    case FUTEX_WAIT_BITSET: 
        return futex_wait(uaddr, flags, val, timeout, val3); 
    ... 
}

注:linux内核中的gcc编译宏定义在文件 include/linux/compiler_attributes.h 中。

9. C/C++中 constructor 和 destructor 属性

若函数被设定为 constructor 属性,则该函数会在 main() 函数执行之前被自动的执行。类似的,若函数被设定为 destructor 属性,则该函数会在 main() 函数执行之后或者 exit() 被调用后被自动的执行。当存在多个 __attribute__((constructor)) 修饰的函数时,也不用持锁,因为是单线程执行的。还可以使用 __attribute__((constructor(prio))) 指定优先级,从而决定调用次序,优先级prio取值范围是 0--65535,其中 0--100 是预留的,不能使用,优先级数值越小优先级越高,越优先被执行调用。只要指定了优先级,就算是指定的优先级是65535也还是会比没有指定优先级的先调用。

#include<stdio.h> 
 
__attribute__((constructor)) void before_main()   
{   
   printf("before main.\n");   
}   
   
__attribute__((destructor)) void after_main()   
{   
   printf("after main.\n");   
}   
   
int main()   
{   
   printf("in main.\n");   
   return 0;   
} 
 
/* 
//执行结果: 
$ ./pp 
before main. 
in main. 
after main. 
*/

使用此属性实现类似 module_init() 的设计模式,参考《利用 constructor 属性完成 module_init() 设计模式

10. section 属性,使用方式有两种

__attribute__((__section__("section_name"))) 
__attribute__((section(".my.init")))

具体说明与使用见《attribute section 属性》

11. used 属性

__attribute__((__used__))

在gcc手册中找到了有关的解释:
used: This attribute, attached to a function, means that code must be emitted for the function even if it appears that the function is not referenced. This is useful, for example, when the function is referenced only in inline assembly.
这个属性,附加在一个函数上,意味着即使函数看起来没有被引用,也必须为该函数编译出代码。例如,当函数仅在内联汇编中被引用时,这很有用。也就是向编译器说明这段代码有用,即使在没有用到的情况下编译器也不会警告!

12. unused 属性

__attribute__((__unused__))

在gcc手册中找到了有关的解释:
unused:This attribute, attached to a function, means that the function is meant to be possibly unused. GCC will not produce a warning for this function.
表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息。


本文参考链接:https://www.cnblogs.com/hellokitty2/p/7279562.html