<CONTAINING_RECORD宏>引发的<结构体深度剖析(内存对齐,对齐参数,偏移量)>
什么是结构体内存对齐?为什么要对齐?怎样对齐?
结构体内存对齐:元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。
从结构体存储的首地址开始,每个元素放置到内存中时,它都会认为内存是按照自己的大小来划分的,因此元素放置的位置一定会在自己宽度的整数倍上开始。
struct T
{
int a;
char b;
double c;
char d;
};
解析:
在windows系统32位平台上:
int占4个字节
char占1个字节
float占4个字节
double占8个字节
int a从0偏移开始,占四个字节,即占用0,1,2,3,
现在可用偏移为4偏移,接下来存char b; 由于4是1的倍数,故而,b占用4偏移,
接下来可用偏移为5偏移,接下来该存double c; 由于5不是8的倍数,所以向后偏移5,6,7,都不是8的倍数,偏移到8时,8是8的倍数,故而c从8处开始存储,占用8,9,10,11,12,13,14,15偏移,
现在可用偏移为16偏移,最后该存char d ;因为16是1的倍数,故d占用16偏移,
接下来在整体向后偏移一位,现处于17偏移,min(默认对齐参数,类型最大字节数)=8;因为17不是8的倍数,所以继续向后偏移18…23都不是8的倍数,到24偏移处时,24为8的整数倍,
故而,该结构体大小为24个字节。

