1、#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)     (include/linux/stddef.h)

1.1 功能:

返回结构体TYPE中MEMBER成员相对于结构体首地址的偏移量,以字节为单位。

1.2 解析:

此类复杂表达式的解析应该采用从内向外、逐层理解的方式。

首先,(TYPE *)0表示将数字0强制类型转换为TYPE类型(TYPE为结构体类型)的指针。因此这里的0代表内存地址0,即我们认为内存地址0开始的sizeof(TYPE)个字节内存储的是一个TYPE类型的变量。

然后,((TYPE *)0)->MEMBER 得到该结构体变量中的MEMBER成员变量,

而 &(((TYPE*)0)->MEMBER) 使用取地址符&取得了MEMBER成员变量的地址,(size_t)加在前面表示将MEMBER成员变量的地址强制类型转换为size_t(即unsigned int),并将结果作为宏的返回值。

可见,offsetof宏返回的是MEMBER成员在内存中的实际地址。又因为整个结构体的起始地址是0,因此MEMBER成员的实际地址在数值上就等于MEMBER成员相对于结构体首地址的偏移量。

1.3 扩展思考:

1.3.1 使用offsetof宏会影响内存0地址处的值吗?

答案是不会,从1.3.2可知offsetof宏的运算是在C编译器编译时完成的,因此内存的0地址在机器指令中根本未被操作,当然不会影响其值了。

1.3.2offsetof宏返回的MEMBER相对于结构体首地址的偏移量是如何得到的?->符号如何能正确寻址到结构体中某个成员变量?

想探究struct如何通过->精确寻址每一个成员,最好的办法就是将C代码汇编为.S的汇编语言代码,通过观察汇编代码可以看到C编译器对代码处理的具体细节。我们的示例代码如下:

#include"stdio.h"

#definemyoffsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

typedefstruct st

{

int a;

int c;     //将该行加上或去掉,对比得到的汇编代码的差别

short d;   //将该行加上或去掉,对比得到的汇编代码的差别

char b;

}st;

intgetoffsetof(void)

{

return myoffsetof(struct st, b);

}

将以上代码保存为offsetof.c,并且使用arm-linux-gcc offsetof.c –S执行汇编,则会得到offsetof.s文件,内容如下:

.file  "offsetof.c"

.text

.align 2

.global getoffsetof

.type  getoffsetof, %function

getoffsetof:

@ Function supports interworking.

@ args = 0, pretend = 0, frame = 0

@ frame_needed = 1, uses_anonymous_args= 0

mov    ip, sp        // 这三行

stmfd  sp!, {fp, ip, lr, pc}   // 是函数

sub    fp, ip, #4           // 栈帧保存

mov    r3, #10          // #10即是offsetof宏计算得到的值

mov    r0, r3        // 将返回值置于R0中

sub    sp, fp, #12      // 函数栈帧

ldmfd  sp, {fp, sp, lr}    // 恢复

bx     lr            // 函数返回

.size  getoffsetof, .-getoffsetof

.ident "GCC: (GNU) 4.1.2"

以上汇编代码中mov r3, #10一句可以看出,offsetof宏计算member的偏移量是C编译器在编译阶段完成的,而并不需要CPU在运行时去计算得出。

可以尝试着更改struct st中成员b之前的成员,然后再次汇编,对比汇编后代码的不同,以此来验证我们的推论。

