【GUN】Function-Attributes

函数属性的声明
在GNU C中,您可以声明关于在程序中调用的函数的某些信息,这有助于编译器优化函数调用并更仔细地检查代码。

关键字__attribute__允许您在声明时指定特殊属性。这个关键字后面是一个放在双括号内的属性规范。目前对所有目标平台上的函数定义了以下属性:noreturn、noinline、always_inline、pure、const、format、format_arg、no_instrument_function、section、constructor、destructor、used、unused、deprecated、weak、malloc和alias。针对特定目标系统的函数还定义了其他属性。其他属性,包括section,也适用于变量声明(参见变量属性)和类型声明(参见类型属性)。

您还可以在每个关键字前后使用__来指定属性。这使您可以在头文件中使用它们,而不必担心可能存在与之同名的宏。例如,您可以使用__noreturn__而不是noreturn。

有关使用属性的确切语法的详细信息,请参阅属性语法。

noreturn
一些标准库函数,如abort和exit,不能返回。GCC自动知道这一点。一些程序定义了自己的永远不会返回的函数。您可以将它们声明为noreturn,以告诉编译器这个事实。例如,
void fatal() attribute((noreturn));

void fatal(…)
{
… /打印错误消息。 /
退出(1);
}

noreturn关键字告诉编译器假定fatal不能返回。然后,它可以进行优化,而不必考虑如果fatal确实返回会发生什么。这会产生稍微更好的代码。更重要的是,它有助于避免未初始化变量的虚假警告。

不要假设调用函数保存的寄存器在调用noreturn函数之前恢复。

对于noreturn函数来说,除了void之外,其他返回类型是没有意义的。

noreturn属性在GCC版本2.5之前的版本中未实现。在当前版本和一些旧版本中,声明一个函数不返回的替代方法如下:

typedef void voidfn();

volatile voidfn fatal;

noinline
此函数属性防止将函数内联。

always_inline
通常,除非指定了优化级别,否则不会内联函数。对于声明为内联的函数,即使没有指定优化级别,此属性也会将函数内联。

pure
许多函数除了返回值之外没有任何效果,并且它们的返回值仅取决于参数和/或全局变量。这样的函数可以像算术运算符一样进行公共子表达式消除和循环优化。这些函数应该声明为纯函数。例如,
int square(int) attribute((pure));

表示假设可以调用函数square的次数少于程序中所说的次数。

一些纯函数的常见示例是strlen或memcmp。有趣的非纯函数是具有无限循环或依赖于易变内存或其他系统资源的函数,在两次连续调用之间可能会更改(例如在多线程环境中的feof)。

属性pure在GCC版本2.96之前的版本中未实现。

const
许多函数除了其参数之外不检查任何值,并且没有任何影响,基本上这只是比上面的纯属性稍微严格一些,因为不允许函数读取全局内存。
请注意,具有指针参数并检查指向的数据的函数不能声明为const。同样,通常不能将调用非const函数的函数声明为const。const函数返回void是没有意义的。

属性const在GCC版本2.5之前的版本中未实现。在当前版本和一些旧版本中,声明函数没有副作用的替代方法如下:

typedef int intfn();

extern const intfn square;

这种方法在GNU C++从2.6.0版本开始不适用,因为语言规定const必须附加到返回值上。

格式(原型,字符串索引,第一个要检查的)
format属性指定一个函数采用printf、scanf、strftime或strfmon样式参数,应该根据格式字符串进行类型检查。例如,声明:
extern int my_printf(void *my_object, const char *my_format, …)
attribute((format(printf, 2, 3)));

使得编译器检查对my_printf的调用的参数是否与printf样式的格式字符串参数my_format一致。

参数原型确定如何解释格式字符串,并且应该是printf、scanf、strftime或strfmon。(您还可以使用__printf__,scanfstrftime__或__strfmon。)参数字符串索引指定格式字符串参数是第几个参数(从1开始),而first-to-check是要检查的第一个参数的编号。对于无法检查参数的函数(例如vprintf),将第三个参数指定为零。在这种情况下,编译器仅检查格式字符串的一致性。对于strftime格式,第三个参数必须为零。

在上面的示例中,格式字符串(my_format)是函数my_print的第二个参数,要检查的参数从第三个参数开始,因此格式属性的正确参数是2和3。

格式属性允许您标识自己的函数,它们采用格式字符串作为参数,以便GCC可以检查对这些函数的调用是否存在错误。编译器总是(除非使用-ffreestanding)在请求此类警告时(使用-Wformat)检查标准库函数printf、fprintf、sprintf、scanf、fscanf、sscanf、strftime、vprintf、vfprintf和vsprintf的格式,因此不需要修改头文件stdio.h。在C99模式下,还检查snprintf、vsnprintf、vscanf、vfscanf和vsscanf函数。除非在严格符合C标准的模式下,否则还将检查X/Open函数strfmon,以及printf_unlocked和fprintf_unlocked。请参阅控制C语言方言的选项。

