第31课 老生常谈的两个宏(linux)
1. Linux内核中常用的两个宏定义
(1)offsetof宏:用于计算TYPE结构体中MEMBER成员的偏移位置
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
#endif
(2)container_of宏:根据成员变量指针反推结构体对象的起始地址
//const typeof(...):编译期类型检查,const指针兼容const和非const指针类型
#ifndef container_of
#define container_of(ptr, type, member) ({ \
const typeof(((type*))->member)* __mptr = (ptr); \
(type*)((char*)__mptr - offsetof(type, member));})
#endif
2. 原理剖析
(1)巧用0地址
①编译器清楚的知道结构体成员变量的偏移地址
②通过结构体变量首地址与偏移量定位成员变量
【编程实验】offsetof宏
//offsetof.c
#include "stdio.h" #ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
#endif struct ST
{
int i; //offset 0
int j; //offset 4
char c; //offset 8
}; void func(struct ST* pst)
{
int* pi = &(pst->i); //(unsigned int)pst + 0;
int* pj = &(pst->j); //(unsigned int)pst + 4;
char* pc = &(pst->c); //(unsigned int)pst + 8; printf("pst = %p\n", pst);
printf("pi = %p\n", pi);
printf("pj = %p\n", pj);
printf("pc = %p\n", pc);
}; int main()
{
struct ST s = {}; func(&s);
func(NULL);//借用0地址计算成员变量的偏移地址,相当于pst=0 printf("offset i: %d\n", offsetof(struct ST, i)); //
printf("offset j: %d\n", offsetof(struct ST, j)); //
printf("offset c: %d\n", offsetof(struct ST, c)); // return ;
}
/*输出结果
pst = 0029FEA4
pi = 0029FEA4
pj = 0029FEA8
pc = 0029FEAC
pst = 00000000
pi = 00000000
pj = 00000004
pc = 00000008
offset i: 0
offset j: 4
offset c: 8
*/
(2)({})是何方神圣
①它是GNU C编译器的语法扩展
②与逗号表达式类似,结果为最后一个语句的值

(3)typeof关键字
①typeof是GNU C编译器的特有关键字
②typeof只在编译期生效,用于得到变量的类型
(4)container_of原理

