内存对齐问题

内存对齐 结构体大小 内存空洞的分析 结构体是我们C语言中非常常见的一种复合数据类型,它的出现解决了那种比较复杂,抽象的事物描述,同时改善了这种函数参数较多容易出错的情况,当我们一下子需要好些变量时,我们完全可以用结构体去代替那些很多的变量。当然在C语言中,还有一些其他的复合类型,如union,enum... 这里我重点讲一下结构体的内存占用空间大小与内存空洞问题。     在我们的计算机中,字节是我们管理内存空间的最小单 阅读详情

typedef struct
{
  UINT32  NumElements;
  union
  {
    UINT32  ObjectHandle;
  } Entry;
} STR_ARRAY, *PSTR_ARRAY;

还有这两句
#pragma pack(push, 1)
#pragma pack(pop)

#pragma pack([ n ])
该指令指定结构和联合成员的紧凑对齐。而一个完整的转换单元的结构和联合的紧凑对齐由/Zp选项设置。紧凑对齐用pack编译指示在数据说明层设置。该编译指示在其出现后的第一个结构或联合说明处生效。该编译指示对定义无效。当你使用 #pragma pack(n) 时, 这里n 为1、2、4、8 或16 。第一个结构成员之后的每个结构成员都被存储在更小的成员类型或n 字节界限内。如果你使用无参量的#pragma pack,结构成员被紧凑为以/Zp 指定的值。该缺省/Zp紧凑值为/Zp8。

编译器也支持以下增强型语法:
#pragma pack([[{push|pop},][标识符,]][n])

若不同的组件使用pack 编译指示指定不同的紧凑对齐, 这个语法允许你把程序组件组合为一个单独的转换单元。带push 参量的pack编译指示的每次出现将当前的紧凑对齐存储到一个内部编译器堆栈中。编译指示的参量表从左到右读取。如果你使用push , 则当前紧凑值被存储起来;如果你给出一个n的值, 该值将成为新的紧凑值。若你指定一个标识符,即你选定一个名称,则该标识符将和这个新的的紧凑值联系起来。带一个pop参量的pack编译指示的每次出现都会检索内部编译器堆栈顶的值,并且使该值为新的紧凑对齐值。如果你使用pop 参量且内部编译器堆栈是空的,则紧凑值为命令行给定的值, 并且将产生一个警告信息。若你使用pop 且指定一个n的值,该值将成为新的紧凑值。若你使用pop且指定一个标识符,所有存储在堆栈中的值将从栈中删除,直到找到一个匹配的标识符,这个与标识符相关的紧凑值也从栈中移出,并且这个仅在
标识符入栈之前存在的紧凑值成为新的紧凑值。如果未找到匹配的标识符,将使用命令行设置的紧凑值,并且将产生一个一级警告。缺省紧凑对齐为8 。

pack编译指示的新的增强功能让你编写头文件, 确保在遇到该头文件的前后的紧凑值是一样的。

什么是内存对齐?

考虑下面的结构:
 struct foo
 {
   char c1;
   short s;
   char c2;
   int i;
 };

假设这个结构的成员在内存中是紧凑排列的,假设c1的地址是0,那么s的地址就应该是1,c2的地址就是3,i的地址就是4。也就是
 c1 00000000, s 00000001, c2 00000003, i 00000004
