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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容