我对CONTAINING_RECORD宏的详细解释
宏CONTAINING_RECORD的用处其实还是相当大的, 而且很是方便, 它的主要作用是:
根据结构体中的某成员的指针来推算出该结构体的指针!
下面从一个简单的例子开始说起:
我们定义一个结构体, 同时类型化:
typedef struct{
int a;
int b;
int c;
}ss;
这是一个很简单的结构体, 没什么特殊的, 稍微分析下该结构体:
结构体的大小(字节):4+4+4=12字节
成员a的偏移:0
成员b的偏移:4
成员c的偏移:8
我们用ss来定义一个变量:
ss s = {1,2,3};
那么此时a,b,c的值分别为:a=1,b=2,c=3.
其实编译器在生成代码的时候其实是这样给成员变量赋值的:
假定s的地址为:0x12000000, 则:
*(int*)((char*)&s + 0) = 1;
*(int*)((char*)&s + 4) = 2;
*(int*)((char*)&s + 8) = 3;
也就是说是在&s的地址基础上加上变量的偏移来确定成员的指针并赋值的, 所以:
&s->a 将得到 0x12000000 + 0 = 0x12000000
&s->b 将得到 0x12000000 + 4 = 0x12000004
&s->c 将得到 0x12000000 + 8 = 0x12000008
所以: 结构体的地址 + 成员变量的偏移 = 成员变量的地址
移一下项: 成员变量的地址 - 成员变量的偏移 = 结构体的地址
哇哇, 这就是我们想要的地址, 不就是做了个减法嘛~~~囧
首先, 成员变量的地址是我们知道的.
其次, 我们需要得到成员变量的偏移(假定为成员b的偏移).
怎么办呢? 我们可以这样:
&s->b - (unsigned long)&s, 这样就可以得到成员b的偏移了, 但是, 但是, &s 是我们需要的, 显然暂时是个未知数, 既然这样...
那, 我们再做一次减法吧(非正确的C语言表达式, 不过结果没问题, 这里只是显得清楚点):
&(s-s)->b - (unsinged long)&(s-s),
其中, 为保证类型一致, 需要:
s-s = (ss*)0
(unsigned long)&(s-s) = (unsigned long)(ss*)0 = 0, 直接省略该部分就可以了
那么, 化简得到: &((ss*)0)->b - (unsigned long)0
最简结果: &((ss*)0)->b, 这就是b的偏移
哈哈, 很简单吧, 0指针的妙用, 总共做了两次减法而已~ 对你们数学帝来说肯定不是问题啦~
其中, 我们需要知道ss结构体的原型, ss结构体中的某个成员变量b(其实无论哪个都一样, 只是要和前面提供指针的那个变量要一致)
总结下, 我们需要提供:结构体中某个成员变量的地址, 该结构体的原型, 该结构体中的某个成员变量(与前面要是同一个变量)
最终的CONTAINING_RECORD的定义为:
#define CONTAINING_RECORD(addr,type,field) ((type*)((unsigned char*)addr - (unsigned long)&((type*)0)->field))
addr: 结构体中某个成员变量的地址
type: 结构体的原型
field: 结构体的某个成员(与前面相同)
好了, 所有的结论都出来了, 这是一个万能公式, 不管成员变量是哪一个结果都正确, 这是相对于知道第一个变量的地址而言的:
如果知道的是第一个成员的地址(pa = &s->a)的话, 这是最简单的情况了:
直接强制类型转换就可以了: (ss*)pa 即可, 此时 &((type*)0)->field 这部分恰好为0
所以结果直接就是((type*)addr)了, 最简单的情况. 也是我们最容易想到的一种情况, 比如把链表元素放在结构体的最开始 ~~~
到这里这个CONTAINING_RECORD宏就已经说完了~
现在, 我们在使用LIST_ENTRY等双向链表时, 不管把该链表放在结构体的哪个地方, 都可以在遍历链表时通过CONTAINING_RECORD宏来准确得到整个结构体的地址了~
记得移除链表中的某个元素的时候, 要free整个结构体的地址才行哦, WDK提供的操作函数只是把该链表元素脱离整个链表~~~
btw:
把addr转换为 unsigned char*的原因是在指针计算时的计算单位为1, 也就是说 (unsigned char*)addr+1 = addr+1, 不转换的话肯定是错误的
把&((type*)0)->field转换为(unsigned long)4个字节宽的同时是要保证表达式不是由两个指针的算术操作构成的, 因为C语言标准未定义那样的运算
写了这么多, 希望没落下什么吧~
女孩不哭(QQ:191035066)@2013-01-07 18:56:57 http://www.cnblogs.com/nbsofer
我对CONTAINING_RECORD宏的详细解释的更多相关文章
- Linux内核中container_of宏的详细解释
上一节拒绝造轮子!如何移植并使用Linux内核的通用链表(附完整代码实现)我们在分析Linux内核链表的时候注意到内核在求解结构体偏移的时候巧妙的使用了container_of宏定义,今天我们来详细剖 ...
- Linux下函数调用堆栈帧的详细解释【转】
转自:http://blog.chinaunix.net/uid-30339363-id-5116170.html 原文地址:Linux下函数调用堆栈帧的详细解释 作者:cssjtuer http:/ ...
- #pragma详细解释(一)
#pragma详细解释 #pragma详细解释(一) 2010-04-18 14:21:00| 分类: 默认分类 | 标签: |字号大中小订阅 在#Pragma是预处理指令它的作用是设定编 ...
- 每天进步一点点------YUV格式详细解释
YUV格式详细解释 YUV开放分类: 网络.计算机.手机.色彩学.影像学 概述 YUV(亦称YCrCb)是被欧洲电视系统所采用的一种颜色编码方法(属于PAL),是PAL和SECAM模拟彩色电视制式 ...
- .htaccess语法之RewriteCond与RewriteRule指令格式详细解释
htaccess语法之RewriteCond与RewriteRule指令格式详细解释 (2012-11-09 18:09:08) 转载▼ 标签: htaccess it 分类: 网络 上文htacc ...
- cookie的详细解释
突然看到网页上中英文切换的效果,不明白怎么弄得查了查 查到了cookie 并且附有详细解释 就copy留作 以后温习 http://blog.csdn.net/xidor/article/detail ...
- tar命令的详细解释
tar命令的详细解释 标签: linuxfileoutputbashinputshell 2010-05-04 12:11 235881人阅读 评论(12) 收藏 举报 分类: linux/unix ...
- Linux学习笔记15——GDB 命令详细解释【转】
GDB 命令详细解释 Linux中包含有一个很有用的调试工具--gdb(GNU Debuger),它可以用来调试C和C++程序,功能不亚于Windows下的许多图形界面的调试工具. 和所有常用的调试工 ...
- C语言 - 结构体(struct)比特字段(:) 详细解释
结构体(struct)比特字段(:) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26722511 结构体(struc ...
随机推荐
- ubuntu16.04 64位server安装php7
You can do the following: sudo apt-get install python-software-properties sudo LC_ALL=C.UTF-8 add-ap ...
- 详解Android动画之Frame Animation(转)
在开始实例讲解之前,先引用官方文档中的一段话: Frame动画是一系列图片按照一定的顺序展示的过程,和放电影的机制很相似,我们称为逐帧动画.Frame动画可以被定义在XML文件中,也可以完全编码实现. ...
- ecshop首页调用评论及图片
1.在library文件夹中建立一个名为index_comment.lbi文件 2.输入以下代码 <meta http-equiv="Content-Type" conten ...
- Markdown解决需要输入两个回车才能为一个空行的问题
markdownDataDiv.children().each(function(){ $(this).html($(this).html().replaceAll("\n",&q ...
- hdu 1074(状态压缩dp+记录路径)
题意:给了n个家庭作业,然后给了每个家庭作业的完成期限和花费的实践,如果完成时间超过了期限,那么就要扣除分数,然后让你找出一个最优方案使扣除的分数最少,当存在多种方案时,输出字典序最小的那种,因为题意 ...
- 搭建XMPP协议,实现自主推送消息到手机
关于服务器端向Android客户端的推送,主要有三种方式: 1.客户端定时去服务端取或者保持一个长Socket,从本质讲这个不叫推送,这是去服务端拽数据.但是实现简单,主要缺点:耗电等 2.Googl ...
- UITableViewCell 自定义绘制 性能高
// // FoodListTableViewCellB.m // BabyFood // // Created by zhuang chaoxiao on 16/3/7. // Copyri ...
- n个数的最小公倍数
Description 求n个数的最小公倍数. Input 输入包含多个测试实例,每个测试实例的开始是一个正整数n,然后是n个正整数. Output 为每组测试数据输出它们的最小公倍数,每个测 ...
- IntelliJ IDEA 13 Keygen
import java.math.BigInteger; import java.util.Date; import java.util.Random; import java.util.zip.CR ...
- linux 学习网站
study-Area:http://www.study-area.org 鸟哥的私房菜:http://linux.vbird.org 鸟哥的私房菜课后答案:http://wapwenku.baidu. ...