C语言的attribute机制

1. __attribute__作用及使用方法

在Linux内核中经常会遇到这么一类的定义:就是在函数、变量、或者数据类型里面添加__attribute__,对于内核的初学者来说,往往不懂这些定义是什么意思,遇到此类的定义往往需要查找半天,导致学习进度延误。

下面就从__attribute__的作用,约束方法以及使用方法和使用实例上来给大家讲解。

1.1 __attribute__的作用:

	设置函数、变量以及数据类型的属性。

之前大家可能会遇到过:在C语言中对于函数或者变量重复定义的问题,attribute机制可以很好的解决这类的冲突问题;C语言中使用main函数作为入口函数,通常大家理解的在main函数之前,或者main函数结束之后可能不会再运行什么函数了,引入attribute机制就可以解决这类的问题;对于函数中可能直接终止的执行进行不恰当的调用会引起程序无故终止,attribute机制也可以解决这类的问题。

1.2 __attribute__使用方法:

	在函数、数据、数据类型的定义之后,定义的顿号之前。
	语法格式为:
			__attribute__((attr_list))
			请注意在__attribute__后一定是两组小括号,后面跟着我们要设置属性的关键字。
			attribute英文字符的下划线是两个。

比如我们可以设置:
void func(void ) __attribute__((attr_list));
int num __attribute__((attr_list));
struct test { int number; } __attribute__((attr_list))

本文章将会根据__attribute__针对函数属性、变量属性数据类型属性一一用实例讲解。根据gnu的attribute介绍一章,链接如下:gnu Function attribute
gnu Variable attribute

2 函数属性设置

gnu对于函数属性主要设置的关键字如下:

		alias:      设置函数别名。
		aligned:    设置函数对齐方式。
		always_inline/gnu_inline: 
					函数是否是内联函数。
		constructor/destructor:
					主函数执行之前、之后执行的函数。
		format:
					指定变参函数的格式输入字符串所在函数位置以及对应格式输出的位置。
		noreturn:
					指定这个函数没有返回值。
					请注意,这里的没有返回值,并不是返回值是void。而是像_exit/exit/abord那样
					执行完函数之后进程就结束的函数。
		weak:指定函数属性为弱属性,而不是全局属性,一旦全局函数名称和指定的函数名称
			 命名有冲突,使用全局函数名称。

2.1 函数的constructor/destructor属性

一般认为,在C语言中,main函数是函数的执行起点和终点,在main函数之前和之后都不会执行什么额外的步骤。但是gnu引入的constructor和destructor的attribute属性关键字,可以让C函数在执行之前,和执行之后分别执行特定的函数。

编译以下实例,然后执行。

//main.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
static __attribute__((constructor))
before(void)
{
        printf("before main\n");
}

static __attribute__((destructor))
after(void)
{
        printf("after main\n");
}

int main(void)
{
        printf("in main\n");

        return EXIT_SUCCESS;
}

我们可以看到以下执行结果:

before main
in main
after main

在代码中的main函数里面,我们没有使用before和after两个函数,但是在实际执行中before函数在main函数之前执行,after函数在main函数之后执行。这和before和after函数分别使用了attribute属性有关。

2.2 函数的noreturn属性

函数的noreturn属性可以告诉编译器,这个函数是没有返回值的,甚至默认不返回值也是不可以的。

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


void noret(void)
__attribute__((noreturn));

void noret(void)
{

}

int main(void)
{
        noret();
        return 0;
}

如以上代码,我们可以看到noret函数在定义的时候使用了noreturn属性,函数里面什么也没有做,相当于是个空函数,但是空函数使用了默认返回值void。如果我们编译的话,会出现以下警告:

main.c: In function ‘noret’:
main.c:12:1: warning: ‘noreturn’ function does return [enabled by default]
 }
 ^

上述警告告诉我们,这个函数设置的noreturn属性,但是的确返回有返回值。空函数默认返回的void空类型空数据。所以说有返回值。
如果把noret函数里面增加noreturn类型的库函数,或者把noreturn属性去掉,就不会再出现错误了,修改如下:

void noret(void)
{
        _exit(0);
}

2.3 函数weak属性

函数的weak属性比较有意思,它的作用在有名称冲突的函数中,如果我们在使用中有两个函数名称是一样的,我们编译器会报告给我们名称冲突的错误。如果其中一个函数使用了weak属性,我们的编译器就会把不含有weak的函数名称编译进入我们的文件里面。
实例如下:

//test.c
#include "test.h"  
#include <stdio.h>
int weak_func(void)
{
        printf("this is in test weak function\n");
        return 0;  
}
//test.h
#ifndef __test_h_  

#define __test_h_

int weak_func(void);
        
#endif
//main.c
#include <stdio.h>
#include "test.h"


//extern int weak_func(void) __attribute__((weak));
int weak_func(void)
{
        printf("this is in main weak function\n");
        return 0;
}

int main(void)
{
        weak_func();
        return 0;
}

上面的函数编译是编译不通过的,编译结果如下:

gcc main.c test.h test.c
/tmp/cciED8ZA.o: In function `weak_func':
test.c:(.text+0x0): multiple definition of `weak_func'
/tmp/ccZwUE37.o:main.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

显示名称冲突,如果把main.c里面的extern int weak_func(void) __attribute__((weak));去掉注释,则可以编译过,使用的是test.c文件里面的weak_func。

2.4 函数format属性

format属性是在使用变长参数指定格式化字符串的时候使用的比较多。可以让编辑器检查格式化的字符串使用的是否正确。
format使用格式如下:

format (archetype, string-index, first-to-check)
archetype:
			指定参照哪个函数的格式化字符串:printf、scanf、strftime....
string-index:
			指定格式化字符串在函数的第几个参数。
			比如:“%d  %s\n”这些参数在函数的第几个位置。
first-to-check:
			指定格式化输入的字符串在函数参数中开始的位置。

如下代码:

#include <stdio.h>


void myprintf(const char * format,...)
__attribute__((format(printf,1,2)));
void myprintf(const char * format,...)
{
        printf("%s", format);
}

int main(void)
{
        myprintf("abc%d\n", 1);
        myprintf("abc%s\n", "abc");
        myprintf("abc%s\n", 1);
        myprintf("%s %d\n", 2, 1);
        return 0;
}

myprintf定义时候指定参照printf的格式化字符串,1代表指定检查格式化字符串的位置,2指定参照字符串开始检查的位置。
我们

  • 7
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值