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宏和结构体的理解的更多相关文章

  1. typeof, offsetof, container_of宏

    container_of宏实现如下: #define container_of(ptr, type, member) ({ \ )->member ) *__mptr = (ptr); \ (t ...

  2. 深入delphi编程理解之消息(二)发送消息函数及消息编号、消息结构体的理解

    一.delphi发送消息的函数主要有以下三个: (一).SendMessage函数,其原型如下: function SendMessage( hWnd: HWND; {目标句柄} Msg: UINT; ...

  3. C++ 结构体指针理解

    上一篇基础链接https://www.cnblogs.com/xuexidememeda/p/12283845.html 主要说一下链表里面双重指针 先说一下结构体 typedef struct LN ...

  4. linux中offsetof与container_of宏定义

    linux内核中offsetof与container_of的宏定义 #define offsetof(TYPE, MEMBER)    ((size_t) &((TYPE *)0)->M ...

  5. (转)offsetof与container_of宏[总结]

    1.前言 今天在看代码时,遇到offsetof和container_of两个宏,觉得很有意思,功能很强大.offsetof是用来判断结构体中成员的偏移位置,container_of宏用来根据成员的地址 ...

  6. offsetof与container_of宏[总结]

    1.前言 今天在看代码时,遇到offsetof和container_of两个宏,觉得很有意思,功能很强大.offsetof是用来判断结构体中成员的偏移位置,container_of宏用来根据成员的地址 ...

  7. Linux中的两个经典宏定义:获取结构体成员地址,根据成员地址获得结构体地址;Linux中双向链表的经典实现。

    倘若你查看过Linux Kernel的源码,那么你对 offsetof 和 container_of 这两个宏应该不陌生.这两个宏最初是极客写出的,后来在Linux内核中被推广使用. 1. offse ...

  8. offsetof与container_of宏分析

    offsetof宏:结构体成员相对结构体的偏移位置 container_of:根据结构体成员的地址来获取结构体的地址 offsetof 宏 原型: #define offsetof(TYPE, MEM ...

  9. 结构体中使用#define定义宏

    struct  hostent {   char    *h_name;        /* official name of host */   char    **h_aliases;    /* ...

随机推荐

  1. 焦大:seo如何快速理解谷歌PR的计算奥秘

    http://www.wocaoseo.com/thread-248-1-1.html 前不久看到一个搞笑的说法,有人问谷歌PR的计算是PR值=0.15+0.85*(A网页传递值+B网页传递值--), ...

  2. 一篇文章高效定位iframe

    今天跟大家分享的是如何高效的定位iframe.我们来看一段最早的代码: # coding: utf-8from selenium import webdriverfrom time import sl ...

  3. 社区布道师揭秘消息中间件技术内幕,撰写MQ架构设计与实现原理

    RocketMQ是什么 RocketMQ是由阿里捐赠给Apache的一款分布式.队列模型的开源消息中间件,经历了淘宝双十一的洗礼. RocketMQ的特性 RocketMQ基本概念 Client端 P ...

  4. js判断传递参数的的类型,并返回

    function   type(target) { var templet  ={ "[object Array]" :  "arry"; "[obj ...

  5. Stack (30)(模拟栈,输出中间数用set)

    Stack is one of the most fundamental data structures, which is based on the principle of Last In Fir ...

  6. eslint prettier vetur eslint

    VScode (版本 1.47.3)安装 eslint prettier vetur 插件 .vue 文件使用 vetur 进行格式化 在文件 .prettierrc 里写 属于你的 pettier ...

  7. python之结合if条件判断和生成随机数的相关知识,完成石头剪刀布的游戏

    程序开始,显示下面提示信息: 请输入:剪刀(0).石头(1).布(2): 用户输入数字0-2中的一个数字,与系统随机生成的数字比较后给出结果信息. 例如:输入0后,显示如下 你的输入为:剪刀(0) 随 ...

  8. 使用zabbix监控sql server的发布订阅

    (一)背景 个人在使用sql server时,用到了sql server的发布订阅来做主从同步,类似MySQL的异步复制.在发布订阅环境搭建完成后,最重要的就是如何监控复制的状态了,sql serve ...

  9. C:将算术表达式的符号和数分开

    程序: #include <stdio.h> #include <string.h> static int pos=; static char* line; void test ...

  10. [Oracle/sql]查看当前用户名下有多少表 以及查看表字段信息

    SQL> select table_name from user_tables order by table_name; TABLE_NAME ------------------------- ...