format_arg(字符串索引)
format_arg属性指定函数采用的格式字符串用于printf、scanf、strftime或strfmon类型函数,并修改它(例如将其翻译成另一种语言),以便可以将结果传递给printf、scanf、strftime或strfmon类型函数(其余的参数与修改之前的字符串相同)。例如,声明:
extern char *
my_dgettext(char *my_domain, const char *my_format)
attribute((format_arg(2)));

使得编译器检查对格式函数调用的参数的一致性,其中格式字符串参数是对my_dgettext函数的调用。

参数字符串索引指定格式字符串参数是第几个参数(从1开始)。

format-arg属性允许您标识自己的函数,这些函数修改格式字符串,以便GCC可以检查对printf、scanf、strftime或strfmon类型函数的调用,其中运算数是对您自己函数的调用。除非使用-ansi或适当的-std选项,或使用-ffreestanding,编译器始终以此方式处理gettext、dgettext和dcgettext。请参阅控制C语言方言的选项。

no_instrument_function
如果给出了-finstrument-functions,将在大多数用户编译的函数的入口和退出处生成调用函数的配置文件。具有此属性的函数将不会被这样检测。

section(“section-name”)
通常,编译器将生成的代码放在文本部分。但是,有时候您需要额外的部分,或者您需要特定的函数出现在特殊的部分中。section属性指定函数位于特定部分中。例如,声明:
extern void foobar(void) attribute((section(“bar”)));

将函数foobar放在bar部分中。

某些文件格式不支持任意部分,因此不是在所有平台上都可用section属性。如果您需要将模块的整个内容映射到特定的部分,请考虑使用链接器的功能。

constructor
destructor
constructor属性导致在执行进入main()之前自动调用函数。类似地,destructor属性导致在main()完成或调用exit()后自动调用函数。具有这些属性的函数对于在程序执行期间隐式使用的数据进行初始化非常有用。
这些属性目前不适用于Objective-C。

unused
此属性附加到函数,表示函数可能未使用。对于此函数,GCC不会生成警告。GNU C++目前不支持此属性,因为在C++中,没有参数的定义是有效的。

used
此属性附加到函数,表示必须为该函数生成代码,即使看起来该函数未被引用。这在只在内联汇编中引用函数时非常有用。

deprecated
如果在源文件的任何地方使用了该函数,deprecated属性将导致警告。这对于识别预计将在将来版本的程序中删除的函数非常有用。警告还包括已弃用函数声明的位置,以便用户可以轻松找到有关为什么函数已弃用或应该做什么的更多信息。请注意,警告仅针对使用处:
int old_fn() attribute((deprecated));
int old_fn();
int (*fn_ptr)() = old_fn;

在第3行会生成警告,但第2行不会生成警告。

deprecated属性也可用于变量和类型(请参阅变量属性,请参阅类型属性)。

weak
weak属性导致声明作为弱符号而不是全局符号发布。这主要用于定义可以在用户代码中被覆盖的库函数,尽管它也可以用于非函数声明。对ELF目标支持弱符号,并且在使用GNU汇编器和链接器时也支持a.out目标。

malloc
malloc属性用于告诉编译器,一个函数可能被当作malloc函数对待。编译器假设对malloc的调用会导致指针,这些指针不会与任何内容重叠。这通常会改善优化。

alias(“target”)
alias属性导致声明作为另一个符号的别名发布,必须指定该符号。例如,
void __f() { /* do something */; }
void f() attribute((weak, alias(“__f”)));

声明f是__f的弱别名。在C++中,必须使用目标的修饰名称。

并非所有目标机器都支持此属性。

regparm(number)
在Intel 386上,regparm属性导致编译器将最多number个整数参数传递给寄存器EAX、EDX和ECX,而不是传递到堆栈上。接受可变数量参数的函数将继续在堆栈上传递所有参数。

stdcall
在Intel 386上,stdcall属性导致编译器假定被调用的函数将弹出用于传递参数的堆栈空间,除非它采用可变数量的参数。
Windows NT的PowerPC编译器当前忽略stdcall属性。

cdecl
在Intel 386上,cdecl属性导致编译器假定调用函数将弹出用于传递参数的堆栈空间。这对于覆盖-mrtd开关的效果很有用。
Windows NT的PowerPC编译器当前忽略cdecl属性。

