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. 拿捏了!ConcurrentHashMap!

    概述 本文将对JDK8中 ConcurrentHashMap 源码进行一定程度的解读.解读主要分为六个部分:主要属性与相关内部类介绍.构造函数.put过程.扩容过程.size过程.get过程.与JDK ...

  2. android开发之splash闪屏页判断是否第一次进入app代码

    package com.david.david.zhankudemo.activity; import android.app.Activity; import android.content.Con ...

  3. Shader之溶解效果的几种实现方法

    这里通过 “是否丢弃像素”的2种方法,写2个shader,效果是一样的,也提到了,丢弃某个像素的3种方式. 是否丢弃: 1.通过脚本控制shader变量判断当前是否丢弃像素,需要额外脚本: 2.sha ...

  4. 【Go语言入门系列】(九)写这些就是为了搞懂怎么用接口

    [Go语言入门系列]前面的文章: [Go语言入门系列](六)再探函数 [Go语言入门系列](七)如何使用Go的方法? [Go语言入门系列](八)Go语言是不是面向对象语言? 1. 引入例子 如果你使用 ...

  5. Mybatis源码学习第八天(总结)

    源码学习到这里就要结束了; 来总结一下吧 Mybatis的总体架构 这次源码学习我们,学习了重点的模块,在这里我想说一句,源码的学习不是要所有的都学,一行一行的去学,这是错误的,我们只需要学习核心,专 ...

  6. [Java数据结构]LinkedHashMap,TreeMap

    HashMap不能记住插入时的顺序,但LinkedHashMap可以做到这一点. 例程: Map<Integer,String> empMap=new LinkedHashMap<I ...

  7. Solr专题(二)详解Solr查询参数

    一.前言 上节我们讲到了怎样去搭建solr服务,作为全文检索引擎,怎样去使用也是比较关键的.Solr有一套自己的查询方式,所以我们需要另外花时间去学习它的这套模式. 启动solr solr start ...

  8. 搭建MQTT学习平台

    关于MQTT协议的介绍就不赘述了,网上资料非常多,直接入正题. MQTT协议的实现非常多,我选择了一个非常轻量级的开源项目来学习MQTT协议——mosquitto,官网:www.mosquitto.o ...

  9. java中包名命名规范

    在idea中创建package遇到的问题 发现一个问题,当我创建一个lesson-02的package时,输入这个包名后,package自动变成了文件夹 在网上搜索发下java包名一般是小写字母进行命 ...

  10. js监听事件的绑定与移除

    监听事件的绑定与移除主要是addEventListener和removeEventListener的运用. addEventListener语法 element.addEventListener(ty ...