结构体指offsetof宏详细解析
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宏详细解析的更多相关文章
- C语言 - 结构体(struct)比特字段(:) 详细解释
结构体(struct)比特字段(:) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26722511 结构体(struc ...
- C语言笔记(结构体与offsetof、container_of之前的关系)
关于结构体学习,需要了解:结构体的定义和使用.内存对齐.结构体指针.得到结构体元素的偏移量(offsetof宏实现) 一.复习结构体的基本定义和使用 typedef struct mystruct { ...
- 结构体对齐及#pragma详细解释
在linux下c语言结构体对齐: 1.自然对齐 struct 是一种复合数据类型,其构成元素既可以是基本数据类型(如int.long.float 等)的变量,也可以是一些复合数据类型(如array.s ...
- bmp文件格式详细解析
先区分几个概念:16色和16位色一样吗? 不一样! 颜色位数,即是用多少位字节表示的值,每一位可以表示0和1两值.通常图片的颜色深度,简称色深,就是用位数来表示的,所以,我通常会看到8位色,16位色, ...
- Go第六篇之结构体剖析
Go 语言通过用自定义的方式形成新的类型,结构体是类型中带有成员的复合类型.Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性. Go 语言中的类型可以被实例化,使用new或&a ...
- Go语言中结构体的使用-第1部分结构体
1 概述 结构体是由成员构成的复合类型.Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性.结构体成员,也可称之为成员变量,字段,属性.属性要满足唯一性.结构体的概念在软件工程上 ...
- 06. Go 语言结构体
Go语言结构体(struct) Go 语言通过用自定义的方式形成新的类型,结构体是类型中带有成员的复合类型.Go 语言使用结构体和结构体成员来描述真实世界的实体和实体对应的各种属性. Go 语言中的类 ...
- 【C语言】这种求结构体成员大小的方法,你可能需要了解一下~
在C语言编程中,有时候需要知道某结构体中某成员的大小,比如使用堆内存来存储结构体中的某成员时,需要知道该成员的大小,才好确定所需申请的空间大小.求某结构体中某成员的大小,你会怎么做? 例子: type ...
- ARM单片机的头文件如何用结构体定义地址
下面我们以ARM Cortex-M0内核单片机LPC1114的头文件lpc11xx.h文件进行说明. 1.先说两句 lpc11xx.h文件是lpc11xx系列单片机包含的头文件.这个文件的作用和51单 ...
随机推荐
- 19/03/15Pyhon笔记
1.快速在python中显示目标的2进制写法 bin(342) "0b101010110" 2.一个二进制位就是一比特(bit) 3.Python2默认无法识别中文,需要加文件头 ...
- Javascript 2.8
声明函数声明参数 function multiply(A,B,...N){}; 用return可以返回一个值/字符串/数组/布尔值 变量命名的Camel记号:从第二个单词开始把每个单词的首字母大写,其 ...
- golang-generate-1pixel-image
package main import ( "bytes" "encoding/base64" "flag" "html/temp ...
- yum源搭建
1:vim /etc/yum.repo.d/ll.repo [local] 这里不能有空格,如[local yum] name=local baseurl=file:///yum gpgcheck ...
- PySide中QtGui.QFrame的用法
最近一位同事的出现让我重新正视PySide中designer这个工具的强大之处,通过QtGui.QObject.setGeometry(QtCore.QRect())这个最简单直接的方法可以完成很多复 ...
- 正点原子stm32f103mini版串口下载BOOT0引脚与与CH340G芯片引脚RTS、DTR、的关系原理
在做串口实验时,一直搞不明白一键下载是怎么回事,于是自己就去捉摸CH340G这块芯片,那么这里我将详细的讲解一下这块芯片怎么与stm32配合使用的. 1.由CH340G芯片资料可以知道这两个引脚的功能 ...
- js EL 正则表达式
<script> //校验是否全由数字组成20位数 var patrn=/^[0-9]{1,20}$/; alert(patrn.test("-30000000000" ...
- 错误 CS0006 Metadata file 'E:\项目名称\xxxx.dll'
错误 CS0006 Metadata file 'E:\桌面临时文件\Pos\xxxx.dll' 1.找到这个类库在当前类库右键发生 找到 应用程序-->把程序集名称改成提示错误 的名称 2.找 ...
- minicom 抓取log
使用minicom也有很长时间了,只用minicom抓过uart log,但是从来没有去保存过这个log,也不知道有这个功能.后来在超级终端中发现有这个功能(传送->捕获文字),想想minico ...
- 竖直的ViewPager,上下滑动的ViewPager,VerticalViewPager ;
直接看代码吧,重写Viewpager,改变了手势和切换效果:和正常的Viewpager使用一模一样: /** * ChenboCui 竖向的Viewpager */ public class Vert ...