方法总结:
a、从零偏移处开始,按字节大小计算,判断此偏移地址是否为该成员变量和对齐参数两者之间的最小值,即min(对齐参数,sizeof()成员);
b、若是,则从此处开始占用内存,大小为该类型所占字节数值,若不是,则内存向后偏移到最小值整数倍处,再开始占用空间。
c、按a、b、两步骤算出结构体实际所占内存时,为了方便后面类型的存储,再向后偏移一位,然后判断该地址是否是默认对齐数与该结构体中最大类型所占字节数的最小值 ,即:min(默认对齐参数,类型最大字节数)的整数倍,若是,则当前偏移地址的字节数便是结构体大小,若不是,继续向后偏移,直至为最小值整数倍为止。
对齐参数如何设置?可以设置为按照任意字节数对齐吗?
解析:
在windows中,VS编译器下,默认对齐数为8; 在Linux中,默认对齐数为4
设置对齐参数可在结构体struct之前加上#pragma pack(对齐数),在struct之后加上#pragma pack;便可以设置对齐参数。
#pragma pack(4)
struct T
{
int a;
char b;
double c;
char d;
};
#pragma pack;
对齐参数不能任意设置,只能是内置类型已有的字节数,如:char(1)、short(2),int(4),double(8)…不能是3,5…任意数。
如何知道结构体某个成员相对于结构体起始位置的偏移量?
使用offsetof宏来判断结构体中成员的偏移地址。使用offsetof宏需要包含stddef.h头文件,该宏定义如下:
#define offsetof(type,menber) (size_t)&(((type*)0)->member)
巧妙之处在于将地址0强制转换为type类型的指针,从而定位到member在结构体中偏移位置,编译器认为0是一个有效的地址,从而认为0是type指针的起始地址。
如何根据结构体中的某成员的地址来推算出该结构体整体的地址?
#include <Windows.h>
#include <stdio.h>
int main()
{
struct T
{
int a;
int b;
int c;
};
//CONTAINING_RECORD宏的作用就是根据结构体中的某成员的地址来推算出该结构体整体的地址。
T t = { 1, 2, 3 };
//假设我们知道T结构体中b的地址和名称,求t的指针
T *pT = CONTAINING_RECORD(&t.b, T, b);
printf("a:%d b:%d c:%d\n", pT->a, pT->b, pT->c);
//CONTAINING_RECORD的定义:
//((type *)( (PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field)))
//最后一部分(&((type *)0)->field) 将0(空指针)转成type,再取地址。
//在本例中就是将空指针转成T*, 然后指向b这个变量, 然后再取地址。
//这个操作的作用就是:假设T开始在0x000000内存位置上分配内存,在此基础上求b的内存地址,
//这样等同于求得b的内存结构体对齐偏移量, 求得b的地址我们转成ULONG_PTR类型,
//然后用实际b的内存地址减去b的结构体偏移量求得结构体首地址。
//
//分解开来就是:
//这种情况是允许的。
//这个大前提很重要!!!
T *pTemp = (T*)0;
//求b的内存地址,在结构体首地址为0的情况下b的内存地址其实就是自身的对齐大小偏移量!!!
//CONTAINING_RECORD宏的核心!!!
int *pB = &pTemp->b;
ULONG_PTR Offset = (ULONG_PTR)pB; //转成数字, 就是b的偏移量。
printf("b的偏移量:%d\n", Offset);
//因为各个成员的地址是递增的,最后用实际b的地址减b的偏移量的到结构体首地址
T *pFinal = (T*)(((char*)&t.b) - Offset);
printf("T中a:%d b:%d c:%d\n", pFinal->a, pFinal->b, pFinal->c);
return 0;
}
————————————————
版权声明:本文为CSDN博主「dai_wen」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dai_wen/article/details/78304568
版权声明:本文为CSDN博主「china_jeffery」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/china_jeffery/article/details/78801331
<CONTAINING_RECORD宏>引发的<结构体深度剖析(内存对齐,对齐参数,偏移量)>的更多相关文章
- 指针直接赋值为整型AND利用宏定义求结构体成员偏移量
首先我们要更正一个很熟悉的概念,那就是指针不仅仅是“地址”,指针还有一个很重要的特性,那就是“类型”. 指针初始化时,“=”的右操作数; 除外,该语句表示指针为空): 所以 ; 这样的代码是不允许的. ...
- Delphi 给结构体指针分配内存,用new(p),释放用dispose(p)
来自:http://blog.163.com/zhangzhifeng688%40126/blog/static/1652627582010102261748481/ 给结构体指针分配内存 但在很多 ...
- C++中的结构体所占内存空间总结
因为结构体有时候需要字节对齐.一般而言,struct 的 sizeof 是所有成员字节对齐后长度相加,而 union 的 sizeof 是取最大的成员长度. 在默认情况下,编译器为每一个变量或数据单元 ...
- C/C++编程笔记:C语言对齐问题【结构体、栈内存以及位域对齐】
引言 考虑下面的结构体定义: 假设这个结构体的成员在内存中是紧凑排列的,且c1的起始地址是0,则s的地址就是1,c2的地址是3,i的地址是4. 现在,我们编写一个简单的程序: 运行后输出: 为什么会这 ...
- C/C++ 结构体成员在内存中的对齐规则(转载)
这几天在看王艳平的<windows 程序设计>,第5章讲解了MFC框架是怎么管理窗口句柄到窗口实例之间的映射,用到了两个类CPlex和CMapPtrToPtr,用于管理内存分配的类(避免因 ...
- 关于C语言中结构体中的结构体成员导致的字节对齐问题
关于结构体的字节对齐是什么,就不赘述,再此附上一篇文章,介绍字节对齐:http://www.linuxsong.org/2010/09/c-byte-alignment/ 这里的结构体字节对齐的数据类 ...
- C/C++ 结构体成员在内存中的对齐规则
这几天在看王艳平的<windows 程序设计>,第5章讲解了MFC框架是怎么管理窗口句柄到窗口实例之间的映射,用到了两个类CPlex和CMapPtrToPtr,用于管理内存分配的类(避免因 ...
- [c/c++] programming之路(28)、结构体存储和内存对齐+枚举类型+typedef+深拷贝和浅拷贝
一.结构体存储 #include<stdio.h> #include<stdlib.h> struct info{ char c; //1 2 4 8 double num; ...
- C 语言结构体 struct 及内存对齐
struct 结构体 对于复杂的数据类型(例如学生.汽车等),C 语言允许我们将多种数据封装到一起,构成新类型. 跟面向对象语言中的对象相比,结构体只能包含成员变量,不支持操作. #include & ...
- 结构体sizeof()问题与字节对齐
32位机器上定义如下结构体: struct xx { long long _x1; char _x2; int _x3; char _x4[2]; static int _x5; }; int xx: ...
随机推荐
- 六、python基础知识之变量常量、索引取值和PEP8规范
目录 一.变量与常量 1.什么是变量? 2.什么是常量? 变量的基本使用 变量使用的语法结构与底层原理 变量名的命名规范和命名风格 变量的命名风格 常量的基本使用 二.索引取值 三.PEP8规范 1. ...
- ARM启动顺序
iROM(也叫BL0)的作用: 初始化系统时钟,设置看门狗,初始化栈和堆 加载BL1 BL1的作用: 初始化RAM , 关闭Cache , 设置栈 加载BL2 BL2的作用: 初始 ...
- 重学SpringBoot. step7 高并发 秒杀
高并发 高并发最容易出现的问题就是数据安全能不能得到保障. 你需要保证速度,又需要保证数据安全,那么速度也必然会有所下降. 所以最简单的办法就是提升硬件.或者把Mysql换成MongoDB,加个Red ...
- C#写文本日志
/*----------------------------------------------------------------- * 作 者(Author): Dennis * 日 期(Crea ...
- 1.2.HBuilder软件与uniapp文件介绍
uni-app官网地址 下载HBuilder 教程
- 随机森林RF模型超参数的优化:Python实现
本文介绍基于Python的随机森林(Random Forest,RF)回归代码,以及模型超参数(包括决策树个数与最大深度.最小分离样本数.最小叶子节点样本数.最大分离特征数等)自动优化的代码. ...
- 字符串、函数、bug
字符串 字符串驻留机制 仅保存一份相同且不可变字符串的方法,不同的值别存放在字符串的驻留池中,Python的驻留机制对相同的字符串只保留一份拷贝,后续穿件相同的字符串时,不会开辟新的空间,而是把字符串 ...
- 【KAWAKO】MobaXterm-使用MobaSSHTunnel进行端口映射
目录 在Tools中打开MobaSSHTunnel(port forwarding) 点击New SSH tunnel 配置信息 点击运行 访问端口 在Tools中打开MobaSSHTunnel(po ...
- JZOJ 7685. 【2022.10.06冲剌NOIP2022模拟】奇怪的函数(function)
\(\text{Solution}\) 观察到关于 \(x\) 的函数在 \(n\) 个操作之后一定是这样的: 一段水平直线加上一段斜率为 \(1\) 的直线再加上一段水平直线 于是线段树维护这个分段 ...
- LeetCode-1706 球会落在何处
来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/where-will-the-ball-fall 题目描述 用一个大小为 m x n 的二维网格 ...