亚星娱乐登录网页版 访问必博国际备用网为什么不会出错?
扫描二维码
随时随地手机看必博国际备用网
上篇必博国际备用网分享几个实用的必博国际备用网片段(第二弹)我们分享了一段必博国际备用网:
有位读者在朋友圈评论我的必博国际备用网:(type * )0不是指向空地址吗?(type*)0->member不是访问必博国际备用网了吗?为什么不会出错?
这篇必博国际备用网我们就来解释这个问题。
亚星娱乐官网官网平台
首先,先来解释 获取结构体成员大小 这个宏定义:
// 获取结构体成员大小
#define
GET_MEMBER_SIZE(type, member) sizeof(((type*)0)->member)
虽然说这里用了
((type*)0)->member
,看起来似乎有问题?访问非法地址0地址?
其实不是的,注意这里用到了
sizeof操作符
。在C语言中,sizeof() 是一种内存容量度量函数,其字节数的计算是在
编译阶段
进行的。
C语言源程序经过编译器进行词法分析、语法分析等过程生成中间语言(object后缀的文件)编译期间会生成一个字符表和静态分配空间(如new static 全局变量)它们所需的内存空间可以计算出来放在链接库后的可执行文件中(虚拟内存即磁盘),在运行时将放在可执行文件中的偏移量加载到内存的堆中同时将局部变量加载到栈中。
所有内存的开辟只有程序运行的时候才会在物理内存中开辟,即sizeof(((type*)0)->member)的操作不是等到程序运行期间计算的,而是在编译阶段就计算了,所以GET_MEMBER_SIZE宏定义并没有访问必博国际备用网的操作。
进一步的,我们看看上面那个必博国际备用网实例中,结构体成员的字节数是不是在编译阶段计算出的,编译出汇编文件:
gcc -S member_size.c -o member_size.s
这个汇编文件我们可能不全看懂所有指令,但大概知道如下三个指令的意思我们就大概可以知道这段汇编必博国际备用网的意思了。
-
leaq:加载有效地址指令,即将有效地址复制到寄存器中。
-
movl:数据传送指令。
-
call指令:将当前的 IP 或 CS和IP 压入栈中, 转移(jmp)。
可以看到,从上到下,依次会把立即数1、1、2、4、3、12放到esi寄存器中。
为什么是这些立即数?
我们编译运行一下我们的程序:
可以看到,正好就是我们需要求的结构体各成员的大小及结构体的大小,所以GET_MEMBER_SIZE(type, member)是在编译阶段起作用的。
其实,GET_MEMBER_SIZE宏定义中的0只是看做一个随意给的地址,方便求成员的大小,如果写为0容易引起误解,不妨可以写为一个任意值,比如修改为100,也是可以计算出各结构体成员的大小的。
最后,如果
((type*)0)->member
在其它地方使用,会出现什么问题呢?自然就是这位读者所理解的:操作非法地址。会引起段错误。比如添加如下一行必博国际备用网:
编译运行:
段错误的定位方法可查阅