__attribute__

简介

本篇文章翻译自http://nshipster.com/__attribute__/
翻译的不对的地方还请多多包涵指正,谢谢~

正文

这个书刊的一个永恒主题是与编译器良好关系的重要性。像任何工艺一样,一个手工艺人的效率可能取决于他们对待工具的态度。良好的爱护使用他们,他们将回报于你。

__attribute__是一个编译指令,可以指定在声明时的错误检查及高级优化的特性。

这个关键词的语法是__attribute__后面跟了两个小括号(这两个小括号让其很容易区别于宏(make it easy to "macro out"),特别是多个属性的时候)。在小括号里面是属性是用逗号分隔的。__attribute__指令放置在函数,变量或类型的声明后。

// 返回一个数的平方
int square(int n) __attribute__((const));

// 声明一个接口的平台可用性
void f(void) 
__attribute__((availability(macosx,introduced=10.4,deprecated=10.6)));

// 向 stderr 和 exit 中发送诸如打印等的消息
extern void die(const char *format, ...) 
__attribute__((noreturn, format(printf, 1, 2)));

是不是让你想起了标准C中的#pragma?我也是~

事实上,当__attribute__第一次被引入到GCC中时,就遇到一些人的阻力,他们认为使用#pragma可以达到同样的目的。

但是,有两个特别重要的原因解释为何要使用__attribute__

  1. 在宏定义(在C99 _Pragma操作符之前)里使用#pragma指令是不可能的。(因为#符号在宏定义当中有特殊用于,用于获取宏中的参数名)
  2. 我们并不知道#pragma在其他编译器里意味着什么。

引用 GCC Documentation for Function Attributes 里的一句话:

这两种情况几乎发生在任何提议用#pragma的应用上。使用#pragma做任何事情本质上就是个错误。

事实上,如果你去看看现代Objective-C Apple框架的头文件或者一些精心设计的开源项目会发现,__attribute__被用于很多目的。(相反的,#pragma现在主要用于#pragma mark的声明)

所以我们不再啰嗦,让我们看看这些最重要的编译属性吧:

GCC

format

这个属性指定一个函数比如printf,scanf,strftime,strfmon 作为参数,并且通过一个格式化字符串来做类型检查。

extern int
my_printf (void *my_object, const char *my_format, ...)
  __attribute__((format(printf, 2, 3)));

Objective-C 程序员还可以通过使用__NSString__格式达到同样的效果,就像在NSString +stringWithFormat:NSLog()里使用字符串格式一样。

nonnull

这个属性指定函数的的某些参数不能是空指针。

extern void *
my_memcpy (void *dest, const void *src, size_t len)
  __attribute__((nonnull (1, 2)));

使用nonnull能够将可能为空的值暴露出来,这样能够找出任何潜伏在调用代码中的空指针bug。记住:编译错误远早于运行时错误。

noreturn

几个标注库函数,例如abort exit,没有返回值。GCC能够自动识别这种情况。noreturn属性指定像这样的任何不需要返回值的函数。

例如,AFNetworking库为它的网络请求显示入口函数使用了该属性。这个在生成一个专用的线程时使用,保证分离的线程能在应用的整个生命周期继续执行。

pure / const

pure属性指定一个除了返回值没有其他影响的函数,因此函数的返回值只取决于他的入参和全局参数。这样的函数可以作为公共子表达式删除或者循环优化,就像算术符一样。

const属性指定函数不能审查除了她参数的的值,而且不能影响除了返回值的其他值。注意此类函数不能有指针作为参数且指向的数据不能声明为const。同样地,调用非const函数的函数肯定也不是const类型。一个返回voidconst函数是没有意义的(因为此类型函数不能任何其他值产生影响,且参数只能值数值型,如果没有返回值,那执行就没有任何意义)。

int square(int n) __attribute__((const));

pureconst都是会触发具有重大性能优化的函数编程范式的属性。const可以认为是比pure更严格的形式,因为她不依赖全局变量,也不能将指针作为参数,或者更改指针的值。

例如,因为const函数返回值只跟入参有关,这种函数的返回值可以被缓存起来。之后调用该函数可以直接使用返回值。(比如:我们都知道一个数的平方是固定的,所以只需要执行一次算法就行)

unused

这个属性,附加到函数上,意味着这个函数很可能是未被使用的。GCC将不会对这个函数产生警告。

使用__unused属性可以达到同样效果。可以将其声明在在函数实现中没有使用过的参数上。这样能够让编译器做相应的优化。你很可能使用该属性在代理的的实现函数上,因为协议经常提供很多不必要的函数,为了满足许多潜在的情况。

LLVM

就像GCC的许多特性一样,Clang支持__attribute__,而且添加了一些自己的小扩展。为了检查一个特殊属性的可用性,你可以使用__has_attribute指令。

availability

Clang引入了可用性属性,这个属性可以在声明中描述跟系统版本有关的生命周期。例如:

void f(void) __attribute__((availability(macosx,introduced=10.4,deprecated=10.6,obsoleted=10.7)));

availability属性声明f函数是在系统 OS X Tiger 引入的,在系统OS X Snow Leopard不建议使用,在系统OS X Lion被废弃不用的。

Clang编译器用这个信息决定在什么时候使用函数f。例如:Clang需要在OS X Leopard系统下编译,那么调用f函数是OK的。如果Clang在OS X Leopard下编译代码,也能编译成功但是会提示该函数不建议使用。最后,Clang在OS X Lion系统下编译的话,调用就会失败因为函数f已经不再可用了。

availability属性写法是逗号分割的,以平台名称作为第一个参数,之后是一些无序的指定重要的声明信息的句子。

  • introduced: 声明被引入的第一个版本信息。
  • deprecated: 第一次不建议使用的版本,意味着使用者应该移除这个方法的使用。
  • obsoleted: 第一次被废弃的版本,意味着已经被移除,不能够使用了
  • unavailable: 意味着这个平台不支持使用。
  • message: 当Clang发出一些关于废弃或不建议使用的警告时的文本。用于引导使用者不要使用改接口了。

各种类型的availability属性放在声明里,这些属性可能在不同的平台是一致的。只有当属性声明的平台和目标的平台一致时,属性才会生效。

支持的平台有:

  • ios: 苹果的iOS操作系统。最小部署目标平台版本是通过-mios-version-min=*version*-miphoneos-version-min=*version*命令行指定的。
  • macosx: 苹果的OS X操作系统。最小部署目标平台版本是通过-mmacosx-version-min=*version*命令行指定的。

overloadable

Clang在C中提供对C++标准函数重载的支持。函数重载在C中是通过overloadable属性引入的。例如:你可以重载tgsin函数,写出sin函数在入参不同时的不同版本。

#include 
float __attribute__((overloadable)) tgsin(float x) { return sinf(x); }
double __attribute__((overloadable)) tgsin(double x) { return sin(x); }
long double __attribute__((overloadable)) tgsin(long double x) { return sinl(x); }

注意到重载只对函数有效。你可以通过改变返回值类型(例如:id *,void *)来重载函数。


当需要对编译优化时,代码是主要的(Context is king when it comes to compiler optimizations)。通过对代码的限制,你可以有机会让你生成的代码越来越高效。让编译器为你工作,你将会获得奖励。

而且__attribute__不仅仅只是服务于编译器。其他人看你代码时也会看到这些额外的编译属性。所以在编译属性上多做些优化吧,将会有益于你的同伴,接替你代码的人,或者是两年之后忘了关于这个代码一切的你自己。

因为最后,你付出的等于你获得的。

你可能感兴趣的:(__attribute__)