什么是结构体内存对齐?为什么要对齐?怎样对齐?
  结构体内存对齐:元素是按照定义顺序一个一个放到内存中去的,但并不是紧密排列的。

  从结构体存储的首地址开始,每个元素放置到内存中时,它都会认为内存是按照自己的大小来划分的,因此元素放置的位置一定会在自己宽度的整数倍上开始。

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宏>引发的<结构体深度剖析(内存对齐,对齐参数,偏移量)>的更多相关文章

  1. 指针直接赋值为整型AND利用宏定义求结构体成员偏移量

    首先我们要更正一个很熟悉的概念,那就是指针不仅仅是“地址”,指针还有一个很重要的特性,那就是“类型”. 指针初始化时,“=”的右操作数; 除外,该语句表示指针为空): 所以 ; 这样的代码是不允许的. ...

  2. Delphi 给结构体指针分配内存,用new(p),释放用dispose(p)

    来自:http://blog.163.com/zhangzhifeng688%40126/blog/static/1652627582010102261748481/ 给结构体指针分配内存  但在很多 ...

  3. C++中的结构体所占内存空间总结

    因为结构体有时候需要字节对齐.一般而言,struct 的 sizeof 是所有成员字节对齐后长度相加,而 union 的 sizeof 是取最大的成员长度. 在默认情况下,编译器为每一个变量或数据单元 ...

  4. C/C++编程笔记:C语言对齐问题【结构体、栈内存以及位域对齐】

    引言 考虑下面的结构体定义: 假设这个结构体的成员在内存中是紧凑排列的,且c1的起始地址是0,则s的地址就是1,c2的地址是3,i的地址是4. 现在,我们编写一个简单的程序: 运行后输出: 为什么会这 ...

  5. C/C++ 结构体成员在内存中的对齐规则(转载)

    这几天在看王艳平的<windows 程序设计>,第5章讲解了MFC框架是怎么管理窗口句柄到窗口实例之间的映射,用到了两个类CPlex和CMapPtrToPtr,用于管理内存分配的类(避免因 ...

  6. 关于C语言中结构体中的结构体成员导致的字节对齐问题

    关于结构体的字节对齐是什么,就不赘述,再此附上一篇文章,介绍字节对齐:http://www.linuxsong.org/2010/09/c-byte-alignment/ 这里的结构体字节对齐的数据类 ...

  7. C/C++ 结构体成员在内存中的对齐规则

    这几天在看王艳平的<windows 程序设计>,第5章讲解了MFC框架是怎么管理窗口句柄到窗口实例之间的映射,用到了两个类CPlex和CMapPtrToPtr,用于管理内存分配的类(避免因 ...

  8. [c/c++] programming之路(28)、结构体存储和内存对齐+枚举类型+typedef+深拷贝和浅拷贝

    一.结构体存储 #include<stdio.h> #include<stdlib.h> struct info{ char c; //1 2 4 8 double num; ...

  9. C 语言结构体 struct 及内存对齐

    struct 结构体 对于复杂的数据类型(例如学生.汽车等),C 语言允许我们将多种数据封装到一起,构成新类型. 跟面向对象语言中的对象相比,结构体只能包含成员变量,不支持操作. #include & ...

  10. 结构体sizeof()问题与字节对齐

    32位机器上定义如下结构体: struct xx { long long _x1; char _x2; int _x3; char _x4[2]; static int _x5; }; int xx: ...

随机推荐

  1. Linux文件常用操作命令

    Linux文件常用操作命令 一.Linux文件和目录简单操作 1.1 查看文件 ls 查看当前目录下的文件 如: -a 显示所有文件及目录 (ls内定将文件名或目录名称开头为"." ...

  2. mysql17-sql优化-慢查询日志

    1.什么是慢查询日志MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行时间超过long_query_time值的SQL,会被记录到慢查询日 ...

  3. 如何快速搭建 Maven私服Nexus【实践可行版】

    搭建私服Nexus Nexus 一般用来搭建位于组织或公司内部的 Maven 私服,代理所有的仓库(包括中央仓库),用户通过它就可以获取和管理所有所需的 Maven 构件. Nexus 开源版具有以下 ...

  4. ubuntu 备份系统

    1.安装Systemback: sudo add-apt-repository ppa:nemh/systemback sudo apt-get update sudo apt-get install ...

  5. python-最近面试遇到的代码题,mark一下

    1. 打印1000以内的质数 draft版本: def printlist(): a = [] for i in range(1, 1001): b.append(i) for j in range( ...

  6. pip和pipenv简记

    一. pip篇 1. 配置文件换源 windows:C:\Users\Administrator\pip\pip.ini mac:~/pip/pip.conf [global] index-url = ...

  7. 前端开发工具VsCode官网下载太慢?直接失败?

    我有个朋友来到公司以后,在搭建基本开发环境时,什么nodejs安装包,vscode包等等都是由同事直接分享,然后一键安装,从来没去过官网下载,以至于想更新新版本的时候首次进入官网有点懵逼,相信很多同学 ...

  8. CyNix: 1

    CyNix: 1 目录 CyNix: 1 1 信息收集 1.1 端口扫描 1.2 后台目录扫描 1.2.1 目录分析 2 GetShell 2.1 赋予id_rsa文件600权限,否则会报错,无法连接 ...

  9. node.js 历史版本下载

    https://nodejs.org/zh-cn/download/releases/

  10. 02#Vue3 Transition 过渡:切换路由组件

    复习作用域插槽 组件可以被插入些许节点作为其子节点,插槽<slot>就是一个接口(或桥梁)引导这些节点进入组件.这些节点应该被渲染到组件里的具体哪个位置,就是具名插槽的作用. 就像是给手机 ...