结构体指offsetof宏详细解析的更多相关文章

  1. C语言 - 结构体(struct)比特字段(:) 详细解释

    结构体(struct)比特字段(:) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26722511 结构体(struc ...

  2. C语言笔记(结构体与offsetof、container_of之前的关系)

    关于结构体学习,需要了解:结构体的定义和使用.内存对齐.结构体指针.得到结构体元素的偏移量(offsetof宏实现) 一.复习结构体的基本定义和使用 typedef struct mystruct { ...

  3. 结构体对齐及#pragma详细解释

    在linux下c语言结构体对齐: 1.自然对齐 struct 是一种复合数据类型,其构成元素既可以是基本数据类型(如int.long.float 等)的变量,也可以是一些复合数据类型(如array.s ...

  4. bmp文件格式详细解析

    先区分几个概念:16色和16位色一样吗? 不一样! 颜色位数,即是用多少位字节表示的值,每一位可以表示0和1两值.通常图片的颜色深度,简称色深,就是用位数来表示的,所以,我通常会看到8位色,16位色, ...

  5. Go第六篇之结构体剖析

    Go 语言通过用自定义的方式形成新的类型,结构体是类型中带有成员的复合类型.Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性. Go 语言中的类型可以被实例化,使用new或&a ...

  6. Go语言中结构体的使用-第1部分结构体

    1 概述 结构体是由成员构成的复合类型.Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性.结构体成员,也可称之为成员变量,字段,属性.属性要满足唯一性.结构体的概念在软件工程上 ...

  7. 06. Go 语言结构体

    Go语言结构体(struct) Go 语言通过用自定义的方式形成新的类型,结构体是类型中带有成员的复合类型.Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性. Go 语言中的类 ...

  8. 【C语言】这种求结构体成员大小的方法,你可能需要了解一下~

    在C语言编程中,有时候需要知道某结构体中某成员的大小,比如使用堆内存来存储结构体中的某成员时,需要知道该成员的大小,才好确定所需申请的空间大小.求某结构体中某成员的大小,你会怎么做? 例子: type ...

  9. ARM单片机的头文件如何用结构体定义地址

    下面我们以ARM Cortex-M0内核单片机LPC1114的头文件lpc11xx.h文件进行说明. 1.先说两句 lpc11xx.h文件是lpc11xx系列单片机包含的头文件.这个文件的作用和51单 ...

随机推荐

  1. 数组排序自定义comparator()

    案例1:现在有一个普通数组arr = [3,1,2,4,5,6,8,0,1]; 自定义一个排序方法: function createComparator(){ return function (obj ...

  2. POJ1821 Fence

    题意 Language:Default Fence Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 6478 Accepted: ...

  3. grafna与饼状图

    官网: https://grafana.com/plugins/grafana-piechart-panel/installation            https://grafana.com/p ...

  4. Syncthing – 数据同步利器---自己的网盘,详细安装配置指南,内网使用,发现服务器配置

    简介: 无论办公.文件共享.团队协作还是家庭照片.视频.音乐.高清电影的存储,我们常常都有文件同步和存储的需求.但随着国内各大网盘的花式阵亡或限速,早已没什么好选择了.好吧,我已经转战使用onedri ...

  5. linq 实现对象映射

    public static string GetPrintList(string data) { string[] _data = data.Split(','); string Order_No = ...

  6. LeetCode——300. Longest Increasing Subsequence

    一.题目链接:https://leetcode.com/problems/longest-increasing-subsequence/ 二.题目大意: 给定一个没有排序的数组,要求从该数组中找到一个 ...

  7. html动态元素点击事件添加

    很多时候,页面的元素是后期异步动态添加在页面上.页面点击事件无效. 非动态的元素直接$().click();便可以直接触发点击事件,而动态元素需要事先注册事件. $(document).on('cli ...

  8. nginx支持ipv6

    今天碰到的问题是nginx对于ipv6的请求没有日志,顺便查了一下,nginx对ipv6的支持. nginx -v查看nginx是否支持ipv6,出现--with-ipv6,则是支持nginx的,否则 ...

  9. go语言学习--处理map的无序输出

    最近工作中遇到了这样的一个场景,需要处理一个无限极分类的问题,对于数据结构的定义首先想到了,map,map[int]map[int]struct.通过两层map的定义归类parent_id和id的关系 ...

  10. python http请求及多线程应用

    目录 概述 tomorrow包准备 运行环境遇到的问题 其他尝试未果 概述 今天, 使用python3 模拟下发包, http get 请求, 然后 采用tomorrow 多线程. 代码如下: # c ...