Tips of Linux C programming
浏览:3977次 出处信息
1. 优雅地使用链表
链表是编程中经常要用到的数据结构,结构体描述时分为数据域和指针域,本没有什么好讲。但有没有想过教科书上的这种方式有什么问题?通过这种方式定义和使用链表,对于不同的链表类型,都要定义各自的链表结构,繁琐的很。linux kernel中链表的用法才应该是教科书中出现的。
基本思想:在Linux内核链表中,不是在链表结构中包含数据,而是在数据结构中包含链表节点。
1) 链表定义:
struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) }
2) 链表使用者定义:
struct user_t { data domain; list_head * node; }; struct user_t g_user_list; LIST_HEAD_INIT(&g_user_list);
3) 通过node定位user_t:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #define container_of(ptr, type, member) ({ const typeof(((type *)0)->member) * __mptr = (ptr); (type *)((char *)__mptr - offsetof(type, member)); }) struct user_t* next = container_of(g_user_list->next->node, struct user_t, node);
这里用到了container_of,container_of又用到了offsetof。offsetof是通过将结构体起始地址强制对齐到0来计算出node和起始地址的偏移offset;而container_of在node地址基础上减去offset得到user_t结构体的地址并返回user_t。
2. 高效地分支判断
写程序不可避免的需要使用if/else,如何高效地进行分支判断呢?使用likely/unlikely。
#define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) long __builtin_expect (long EXP, long C)
__builtin_expect是一个GCC内置的函数,语义是表示EXP == C,返回值是表达式的值。这两个宏定义是为了提示编译器正确地进行分支判断的优化。
优化的原理是:通过调整生成汇编代码的顺序,将经常发生的分支代码放在cmp指令之后顺序执行,将不经常发生的分支代码通过jump指令跳过去,从而降低jump指令清空处理器流水线的影响。
3. 汇编实现的原子操作
__asm__ __volatile__( " lock ;n" " addl %1,%0 ;n" : "=m" (my_var) //output : "ir" (my_int), "m" (my_var) //input : //modify /* no clobber-list */ );
这条汇编表示给my_var原子加my_int,lock前缀就是用来保证原子性的。
Intel CPU有3种保证原子性的方式,lock前缀是其中之一,原理是通过锁总线来阻止其它处理器操作相应的地址。
4. 0长度数组
定义数组时,长度必须是一个编译时确定的值,如果想使用运行时的值来确定数组的长度,可以使用0长度数组。
struct line { int length; char contents[0]; }; struct line *thisline = (struct line *)malloc (sizeof (struct line) + this_length); thisline->length = this_length;
只有GNU C才支持的特性,对于定义不确定长度的数组非常有用。
5. 三目运算的另类表达
GNU 允许C 语言省略条件表达式中的表达式2省略, 此时表示表达式2与表达式1相同.例如
a = x ? : y;
等价于
a = x ? x : y;
但是如果 x 是一个表达式, 仅求值一次.
也来分享一下你正在使用的tips吧!
参考文章:
http://www.ibm.com/developerworks/cn/linux/kernel/l-chain/index.html
http://kernelnewbies.org/FAQ/LikelyUnlikely
http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
http://zh.wikipedia.org/wiki/%E6%9D%A1%E4%BB%B6%E8%BF%90%E7%AE%97%E7%AC%A6
建议继续学习:
- Linus:利用二级指针删除单向链表 (阅读:11365)
- 为什么数组标号是从0开始的? (阅读:4950)
- C语言结构体里的成员数组和指针 (阅读:4861)
- 将数组定义为常量 (阅读:4569)
- 一道不错的算法题-判断链表是否有环 (阅读:3397)
- xml转数组的方法 (阅读:3498)
- javascript扩展Array(数组)类 (阅读:3241)
- 动态数组的 C 实现 (阅读:3116)
- php数组排序 (阅读:3017)
- Nginx的connections数组 (阅读:2911)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
扫一扫订阅我的微信号:IT技术博客大学习
<< 前一篇:基于用户的协同过滤和皮尔逊相关系数
后一篇:谈谈SVD和LSA >>
文章信息
- 作者:千石 来源: 淘宝核心系统团队博客
- 标签: 三目运算 分支判断 原子操作 数组 链表
- 发布时间:2012-12-23 23:08:30
建议继续学习
近3天十大热文
- [45] 界面设计速成
- [42] Oracle MTS模式下 进程地址与会话信
- [41] 图书馆的世界纪录
- [41] 如何拿下简短的域名
- [41] android 开发入门
- [40] IOS安全–浅谈关于IOS加固的几种方法
- [39] 视觉调整-设计师 vs. 逻辑
- [37] 程序员技术练级攻略
- [37] 【社会化设计】自我(self)部分――欢迎区
- [36] 读书笔记-壹百度:百度十年千倍的29条法则