longcall
在RS/6000和PowerPC上,longcall属性导致编译器始终通过指针调用函数,以便可以调用距离当前位置超过64兆字节(67,108,864字节)的函数。
long_call/short_call
此属性允许指定如何在ARM上调用特定函数。这两个属性都会覆盖-mlong-calls(请参阅ARM选项)命令行开关和#pragma long_calls设置。long_call属性导致编译器始终通过将其地址加载到寄存器中,然后使用该寄存器的内容来调用该函数。short_call属性始终将函数从调用点到函数的偏移量直接放入BL指令中。
dllimport
在运行Windows NT的PowerPC上,dllimport属性导致编译器通过由Windows NT dll库设置的全局函数指针来调用函数。指针名称由__imp_和函数名称组成。
dllexport
在运行Windows NT的PowerPC上,dllexport属性导致编译器提供指向函数指针的全局指针,以便可以使用dllimport属性调用它。指针名称由__imp_和函数名称组成。
exception(except-func [,except-arg])
在运行Windows NT的PowerPC上,exception属性导致编译器修改为所声明的函数发出的结构化异常表条目。字符串或标识符except-func放置在结构化异常表的第三个条目中。它表示一个函数,如果发生异常,则由异常处理机制调用它。如果指定了,则字符串或标识符except-arg放置在结构化异常表的第四个条目中。
function_vector
在H8/300和H8/300H上使用此属性,指定应通过函数向量调用指定的函数。通过函数向量调用函数将减少代码大小,但是函数向量具有有限的大小(H8/300最多128个条目,H8/300H最多64个条目)并且与中断向量共享空间。
为了使此属性正常工作,您必须使用GNU binutils版本2.7或更高版本的GAS和GLD。

interrupt
在ARM、AVR、M32R/D和Xstormy16端口上使用此属性,指示所指定的函数是中断处理程序。当存在此属性时,编译器将生成适用于中断处理程序的函数入口和退出序列。注意,函数内部将禁用中断。

注意,在AVR上,中断将在函数内部启用。

注意,对于ARM,您可以通过向中断属性添加一个可选参数来指定要处理的中断类型,如下所示:

void f() attribute((interrupt(“IRQ”)));

此参数的可接受值为:IRQ、FIQ、SWI、ABORT和UNDEF。

interrupt_handler
在H8/300、H8/300H和SH上使用此属性,指示所指定的函数是中断处理程序。当存在此属性时,编译器将生成适用于中断处理程序的函数入口和退出序列。

sp_switch
在SH上使用此属性,以指示interrupt_handler函数应切换到备用堆栈。它需要一个字符串参数,该参数命名为保存备用堆栈地址的全局变量。
void *alt_stack;
void f() attribute((interrupt_handler,
sp_switch(“alt_stack”)));

trap_exit
在SH上,interrupt_handle使用此属性返回使用trapa而不是rte。该属性需要一个整数参数,指定要使用的陷阱号。

eightbit_data
在H8/300和H8/300H上使用此属性,以指示指定的变量应放置在8位数据部分中。编译器将针对8位数据区域上的某些操作生成更高效的代码。请注意,8位数据区域的大小限制为256字节。
要使此属性正常工作,您必须使用GNU binutils版本2.7或更高版本的GAS和GLD。

tiny_data
在H8/300H上使用此属性,以指示指定的变量应放置在小数据部分中。编译器将针对在小数据部分中的数据进行的加载和存储生成更高效的代码。请注意,小数据区的大小略小于32k字节。
signal
在AVR上使用此属性,指示指定的函数是信号处理程序。当存在此属性时,编译器将生成适用于信号处理程序的函数入口和退出序列。函数内部将禁用中断。
naked
在ARM或AVR端口上使用此属性,指示指定的函数不需要编译器生成的序言/尾言序列。由程序员提供这些序列。
model(model-name)
在M32R/D上使用此属性,以设置对象的可寻址性以及函数的生成代码。标识符model-name是小、中或大之一,分别表示每个代码模型。

小型模型对象位于内存的低16MB(以便可以使用ld24指令加载其地址),可以使用bl指令调用。

中型模型对象可以位于32位地址空间中的任何位置(编译器将生成seth/add3指令以加载其地址),可以使用bl指令调用。

大型模型对象可以位于32位地址空间中的任何位置(编译器将生成seth/add3指令以加载其地址),可能不能使用bl指令(编译器将生成更慢的seth/add3/jl指令序列)访问。.

您可以通过在双括号内用逗号分隔它们来在声明中指定多个属性,或者通过在属性声明后立即跟另一个属性声明来指定多个属性。

有些人反对__attribute__功能,建议改用ISO C的#pragma。在设计__attribute__时,有两个原因不这样做。

无法从宏生成#pragma命令。
无法知道同一个#pragma在另一个编译器中可能意味着什么。
这两个原因适用于几乎任何可能提出的应用程序。在使用#pragma的任何应用程序基本上都是一个错误。

ISO C99标准包括_Pragma,现在可以从宏中生成编译指示。此外,当前使用#pragma GCC命名空间作为GCC特定编译指示的一部分。但是,发现使用__attribute__使属性与其相应的声明自然相关,而#pragma GCC对于不自然成为语法的构造很有用。请参阅其他预处理指令。

原文链接:GUN Function-Attributes

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值