【编程实验】container_of原理
//container_of.c
#include "stdio.h" #ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE*)0)->MEMBER)
#endif //const typeof(...):编译期类型检查,const指针兼容const和非const指针类型
#ifndef container_of
#define container_of(ptr, type, member) ({ \
const typeof(((type*))->member)* __mptr = (ptr); \
(type*)((char*)__mptr - offsetof(type, member));})
#endif struct ST
{
int i; //offset 0
int j; //offset 4
char c; //offset 8
}; //({})是何方神圣
void func()
{
int a = ;
int b = ;
int c = (a=, b=, a+b); //括号表达式
int d = ({int a=; int b=; a+b;}); //({})表达式,其中的{}表示一个作用域,{}外再加上()表示取最后一个语句的值
//功能上类似于逗号表达式,这是GNU C扩展语法
printf("c = %d\n", c);
printf("d = %d\n", d);
} //typeof关键字:GNU C的扩展关键字
void type_of()
{
int i = ;
typeof(i) j = i;
const typeof(j)* p = &j; printf("sizeof(j)= %d\n", sizeof(j));
printf("j = %d\n", j);
printf("*p = %d\n", *p);
} int main()
{
func();
type_of(); struct ST s = {};
char* pc = &s.c; int e = ;
int* pe = &e; struct ST* pst = container_of(pc, struct ST, c);
//struct ST* pst = container_of(pe, struct ST, e); //类型检查,编译不过
printf("&s = %p\n", &s);
printf("pst = %p\n", pst); //pst == &s; return ;
}
/*输出结果
c = 3
d = 3
sizeof(j)= 4
j = 100
*p = 100-
&s = 0029FE94
pst = 0029FE94
*/
3. 小结
(1)编译器清楚地知道结构体成员变量的偏移位置
(2)({})与逗号表达式类似,结果为最后一个语句的值
(3)typeof只在编译期生效,用于得到变量的类型
(4)container_of使用({})进行类型安全检查
第31课 老生常谈的两个宏(linux)的更多相关文章
- 第八课:不一样的链表 linux链表设计哲学 5星级教程
这一课最后实现的链表,和普通链表不同,借鉴了linux内核链表的思想,这也是企业使用的链表. 基础介绍: 顺序表的思考 顺序表的最大问题是插入和删除需要移动大量的元素!如何解决?A:在线性表数据元素之 ...
- [Spark内核] 第31课:Spark资源调度分配内幕天机彻底解密:Driver在Cluster模式下的启动、两种不同的资源调度方式源码彻底解析、资源调度内幕总结
本課主題 Master 资源调度的源码鉴赏 [引言部份:你希望读者看完这篇博客后有那些启发.学到什么样的知识点] 更新中...... 资源调度管理 任务调度与资源是通过 DAGScheduler.Ta ...
- 两款【linux字符界面下】显示【菜单】,【选项】的powershell脚本模块介绍
两款[linux字符界面下]显示[菜单],[选项]的powershell脚本模块介绍 powershell linux ps1 menu choice Multiselect 传教士 菜单 powe ...
- 零基础学习python_pickle(31课)
上次我提到了对文件的读写等一系列操作,回想下,要想从文件内读取内容无论是read还是readline,读取出来的是不是都是字符串呢?那么如果想让字典.列表这些数据类型保存进文件到读取出来都是原来的类型 ...
- 两级宏&&字符串化宏
如果你想字符串化宏参数扩展的结果,你必须使用两个级别的宏. #define xstr(s) str(s) #define str(s) #s #define foo 4 str (foo) ==> ...
- 为何在新建STM工程中全局声明两个宏
在uVision中新建STM32工程后,需要从STM32标准库中拷贝标准外设驱动到自己的工程目录中,此时需要在工程设置->C/C++选项卡下的Define文本框中键入这两个全局宏定义. STM3 ...
- MFC中关于运行时类信息及动态创建对象的两个宏的意义(转)
http://blog.csdn.net/ligand/article/details/49839507 MFC运行时类信息 用途: 程序在运行时,获取对象类的信息及类的继承关系 实现: 1.定义的类 ...
- Flask第31课——include标签
我们在上一节代码基础上增加一些代码,样式: 文件名index.html,代码: {% from 'macros/forms.html' import input %} <!DOCTYPE htm ...
- 第31课 std::atomic原子变量
一. std::atomic_flag和std::atomic (一)std::atomic_flag 1. std::atomic_flag是一个bool类型的原子变量,它有两个状态set和clea ...
随机推荐
- 路由器外接硬盘做nas可行吗?
话说把家里的newifi mini升级到最新版后,又外接了个移动硬盘做nas,第一部就打算吧手机的视频移过去.一试才发现这速度慢的不行.只有几百kb 所以说,用是能用,单着速度也太慢了 再就是貌似硬盘 ...
- [转]MySQL中乐观锁、悲观锁(共享锁、排他锁)简介
InnoDB与MyISAM Mysql 在5.5之前默认使用 MyISAM 存储引擎,之后使用 InnoDB. MyISAM 操作数据都是使用的表锁,你更新一条记录就要锁整个表,导致性能较低,并发不高 ...
- 【转】iOS编译OpenSSL静态库(使用脚本自动编译)
原文网址:https://www.jianshu.com/p/651513cab181 本篇文章为大家推荐两个脚本,用来iOS系统下编译OpenSSL通用库,如果想了解编译具体过程,请参看<iO ...
- Linux之异步通知机制分析
1.概念: 异步通知机制:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,是一种“信号驱动的异步I/O”.信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号 ...
- GTX log 6
5.24-7.2 相关算法 判断两颗二叉树相等 有两种方法:一种是递归比较.另一种是二叉树的遍历. 先说二叉树的遍历.由于先序遍历 再加上 中序遍历能唯一确定一棵二叉树.故,对这两棵树分别进行先序和中 ...
- LeetCode——16. 3Sum Closest
一.题目链接:https://leetcode.com/problems/3sum-closest/ 二.题目大意: 给定一个数组A和一个目标值target,要求从数组A中找出3个数来,使得这三个数的 ...
- servlet.xml 出现 Referenced file contains errors(http://.......)
问题描述: 打开Eclipse突然发现Web工程的servlet.xml突然报了红叉叉,错误信息如下: Referenced file contains errors (http://www.spri ...
- Bootstrap&bxslider
Bootstrap 规则 一.响应式布局 @media 二.图标.字体 @font-face 三.基本使用 实例: <!DOCTYPE html> <html lang=" ...
- 【IIS错误 - HTTP 错误 500.19】HTTP 错误 500.19- Internal Server Error 错误解决方法(一)
刚在本机部署了一个WebService测试,浏览的时候出现了“HTTP 错误 500.19 - Internal Server Error ”错误,如下图: 经过检查发现是由于先安装vs2008后安装 ...
- openssl命令实例
基本知识 1,证书标准 X.509 X.509 - 这是一种证书标准,主要定义了证书中应该包含哪些内容.其详情可以参考RFC5280,SSL使用的就是这种证书标准. X.509的证书文件,一般以.cr ...