(一)__attribute__理解

    接触Linux一年多的时间,发现自己的技术水平还在原地打转,很不爽。想办法分析原因,发现在看系统代码中很多基础的东西都不知道,追着追着就追不下去了,根本看不懂啊。。。也许以后方向会出现变化,但是暂且不管,遇弱则恶补!

   开个博客,在这里记录每次学习的内容,以及自己的理解。权当给自己留个脚印!希望能坚持住!


__attribute__,不知道是干嘛用的,有一次被面试的时候也提到这个词,当时问了static,violate等等。__attribute__GNUC的一大特色!

一:基础认识

     作用:设置各种属性值,例如函数属性(Function    Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。

    使用格式:__attribute__(attribute_list),有人说这个格式是放在声明之后,“;”之前,不知道这个是不是规定,还是说其他格式也可以?我看到的代码中有不同的情况(这个问题有待后续确认)。例如,函数的属性设置

                                 void __attribute__((noreturn))   protected_mode_jump(u32 entrypoint, u32 bootparams);

先不用管属性值,只看格式,这个地方是放在函数返回值类型和函数名之间的!

     不过在设置变量的属性的时候,确实是放在声明之后,“;”之前。例如下面的例子

                        union c2wr_srq_destroy {
                                 struct c2wr_srq_destroy_req req;
                                 struct c2wr_srq_destroy_rep rep;
                        } __attribute__((packed)) ;

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

上述东西的例子,只是在linux源码中随便搜索得到的。解决了基本概念问题,下面疑惑的就是noreturn,packed等属性值是什么意思了!

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

二:属性值介绍

函数属性值包括noreturn, noinline,always_inline,pure,const, nothrow,sentinel,format,format_arg, no_instrument_function,section,constructor,destructor, used,unused,deprecated,weak, malloc,alias,warn_unused_result andnonnull,当然也有其他的特定系统定义的一些!section这个属性值也支持变量属性和类型属性!

下面介绍几个常看到的属性。

1.__attribute__ noreturn   ---- 函数属性
2.__attribute__ packed    ----- 变量属性
3.__attribute__ const     ---- 函数属性 

//4-6  没写出来

4.__attribute__ weak
5.__attribute__ constructor
6.__attribute__ destructor   

1:__attribute__((noreturn))
       该属性通知编译器函数从不返回值,当遇到类似函数需要返回值而却不可能运行到返回值处就已经退出来的情况,该属性可以避免出现错误信息。

比如一个函数

int foo(void){

     return 0;

}

调用的时候这样调用

int main(void){

foo();

return 0;

}

这个时候编译器就会warning,因为定义了一个有返回值的foo函数,却可能没有返回值,编译器不知道该怎么办了,就报出里warning!

但是如果把int foo(void)函数修改为int __attribute__((noreturn)) foo(void),就不会出现上述报警了!

2:__attribute__((packed))

使用该属性可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节(one byte)对齐,对域(field)是位(one bit)对齐!

__attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。这个功能是跟操作系统没关系,跟编译器有关!


在写到这个地方的时候,很纠结,理解不了。因为参考的很多资料都是照字面意思去翻译英文版的

https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes

导致很晦涩难懂,甚至有的都是错误的!!

在这里举例子说明这个属性(ubuntu 平台下测试):

  1 #include <stdio.h>
  2
  3 struct test{
  4     char a;
  5     int  x[2];//__attribute__((packed));
  6 };
  7
  8 int main(void)
  9 {
 10     printf("sizeof(int) = %d\n",sizeof(int));
 11     printf("sizeof(struct test) = %d\n",sizeof(struct test));
 12     return 0;
 13 } 


上述加不加__attribute__((packed)),第二条打印的结构是不一样的,加的话,sizeof(struct test) = 9;如果不加,sizeof(struct test) = 12!其实这个属性就是设置对齐方式的。

不加的话,编译器按照4字节对齐方式,不足4字节,按4字节计算!  (对这句话,突然有种很熟悉的感觉。。。在C语言面试技巧里面经常出现这种题目,考对齐方式的!)

加的话,编译器会按照最小的对齐方式对齐,所以,上述程序就会按照char(1个字节)对齐,所以输出的结果就是9!

3:__attribute__ const

 该 属性只能用于带有数值类型参数的函数上。当重复调用带有数值参数的函数时,由于返回值是相同的,所以此时编译器可以进行优化处理,除第一次需要运算外,其 它只需要返回第一次的结果就可以了,进而可以提高效率。该属性主要适用于没有静态状态(static state)和副作用的一些函数,并且返回值仅仅依赖输入的参数。例子:

#include <stdio.h>
 
int  __attribute__((const)) inc(int x)
{
    printf("%s(%d)\n", __FUNCTION__, x);
    return x + 1;
}
 
int  inc2(int x)
{
    printf("%s(%d)\n", __FUNCTION__, x);
    return x + 1;
}
 
int main(void)
{
    int i, j;
 
    i = inc(10);
    j = inc(10);
 
    printf("%d %d\n", i, j);
 
    i = inc2(10);
    j = inc2(10);
 
    printf("%d %d\n", i, j);
 
    return 0;
}
用gcc    -o const const.c后运行const, inc(10)就会输出两次。
用gcc -O -o const const.c后运行const, inc(10)只会输出一次。

运行gcc -O -S const_test.c  然后会生成const_test.s,打开const_test.s就可以看到以下内容,看inc1被调用几次!

     .file    "const_test.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string    "%s(%d)\n"
    .text
    .globl    inc
    .type    inc, @function
inc:
.LFB22:
    .cfi_startproc
    pushl    %ebx
    .cfi_def_cfa_offset 8
    .cfi_offset 3, -8
    subl    $24, %esp
    .cfi_def_cfa_offset 32
    movl    32(%esp), %ebx
    movl    %ebx, 12(%esp)
    movl    $__FUNCTION__.1873, 8(%esp)
    movl    $.LC0, 4(%esp)
    movl    $1, (%esp)
    call    __printf_chk
    leal    1(%ebx), %eax
    addl    $24, %esp
    .cfi_def_cfa_offset 8
    popl    %ebx
    .cfi_def_cfa_offset 4
    .cfi_restore 3
    ret
    .cfi_endproc
.LFE22:
    .size    inc, .-inc
    .globl    inc2
    .type    inc2, @function
inc2:
.LFB23:
    .cfi_startproc
    pushl    %ebx
    .cfi_def_cfa_offset 8
    .cfi_offset 3, -8
    subl    $24, %esp
    .cfi_def_cfa_offset 32
    movl    32(%esp), %ebx
    movl    %ebx, 12(%esp)
    movl    $__FUNCTION__.1877, 8(%esp)
    movl    $.LC0, 4(%esp)
    movl    $1, (%esp)
    call    __printf_chk
    leal    1(%ebx), %eax
    addl    $24, %esp
    .cfi_def_cfa_offset 8
    popl    %ebx
    .cfi_def_cfa_offset 4
    .cfi_restore 3
    ret
    .cfi_endproc
.LFE23:
    .size    inc2, .-inc2
    .section    .rodata.str1.1
.LC1:
    .string    "%d %d\n"
    .text
    .globl    main
    .type    main, @function
main:
.LFB24:
    .cfi_startproc
    pushl    %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    pushl    %ebx
    andl    $-16, %esp
    subl    $16, %esp
    movl    $10, (%esp)
    .cfi_offset 3, -12
    call    inc
    movl    %eax, 12(%esp)
    movl    %eax, 8(%esp)
    movl    $.LC1, 4(%esp)
    movl    $1, (%esp)
    call    __printf_chk
    movl    $10, (%esp)
    call    inc2
    movl    %eax, %ebx
    movl    $10, (%esp)
    call    inc2
    movl    %eax, 12(%esp)
    movl    %ebx, 8(%esp)
    movl    $.LC1, 4(%esp)
    movl    $1, (%esp)
    call    __printf_chk
    movl    $0, %eax
    movl    -4(%ebp), %ebx
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    .cfi_restore 3
    ret
    .cfi_endproc
.LFE24:
    .size    main, .-main
    .section    .rodata
    .type    __FUNCTION__.1873, @object
    .size    __FUNCTION__.1873, 4
__FUNCTION__.1873:
    .string    "inc"
    .type    __FUNCTION__.1877, @object
    .size    __FUNCTION__.1877, 5
__FUNCTION__.1877:
    .string    "inc2"
    .ident    "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
    .section    .note.GNU-stack,"",@progbits


用上述方法,可以测试不优化的时候的汇编情况。

自己经常看别人博客的时候,看到很长的就看不下去了,这个就先写这么多吧,其他的有不懂的时候就去到

https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes

去看!

以上的表述只是自己的理解,如果有错误的地方,敬请谅解,顺便烦请回复,好让我做更改,以免有人看到的时候误人子弟!大笑


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值