可是,我们在Visual C/C++ 6中写一个简单的程序:
  struct foo a;
  printf("c1 %p, s %p, c2 %p, i %p/n",
      (unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a,
      (unsigned int)(void*)&a.s - (unsigned int)(void*)&a,
      (unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a,
      (unsigned int)(void*)&a.i - (unsigned int)(void*)&a);

运行,输出:
  c1 00000000, s 00000002, c2 00000004, i 00000008。
为什么会这样?这就是内存对齐而导致的问题。

为什么会有内存对齐?

 以下内容节选自《Intel Architecture 32 Manual》。
 字(word),双字(dword),和四字在自然边界上不需要在内存中对齐。(对“字“,“双字“,和“四字“来说,自然边界分别是偶数地址,可以被4整除的地址,和可以被8整除的地址。)
 无论如何,为了提高程序的性能,数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;然而,对齐的内存访问仅需要一次访问。
 一个字或双字操作数跨越了4字节边界,或者一个四字操作数跨越了8字节边界,被认为是未对齐的,从而需要两次总线周期来访问内存。一个字起始地址是奇数但却没有跨越字边界被认为是对齐的,能够在一个总线周期中被访问。
 某些操作双四字的指令需要内存操作数在自然边界上对齐。如果操作数没有对齐,这些指令将会产生一个通用保护异常(#GP)。双四字的自然边界是能够被16整除的地址。其他的操作双四字的指令允许未对齐的访问(不会产生通用保护异常),然而,需要额外的内存总线周期来访问内存中未对齐的数据。

编译器对内存对齐的处理

缺省情况下,C/C++编译器默认将结构、栈中的成员数据进行内存对齐。因此,上面的程序输出就变成了:
  c1 00000000, s 00000002, c2 00000004, i 00000008。
编译器将未对齐的成员向后移,将每一个都成员对齐到自然边界上,从而也导致了整个结构的尺寸变大。尽管会牺牲一点空间(成员之间有空洞),但提高了性能。也正是这个原因,我们不可以断言sizeof(foo)==8。在这个例子中,sizeof(foo)==12。

如何避免内存对齐的影响

    那么,能不能既达到提高性能的目的,又能节约一点空间呢?有一点小技巧可以使用。比如我们可以将上面的结构改成:

 struct bar
 {
   char c1; 
   char c2;
   short s;
   int i;
 };
 这样一来,每个成员都对齐在其自然边界上,从而避免了编译器自动对齐。在这个例子中,sizeof(bar)==8。

 这个技巧有一个重要的作用,尤其是这个结构作为API的一部分提供给第三方开发使用的时候。第三方开发者可能将编译器的默认对齐选项改变,从而造成这个结构在你的发行的DLL中使用某种对齐方式,而在第三方开发者哪里却使用另外一种对齐方式。这将会导致重大问题。
    比如,foo结构,我们的DLL使用默认对齐选项,对齐为
c1 00000000, s 00000002, c2 00000004, i 00000008,同时sizeof(foo) == 12。
而第三方将对齐选项关闭,导致
    c1 00000000, s 00000001, c2 00000003, i 00000004,同时sizeof(foo) == 8。

如何使用C/C++中的对齐选项

 VC6中的编译选项有 /Zp[1|2|4|8|16] ,/Zp1表示以1字节边界对齐,相应的,/Zpn表示以n字节边界对齐。n字节边界对齐的意思是说,一个成员的地址必须安排在成员的尺寸的整数倍地址上或者是n的整数倍地址上,取它们中的最小值。也就是:
    min(sizeof(member), n)
 实际上,1字节边界对齐也就表示了结构成员之间没有空洞。/Zpn选项是应用于整个工程的,影响所有的参与编译的结构。要使用这个选项,可以在VC6中打开工程属性页,C/C++页,选择Code Generation分类,在Struct member alignment可以选择。

要专门针对某些结构定义使用对齐选项,可以使用#pragma pack编译指令。指令语法如下:
   #pragma pack([show]|[push|pop][,identifier],n)
意义和/Zpn选项相同。比如:

 #pragma pack(1)
 struct foo_pack
 {
   char c1;
   short s;
   char c2;
   int i;
 };
 #pragma pack()

栈内存对齐

我们可以观察到,在VC6中栈的对齐方式不受结构成员对齐选项的影响。(本来就是两码事)。它总是保持对齐,而且对齐在4字节边界上。

验证代码

#include <stdio.h>

struct foo
{
  char c1;
  short s;
  char c2;
  int i;
};

struct bar
{
  char c1;
  char c2;
  short s;
  int i;
};

#pragma pack(1)
struct foo_pack
{
  char c1;
  short s;
  char c2;
  int i;
};
#pragma pack()


int main(int argc, char* argv[])
{
  char c1;
  short s;
  char c2;
  int i;

  struct foo a;
  struct bar b;
  struct foo_pack p;

  printf("stack c1 %p, s %p, c2 %p, i %p/n",
    (unsigned int)(void*)&c1 - (unsigned int)(void*)&i,
    (unsigned int)(void*)&s - (unsigned int)(void*)&i,
    (unsigned int)(void*)&c2 - (unsigned int)(void*)&i,
    (unsigned int)(void*)&i - (unsigned int)(void*)&i);

  printf("struct foo c1 %p, s %p, c2 %p, i %p/n",
    (unsigned int)(void*)&a.c1 - (unsigned int)(void*)&a,
    (unsigned int)(void*)&a.s - (unsigned int)(void*)&a,
    (unsigned int)(void*)&a.c2 - (unsigned int)(void*)&a,
    (unsigned int)(void*)&a.i - (unsigned int)(void*)&a);

  printf("struct bar c1 %p, c2 %p, s %p, i %p/n",
    (unsigned int)(void*)&b.c1 - (unsigned int)(void*)&b,
    (unsigned int)(void*)&b.c2 - (unsigned int)(void*)&b,
    (unsigned int)(void*)&b.s - (unsigned int)(void*)&b,
    (unsigned int)(void*)&b.i - (unsigned int)(void*)&b);

  printf("struct foo_pack c1 %p, s %p, c2 %p, i %p/n",
    (unsigned int)(void*)&p.c1 - (unsigned int)(void*)&p,
    (unsigned int)(void*)&p.s - (unsigned int)(void*)&p,
    (unsigned int)(void*)&p.c2 - (unsigned int)(void*)&p,
    (unsigned int)(void*)&p.i - (unsigned int)(void*)&p);

  printf("sizeof foo is %d/n", sizeof(foo));
  printf("sizeof bar is %d/n", sizeof(bar));
  printf("sizeof foo_pack is %d/n", sizeof(foo_pack));

  return 0;
}

遭遇内存对齐问题 先说理论的:为何要内存对齐? 1、平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。 2、 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。 图一: 这是 阅读详情

相关推荐

C/C++中内存对齐问题的一些理解

内存管理是C/C++编程非常重要的一部分内容,熟悉C/C++中一些常见数据结构和数据类型的内存分布情况,可以很大程度上降低了coding过程中内存泄漏和越界等比较严重的内存问题,下面主要讨论一下结构体(类中同样存在内存对齐,内存对齐会复杂一些,涉及到虚表等问题,后面介绍类的时候会提到)中的内存对齐问题。 如有侵权,请联系删除,如有错误,欢迎大家指正,谢谢 内存对齐指令 一般来说,内存对齐过程对c...

xiao_ma_nong_last的博客 619

#progma整理

 #pragma预处理指令详解 在所有的预处理指令中,#Pragma  指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C ++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。  其格式一般为:        #Pragma

Tommy(凌飞)的专栏 1098

为什么你的SiC MOSFET仿真不准?可能是离子注入的‘沟道效应’在捣鬼(Silvaco BCA模块详解)

本文深入分析了SiC MOSFET仿真中离子注入沟道效应的关键影响,揭示了Silvaco BCA模块在模拟SiC晶向和角度参数时的核心设置要点。通过详细解析晶圆偏切与注入角度的等效关系,提供优化MOSFET沟道区注入仿真的五步实战方法,帮助工程师将阈值电压仿真误差从26%降低到5%以内。

weixin_26789847的博客 410

什么是内存对齐?为什么要内存对齐?

要了解为什么要内存对齐,首先我们要了解什么是内存对齐 什么是内存对齐 关于什么是内存对齐,我们先来看几个例子 typedef struct { int a; double b; short c; }A; typedef struct { int a; short b; double c; }B; 分别对他们求大小,sizeof(A)...

zw_ss 4万+

从零开始:用ADS 2023手把手教你设计2.4GHz Wi-Fi LNA(基于ATF-54143,附模型文件)

本文详细介绍了如何使用ADS 2023软件设计2.4GHz Wi-Fi频段的低噪声放大器(LNA),基于ATF-54143晶体管。内容涵盖模型导入、直流工作点确定、稳定性优化、噪声匹配等关键步骤,并提供可下载的模型文件包,帮助工程师快速掌握LNA设计技巧。

weixin_42531128的博客 320

CTP高频Tick数据:五档期货Level2深度解析20241224

高频Tick五档期货Level2数据,作为一种先进的金融市场分析工具,以其一秒四次的高频更新,为投资者揭示了市场的微观结构。本文将深入探讨这一数据如何帮助投资者洞察市场动态,优化交易策略,实现投资收益的最大化。

level2Tick的博客 1217

字节对齐

什么是字节对齐,为什么要对齐      现代计算机中内存空间都是按照BYTE划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。   作用和原因: 各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某

barbarian_zc的专栏 625

Web渗/透/攻/击实战(1)—成功渗/透台湾某净化设备公司官网

写在前面 作为一个防御型白帽黑客​ 你一定要知道黑客的进攻套路 才能有相应的防御措施 声明:只做测试,发现对方的漏洞,并不进行破坏性操作 1、 网站分析 地址:http://www.xxx.com.tw/ 这是一家中国台湾地区的网站,看着满屏的繁体字,就不是很舒服 网站首页包括:关于我们、资料下载、联络方式等等。 这些对我们进行sql注入用处并不大 我们需要的是可以进行参数传入的页面,这样我们就可以在参数里做文章 找一下 有没有新闻列表相关的页面 页...

信息安全领域优质创作者|操作系统领域专家 1806

为什么需要内存对齐以及对齐规则的简单分析

Ubuntu 16.04.2编译环境arm-linux3.4.5linux-2.6.22.6 首先需要知道的是:CPU每次从内存中取出数据或者指令时,并非想象中的一个一个字节取出拼接的,而是根据自己的字长,也就是CPU一次能够处理的数据长度取出内存块,比如32位处理器将取出32位也就是4个字节的内存块进行处理。这里有一个问题:是只需要两个字节怎么办?答案是还是取出4个字节,然后内存处理器...

G_METHOD的博客 4982

ChatTutor:让AI老师“会写会画”,打造可视化、互动式智能教育新范式

ChatTutor是一款突破性的开源AI教学助手,通过可视化电子白板实现"边讲边写"的沉浸式教学体验。该项目支持数学公式手写输入、动态函数绘图、自动生成思维导图等功能,采用多智能体架构实现专业协同。未来将扩展至编程、物理等STEM教育领域,适用于学生自学、教师备课和教育平台集成。作为MIT协议开源项目,ChatTutor通过赋予AI"教学肢体语言",推动教育从信息传递转向可视化互动建构,重新定义了AI助教的可能性。

这里汇聚了前沿的技术分享与实用的开发技巧,带你探索从创意到企业的技术创业之路。 423

【人工智能】RAG如何根据实际需求选择和进行文档解析(如 PDF、Word、TXT、网页等)?

文档解析的核心在于根据解析目标、文档类型和处理量选择合适工具。PDF、Word、TXT和网页各有特性:PDF需区分可编辑与扫描版,Word需处理结构化内容,TXT简单但需注意编码,网页需应对动态内容。解析场景包括提取纯文本、结构化数据、保留格式和批量处理。非技术用户可借助工具如Adobe Acrobat、WPS和在线OCR,技术用户可通过编程实现自动化。通用流程为:明确目标、分析文档属性、选择工具并测试优化。核心是“工具适配需求”,无需追求万能方案。

奶爸的编程之路,也就一周冷个三天 506

【C/C++】详解内存对齐问题,C语言内存对齐整理

本专栏记录了博主C++从初阶到高阶完整的学习历程,会发布一些博主学习的感悟、碰到的问题、重要的知识点,和大家一起探索C++这门程序语言的奥秘。这个专栏将记录博主C++语法、高阶数据结构、STL的学习过程,正所谓“万丈高楼平地起”嘛,我们话不多说,继续进行C++阶段的学习。本文我们不讲C++主线的内容,我们来拓展一下或者说整理一下我们学习时C/C++时经常会提到的一些专有名词,例如形参、实参,显式类型转换和隐式类型转换类型转换,内置类型、内存对齐问题等等。

艾莉丝努力练剑的博客,感谢大家一直以来的对艾莉丝的支持和鼓励,uu们如果对C/C++方向感兴趣的话,可以通过艾莉丝主页的微信名片加艾莉丝的好友,和努力练剑的艾莉丝一起加油! 1509

不加行也能填满:用 ABAP BDC 驯服 Table Control 滚动录制缺陷,批量维护供应商银行账户

摘要 本文探讨了在SAP ECC/S4HANA系统中处理供应商主数据时,Table Control控件带来的批量导入挑战。文章指出,银行账户维护界面中Table Control的滚动动作难以被录制工具捕捉,导致批量导入数据时仅能填充前5行。解决方案是通过BDC_OKCODE显式触发翻页(如=P+),并配合行号拼接(如(01)~(05))将数据写入不可见行。文中提供了完整的ABAP程序示例,展示了如何读取外部文件、动态生成BDCDATA、处理表格翻页逻辑,最终通过CALL TRANSACTION执行XK01事

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 272

内存对齐相关问题总结

海量数据处理 位图 布隆过滤器

selfsongs 1692

机器学习特征选择-t检验

在特征选择的过程中通过计算检验统计量值,比较特征之间的统计量的大小,并降序排列,选取统计值较高的特征,从而起到去除差别小的特征(此差别小的特征难以区分不同的额类别)的目的。 以t检验(Student's t test)为例: 假设检验 通过比较两类样本的随机变化,通过计算其p值决定是否拒绝零假设,也即是说:通过计算p值,若p值小于某一阈值则认为两个样本具有显著差异,两个样本是不同的,此时拒绝...

heda3的博客 6700

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

YUV内存对齐问题

内存对齐是计算机体系结构中的一个基本概念。它指的是数据在内存中的起始地址必须是某个特定值(通常是 2、4、8 或 16 的倍数)的整数倍。内存对齐是为了提升性能和满足硬件要求而对数据在内存中存储地址的限制。在 YUV 平面格式中,对齐主要影响每一行像素数据的长度。为了对齐,行的末尾会被填充额外的字节。FFmpeg 通过AVFrame结构体中的linesize数组来告知用户每个平面每行的实际字节数(包含填充)。在所有涉及到访问视频帧像素数据的操作中,你都必须使用linesize而不是简单地用。

C/C++ 976

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

C++内存池的内存对齐问题

摘要 内存对齐是硬件要求、软件正确性和性能优化的三重刚需。内存池必须实现严格对齐,否则会导致性能暴跌、内存碎片甚至程序崩溃。C++内存池主要采用两种对齐方式:手动位运算(极致性能)和C++11标准关键字(优雅安全)。生产级内存池的SizeClass策略遵循「分级对齐+2的幂次取整」原则,小内存精细对齐(8/16字节),大内存粗粒度对齐(64/128字节),在内存利用率和管理效率间取得平衡。这种设计被tcmalloc/jemalloc等工业级内存池广泛采用,是保证高性能和稳定性的核心机制。

m0_62807361的博客 882

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

结构体内存对齐问题

结构体内存对齐问题,是笔试重点考察部分

如果需要沟通,可添加微信公众号找我免费答疑 2024

C++ 内存对齐四大核心问题解析

SIMD寄存器(如SSE/AVX)要求连续内存块严格对齐,否则触发硬件异常。掌握这四大核心问题的应对策略,即可在性能优化与系统稳定性之间找到最佳平衡点。时,显式对齐具有正向收益。

qq_39612333的博客 1236

内存字节对齐问题

刚才工作的时候跟一同事突然问起内存对齐问题,但是自己突然又想不起来,关于内存字节对齐,我想大家都一样,看了忘、然后下次再碰到的时候还得继续查,所以比较苦恼! 下面我就根据自己的总结来说下这个内存

假装在纽约 1817

【C语言】结构体内存对齐问题

我们已经基本掌握了结构体的使用了。那我们现在必须得知道结构体在内存中是如何存储的?内存是如何分配的?所以我们得知道如何。

李白同学的博客 3660

关于内存对齐的问题

从80X86结构看内存对齐问题 2010四月15 作者:  Simon_fu  lygno1:我是从这个地址拷贝过来的  http://my.unix-center.net/~Simon_fu/?p=262      最近在学习《The art of Assembly language》,该书对80X86 CPU的内存子系统结构,进行了一个比较详细的介绍

747

C内存对齐问题

其中关于内存对齐,讲了结构体以及位域,以及一些容易出错的地方,非常好。下面提到的对齐模数就是上面提到的有效对齐值N。注意,这里讲位域只能用在结构体或联合体中。注意位域对齐,既要考虑自身字节存储时需不需要填充位,又要考虑整体作为结构体存储时需不需要填充字节。

weixin_40194697的博客 640

对ARM处理器的内存对齐问题(译)

介绍 可以对齐或不对齐的内存访问。对齐的内存访问发生时的数据都位于其自然大小边界。例如,如果该数据类型的大小是4个字节,那么它属于被4整除的内存地址是位于其自然大小边界。未对齐的内存访问发生在所有其他情况下(在上面的例子中,内存地址时,是不能被4整除)。 ARM处理器的设计有效地访问对齐的数据。在ARM处理器上试图访问未对齐的数据会导致不正确的数据或显着的性能损失(这些不同的症状会在稍后讨论

起点 5167

Eigen3 内存对齐问题

总结一下Eigen3内存对齐的段错误

不要当真! 1720

[转]MSP430内存对齐问题

今天终于遇到了内存对齐的问题。 定义了一个数组 u16 xyz[3], 定义了一个指针 u8 *p_xyz = (u8*)xyz 定义了如下宏 1 2 3 4 5 6 #define XHi *(p_xyz) #define XLo *(p_xyz + 1) #define YHi *(p_xyz +...

lyd0813的博客 700
上一篇: What is Blog?
下一篇: Meaning Of Names In The Lord Of The Ring
MichaelGen
博客等级 码龄23年 0粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值