container_of 和 offsetof 宏详解
在linux内核链表中,会遇到两个宏。
在include/linux/stddef.h中,有这样的定义
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
这里的TYPE表示某个结构体类型,MEMBER表示结构体中的一个成员,这个宏求出了成员在结构体中的位置偏移(以字节为单位)
如果你还不理解,我们举个例子吧。
struct student
{
char name[20];
unsigned char age;
}; int main(void)
{
int off = offsetof(struct student, age);
printf("off = %d\n",off);
}
运行结果是off = 20
其实宏里面的0只是一种特殊情况而已。对这个宏的解释是:假设结构体处于0x1234这个地址,
((TYPE*)0x1234 )-> MEMBER
->比类型转换的优先级高,所以要加括号。上面就得到了成员,再取地址,得到成员的地址
&((TYPE*)0x1234 )-> MEMBER
不用加括号,因为&的优先级比较低
再来一个类型转换
(size_t)&((TYPE*)0x1234 )-> MEMBER
终于得到成员的起始地址了,还没有完,既然算偏移,就要减去结构体的起始地址
(size_t)&((TYPE*)0x1234 )-> MEMBER - 0x1234
不难看出,这里的0x1234换成什么数字都可以,因为偏移是和起始地址无关的。
不信的话可以把这个宏定义改一改,再测试一下
#define offsetof(TYPE, MEMBER) ( (size_t) &((TYPE *)0x2222)->MEMBER - 0x2222 ) struct student
{
char name[20];
unsigned char age;
}; int main(void)
{
int off = offsetof(struct student, age);
printf("off = %d\n",off);
}
改成0x2222后,结果还是20.
既然什么数字都可以,那就改成0吧,于是后面的-0就可以省略了。于是就得到了开头的那个宏。
下面我们说另外一个宏。
/**
827 * container_of - cast a member of a structure out to the containing structure
828 * @ptr: the pointer to the member.
829 * @type: the type of the container struct this is embedded in.
830 * @member: the name of the member within the struct.
831 *
832 */
833 #define container_of(ptr, type, member) ({ \
834 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
835 (type *)( (char *)__mptr - offsetof(type,member) );})
这个宏就是从一个成员的地址得到这个结构体的地址,俗称小指针转大指针。
继续举例子。
struct student
{
char name[20];
unsigned char age;
}; int main(void)
{
struct student stu = {"wangdong",22}; printf("&stu = %p\n",&stu);
printf("&stu.age = %p\n",&stu.age); struct student *p = container_of(&stu.age, struct student, age);
printf("p= %p\n",p);
}
运行结果为
&stu = 0x7fff53df9c40
&stu.age = 0x7fff53df9c54
p= 0x7fff53df9c40
可是为什么不是这样写的呢,而是要多出来一行
const typeof( ((type *)0)->member ) *__mptr = (ptr);
没有这行到底行不行,其实也行,用上面的例子,去掉这行,也可以得到一样的结果。
先看看这行什么意思吧, ((type *)0)->member 这是成员,typeof( ((type *)0)->member ) 得到了成员的类型,假设就是unsigned char类型,
const typeof( ((type *)0)->member ) *__mptr 定义了一个这个类型的指针
const unsigned char * __mptr = (ptr); 赋值给定义的这个指针。
我苦思冥想,又结合网上的资料,认为这样写是做了一个类型检查。如果有了这行,假设结构体就没有member这个成员,那么编译会报错。
所以这样写保证了member确实是type的一个成员。
还有,这样写也保证了ptr确实是这个成员类型的指针,如果不是,编译也会报错。再做个实验。
把刚才的代码改一下
struct student *p = container_of((int *)&stu.age, struct student, age);
故意把&stu.age转换成int*,编译就会报警告:
test.c:33:22: warning:
incompatible pointer types initializing 'const typeof (((struct student *)0)->age)
*' (aka 'const unsigned char *') with an expression of type 'int *' [-Wincompatible-pointer-types]
struct student *p = container_of((int *)&stu.age, struct student, age);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.c:11:39: note:expanded from macro 'container_of'
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
^ ~~~~~
如果没有这一行,那么就不会报错。
所以,作者的出发意图是千方百计地让程序员写出安全的代码啊!真的是用心良苦。
(完)
container_of 和 offsetof 宏详解的更多相关文章
- CTL_CODE 宏 详解
CTL_CODE宏 CTL_CODE:用于创建一个唯一的32位系统I/O控制代码,这个控制代码包括4部分组成: DeviceType(设备类型,高16位(16-31位)), Function(功能2- ...
- 预定义宏,C语言预定义的宏详解
1.预定义宏 对于预定义宏,相信大家并不陌生.为了方便处理一些有用的信息,预处理器定义了一些预处理标识符,也就是预定义宏.预定义宏的名称都是以"__"(两条下划线)开头和结尾的,如 ...
- (转)C/C++ 宏详解
众多C++书籍都忠告我们C语言宏是万恶之首,但事情总不如我们想象的那么坏,就如同goto一样.宏有一个很大的作用,就是自动为我们产生代码.如果说模板可以为我们产生各种型别的代码(型别替换),那么宏其实 ...
- C++宏定义详解
一.#define的基本用法 #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能 理解该命令的本质 ...
- [转]C++宏定义详解
一.#define的基本用法 #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能 理解该命令的本质 ...
- 【转载】C++宏定义详解
摘自:http://blog.chinaunix.net/uid-21372424-id-119797.html C++宏定义详解 2011-02-14 23:33:24 分类: C/C++ ...
- offsetof宏与container_of宏
offsetof宏与container_of宏1.由结构体指针进而访问各元素的原理(1)通过结构体整体变量来访问其中各个元素,本质上是通过指针方式来访问的,形式上是通过.的方式来访问的(这个时候其实是 ...
- [C++] C++中的宏定义详解
转载自:C++中的宏定义 和 C++宏定义详解 一.#define解析 #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率 ...
- [转帖]基于VIM漏洞CVE-2019-12735的VIM宏后门病毒详解
基于VIM漏洞CVE-2019-12735的VIM宏后门病毒详解 不明觉厉 只要是人做的东西 就会有bug 就会有安全问题 就看发现bug 或者是发现安全问题 有没有收益了 会用linux的都是比较熟 ...
随机推荐
- wget下载jdk 蛋疼问题
wget --no-check-certificate --no-cookies --header "Cookie: gpw_e24=http%3A%2F%2Fwww.oracle.com% ...
- Git配置代理命令
针对***的代理配置 设置代理 git config --global http.proxy 'socks5://127.0.0.1:1080' git config --global https.p ...
- 从底层了解ASP.NET体系结构
导读: 前言 关于ASP.NET的底层的工作机制,最近园子里讨论的甚是火热.相信很多人都看过Rick Strahl先生的一篇经典之作:A low-level Look at the ASP.NET ...
- PS2018学习笔记(03-18节)
3-认识主界面 # 主界面包括: 菜单栏.选项栏.工具栏.面板.图像编辑窗口(中间)和状态栏(底部): # 界面设置: 方法1:Ctrl+k:打开界面设置; 方法2:编辑-首选项-界面 # shift ...
- pch文件的创建与配置
1.ios中pch文件的创建与配置 1.1 ios中pch文件的创建与配置 1.2 全局宏定义标志的配置 2.宏定义 这里放的主要是开发中常用的宏定义. /** 动态的字符串格式化宏 */ #defi ...
- Python-OpenCV中VideoCapture类的使用
目录 主要记录Python-OpenCV中的VideoCapture类的使用:官方文档: VideoCapture()是用于从视频文件.图片序列.摄像头捕获视频的类: #!/usr/bin/e ...
- JavaScript之DOM HTML
前言 JavaScript这门语言在一定程度上让我们html之间耦合度降低了,为什么这样说呢?JavaScript语言一样可以可以随意写入html页面一些东西,比如:JavaScript的DOM可以改 ...
- 博弈论-一堆nim博弈合在一起
今天A了张子苏大神的的题,感觉神清气爽. 一篇对于多层nim博弈讲的很透彻的博文:http://acm.hdu.edu.cn/forum/read.php?fid=9&tid=10617 我来 ...
- web flash推流录制测试研究
用flash as3写了一段推流测试demo,参考srs_publisher和simplest_as3_rtmp_streamer.推流到srs2服务器,录制为flv文件.测试一轮结果如下: Web ...
- 洛谷P2071 座位安排
题目背景 公元二零一四年四月十七日,小明参加了省赛,在一路上,他遇到了许多问题,请你帮他解决. 题目描述 已知车上有N排座位,有N*2个人参加省赛,每排座位只能坐两人,且每个人都有自己想坐的排数,问最 ...