对offsetof、 container_of宏和结构体的理解
offsetof 宏
#include<stdio.h>
#define offsetoff(type, member) ((int)&((type*)0)->member)
/*
((type*)0)->member 释义:声明一个相应类型的结构体指针,该指针指向0地址处。再通过该指针访问各元素。我们只要获取一个指针就能访问其内部的元素吗,可以这么搞?其实我是想联系全局和非全局变量,通过上面这个代码也许你不明白我要表达的意思。请继续慢慢看,直到本文后面,结合本文后面对单链表的一个疑点及分析,你就会体会更深了。暂时这里先不管这个,
指向0地址处的结构体指针访问各元素得到的地址值就是其偏移量了? 看来这是一个公认的道理。
我不能解释为什么,但是我可以分析一下通过 '->"、 普通结构体指针访问其元素的性质,通过对比,
如果性质相同,那就认了这个道理,不必深究。
*/
typedef struct stu{
char a;
int b;
char c;
}*p1;
int main(void)
{
struct stu stu1;
stu1.b = 666;
p1 p = &stu1;
printf("&stu1 = %p.\n", &stu1);
printf("p = %p.\n", p);
printf("stu1.b = %d.\n", p->b);
printf("&(stu1.b) = %p.\n", &(p->b));
}
/*
aston@ubuntu:/mnt/hgfs/shared$ gcc a.c
aston@ubuntu:/mnt/hgfs/shared$ ./a.out
&stu1 = 0xbfc485b4.
p = 0xbfc485b4.
stu1.b = 666.
&(stu1.b) = 0xbfc485b8. //比上面多4个字节,合理。
打印出来的都是0xbfc485b + x,
如果直接先看这个例子,那我也很能理解各元素的地址打印出来将会是什么样,甚至都不会细想。想当然地认为:
指针 + 结构体嘛,考虑下对齐,就知道各元素地址了。但是为什么一看见offsetoff就有点迷糊了呢?
小结论:一个知识点只有在多种不同场景下被应用,才能加深印象。
回到主问题:我们通过实验发现,结构体指针不同时,编译器都会自动增加偏移量。
大结论: 我们通过“->”的方式访问结构体元素,其实是利用了编译器自动帮我们计算元素偏移量的特点。
只有理解了这点,才能更好地理解 offsetof 宏。
另外,这个例子我并没有产生“我们只要获取一个指针就能访问其内部的元素吗,可以这么搞”的疑惑,
因为在这个例子里,stu1.b = 666;和printf("stu1.b = %d.\n", p->b);这两句代码都在一个代码块{}中,
我p->b访问结构体元素,p 和 b 都在同一个代码块{},当然可以访问啊。
*/
再来看这个代码:
#include<stdio.h>
#include <stdlib.h>
#include <strings.h>
typedef struct node{
unsigned int data;
struct node* pNext; //难道这个指针是全局的吗?
}Node;
Node* creat_node(unsigned int data)
{
Node *p = (Node*)malloc(sizeof(Node));
printf("sizeof(Node)-8- = %d.\n", sizeof(Node));
if(NULL ==p)
{
printf("malloc error");
return NULL;
}
bzero(p,sizeof(Node));
p->data = data;
p->pNext = NULL;
return p;
}
int main(void)
{
Node* pheader= NULL;
pheader = creat_node(666);
pheader->pNext = creat_node(777);
pheader->pNext->pNext = creat_node(888);
printf("node1: data:%d\n", pheader->data);
//creat_node的返回值只是p,但是却可以使用pNext这个指针(即访问p指向节点内的元素)。难道这个指针是全局的吗?data同样作为节点内的元素,data也是全局的?
printf("node1: data:%d\n", pheader->pNext->data);
printf("node1: data:%d\n", pheader-pNext-pNext->data);
}
我们写个测试代码
局部的结构体变量aaa。
#include <stdio.h>
typedef struct node{
int data;
}Node;
void func(void)
{
Node aaa;
aaa.data = 100;
}
int main(void)
{
printf("%d\n", aaa.data);
return 0;
}
编译报错error: ‘aaa’ undeclared (first use in this function)
测试结果:不是全局的。
结论:我们只要获取了结构体的指针,我们就可以访问结构体内的元素(member).
再结合这点理解文首的((type*)0)->member。哪怕你member是局部变量,我也通过结构体指针,就能获取你。
再来证实一下:
typedef struct node{
int data;
}Node;
int func(void)
{
int addr = 0;
Node aaa;
addr = (int)&aaa;
aaa.data = 100;
return addr;
}
int main(void)
{
int a = func();
printf("%d\n", ((Node*)a)->data);
return 0;
}
container_of宏
#include <stdio.h>
struct mystruct
{
char a; // 0
int b; // 4
short c; // 8
};
// TYPE是结构体类型,MEMBER是结构体中一个元素的元素名
// 这个宏返回的是member元素相对于整个结构体变量的首地址的偏移量,类型是int
#define offsetof(TYPE, MEMBER) ((int) &((TYPE *)0)->MEMBER)
// ptr是指向结构体元素member的指针,type是结构体类型,member是结构体中一个元素的元素名
// 这个宏返回的就是指向整个结构体变量的指针,类型是(type *)
#define container_of(ptr, type, member) ({ \
const typeof(((type *)0)->member) * __mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
//我的下面两句代码不行。为什么?????????
//#define container_of(ptr, type, member) ((type*)((char*)ptr - offsetof(type, member));)
//#define container_of(ptr, type, member) ((type*)((int)ptr - offsetof(type, member));)
int main(void)
{
struct mystruct s1;
struct mystruct *pS = NULL;
short *p = &(s1.c); // p就是指向结构体中某个member的指针
printf(" &s1 :%p.\n", &s1);
// 问题是要通过p来计算得到s1的指针
pS = container_of(p, struct mystruct, c);
printf("pS is :%p.\n", pS);
return 0;
}
对offsetof、 container_of宏和结构体的理解的更多相关文章
- typeof, offsetof, container_of宏
container_of宏实现如下: #define container_of(ptr, type, member) ({ \ )->member ) *__mptr = (ptr); \ (t ...
- 深入delphi编程理解之消息(二)发送消息函数及消息编号、消息结构体的理解
一.delphi发送消息的函数主要有以下三个: (一).SendMessage函数,其原型如下: function SendMessage( hWnd: HWND; {目标句柄} Msg: UINT; ...
- C++ 结构体指针理解
上一篇基础链接https://www.cnblogs.com/xuexidememeda/p/12283845.html 主要说一下链表里面双重指针 先说一下结构体 typedef struct LN ...
- linux中offsetof与container_of宏定义
linux内核中offsetof与container_of的宏定义 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->M ...
- (转)offsetof与container_of宏[总结]
1.前言 今天在看代码时,遇到offsetof和container_of两个宏,觉得很有意思,功能很强大.offsetof是用来判断结构体中成员的偏移位置,container_of宏用来根据成员的地址 ...
- offsetof与container_of宏[总结]
1.前言 今天在看代码时,遇到offsetof和container_of两个宏,觉得很有意思,功能很强大.offsetof是用来判断结构体中成员的偏移位置,container_of宏用来根据成员的地址 ...
- Linux中的两个经典宏定义:获取结构体成员地址,根据成员地址获得结构体地址;Linux中双向链表的经典实现。
倘若你查看过Linux Kernel的源码,那么你对 offsetof 和 container_of 这两个宏应该不陌生.这两个宏最初是极客写出的,后来在Linux内核中被推广使用. 1. offse ...
- offsetof与container_of宏分析
offsetof宏:结构体成员相对结构体的偏移位置 container_of:根据结构体成员的地址来获取结构体的地址 offsetof 宏 原型: #define offsetof(TYPE, MEM ...
- 结构体中使用#define定义宏
struct hostent { char *h_name; /* official name of host */ char **h_aliases; /* ...
随机推荐
- 放眼SEM现状及发展历程
http://www.wocaoseo.com/thread-187-1-1.html 由于近年来移动应用的基本普及,搜索引擎营销随之进入高速发展时代,应用层次的提升已经成为企业营销策略的一个重要组成 ...
- 做seo如何选择一个好的标题
http://www.wocaoseo.com/thread-86-1-1.html 我们常说人要红妆马配鞍,那么对于seo来说,网站的标题也是很重要的,如何写好标题呢?只要注意以下几点就行了. 首先 ...
- python123期末四题编程题 -无空隙回声输出-文件关键行数-字典翻转输出-《沉默的羔羊》之最多单词
1. 无空隙回声输出 描述 获得用户输入,去掉其中全部空格,将其他字符按收入顺序打印输出. ...
- 让“不确定性”变得有“弹性”?基于弹性容器的AI评测实践
0. 前言 AI的场景丰富多彩,AI的评价方法百花齐放,这对于设计一套更通用的评测框架来说,是一个极大的挑战,需要兼顾不同的协议,不同的模型环境,甚至是不同的操作系统.本文分享了我们在AI评测路上的一 ...
- React其它相关知识点
React其它相关知识点 一,解释一下React Fiber? 简单来说,核心就是在虚拟dom和浏览器的调用栈之间多了一个虚拟调用栈,和虚拟dom一样,这个虚拟调用栈也是在内存中的,这个虚拟调用栈就类 ...
- @PostConstruct详解
一.定义: @PostContruct是spring框架的注解,在方法上加该注解会在项目启动的时候执行该方法,也可以理解为在spring容器初始化的时候执行该方法. 从Java EE5规范开始,Ser ...
- 腾讯大牛半年心血高级编程PDF,帮你轻松构建企业级Web应用
毫无疑问,Java 是这些年来最流行的编程语言之一.它无处不在一计算机. 手机.网站以及各种嵌入式设备中都存在着大量的Java 应用程序,而其中应用最为广泛的应该就是Java EE Web应用程序(以 ...
- adb 打开手机端口进行无线模式调试
打开手机端口 让手机在指定的端口可以接收到TCP/IP连接. 确保手机开启了usb调试 用usb线把手机和电脑连接起来 执行命令:adb tcpip 5555 执行成功后就可以把usb线拔掉了,端口可 ...
- python笔记-字符串连接
字符串连接 + 1.Java中其他基本数据类型和string做+,自动转成string处理 Python中没有此特性.需要先转成string再做拼接 2.每连接一次,就要重新开辟空间,然后把字符串连接 ...
- oracle之三存储库及顾问框架
AWR存储库及顾问框架(PPT-I-349-360) 14.1 Oracle数据库采样ASH和AWR. 1) ASH(Active Session History) ASH收集的是活动会话的样本数据, ...