结构/类对齐的声明方式

gcc和windows对于modifier/attribute的支持其实是差不多的。比如在gcc的例子中,内存对齐要写成:

class X
{
//...
} __attribute__((aligned()));

但是实际上你写成

class __attribute__((aligned())) X
{
/*...*/
};

gcc一样可以识别。这样MSVC和gcc就可以使用宏完成跨平台编译。

对齐类型的变量在堆与栈上的分配

对齐在以下场合都能提示编译器为它的变量分配对齐的地址:

void foo()
{
X v; // v是个栈上的16字节对齐的变量
X* p = new X; // p是堆上的16字节对齐的指针
X* a = new X[ARRAY_SIZE]; // 那么这个呢?
}

栈上的变量堆上分配出的变量,因为align这个hint的存在,都能满足16字节对齐的要求。但是数组呢?按照一般规律来分析,对齐后的sizeof(X),一定是对齐的整数倍。比如16字节对齐的话,那么X的大小只能是16的倍数。所以对于本例的数组而言,编译器应该也能知道a应该是16字节对齐的。

但是事实上挺奇怪。在MSVC上,p和a都很好的遵守了对齐的要求;在gcc上,p是对齐的,但是a却不是。其实这个问题在2004年便有人提出来,只是到目前为止一直都没有人动手过。当然,标准也没有规定X的数组就一定是要对齐的。要解决这个问题,要么重载class的operator new/delete,要么用memalign/aligned_malloc分配出对齐的内存,再placement new。出于易用性,我选择的是操作符重载。

clang对于对齐的支持更干脆:16B的对齐已经够用了。所以align完全被编译器忽视了。结果Intel出来了AVX,Clang就傻逼了。不知道这个问题3.4会不会修正。

编译器如何实现内存对齐

MSVC在x86下默认是支持的4B的内存对齐。也就是说在函数入口处,ESP和EBP只保证是4字节对齐的。这时,当前函数域栈上变量的地址都是ESP + 4 * x的形式。如果函数体内有对齐的变量,例如:

void foo()
{
int __declspec(align()) x;
// ...
}

那么编译器在代码生成时,会在函数的前部插入一段称为prolog的代码,这段代码会将堆栈修正为16B对齐,比如

PUSH EBP
MOV EBP, ESP
SUB ESP, XXX
AND ESP, 0xFFFFFFF0h

这样ESP就一定是16字节对齐的。这个时候给x分配的地址,就可以是ESP + 0x10 * n的形式,这样就满足了对齐的需要。

在GCC上,gcc认为所有的函数都有义务在调用其它函数的时候,ESP是16字节对齐的(当然,可以通过编译选项修改这一要求)。不光是调用方会这样保证,被调用方也是这样默认的。所以GCC为了调用效率更高一点,便根据调用方的假设,去掉了“堆栈修正”这个步骤。

原来的代码可能就变成了

PUSH EBP             ; 假设这里的ESP是16B对齐的,Push了EBP,ESP就是16x-4了。
MOV EBP, ESP
SUB ESP, 0x0000023Ch ; 减完以后这里又是16字节对齐了

那么当被调用方遵守这个约定的时候,ESP当然就是16字节对齐的。但是有一种情况例外。在MinGW下,线程的入口函数是被API回调的。这个函数很可能是按照Windows的标准4个字节对齐的。这样,在没有堆栈修正的情况下,整个线程调用链16B对齐的默契就被打破了。如果这个时候出现了SSE代码试图存取“16字节对齐”的变量,那可能就会发生segment fault的异常,因为这些变量的地址并不是对齐的。

解决这个问题,有两种常见的办法:第一,写一个Wrapper函数,对齐ESP后转发调用;第二,使用编译选项-mstackrealign。这个选项会为所有函数增加堆栈修正的PROLOG代码,以保证函数栈帧一定是按照16字节或用户指定大小对齐。

Windows+GCC下内存对齐的常见问题的更多相关文章

  1. windows和Linux内存的对齐方式

    一.内存对齐的初步解说 内存对齐能够用一句话来概括: "数据项仅仅能存储在地址是数据项大小的整数倍的内存位置上" 比如int类型占用4个字节,地址仅仅能在0,4,8等位置上. 例1 ...

  2. Windows平台下的内存泄漏检测

    在C/C++中内存泄漏是一个不可避免的问题,很多新手甚至有许多老手也会犯这样的错误,下面说明一下在windows平台下如何检测内存泄漏. 在windows平台下内存泄漏检测的原理大致如下. 1. 在分 ...

  3. C/C++: C++位域和内存对齐问题

    1. 位域: 1. 在C中,位域可以写成这样(注:位域的数据类型一律用无符号的,纪律性). struct bitmap { unsigned a : ; unsigned b : ; unsigned ...

  4. C++位域和内存对齐问题

    1. 位域: 1. 在C中,位域可以写成这样(注:位域的数据类型一律用无符号的,纪律性). struct bitmap { unsigned a : ; unsigned b : ; unsigned ...

  5. C语言内存对齐

    转:http://blog.csdn.net/embeddedman/article/details/7429976 首先由一个程序引入话题:  1 //环境:vc6 + windows sp2 2  ...

  6. 对C语言内存对齐的初步了解

    在解释内存对齐的作用前,先来看下内存对齐的规则: 1. 对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员的自身 ...

  7. C++成员变量内存对齐问题,ndk下非对齐的内存访问导致BUS_ADRALN

    同样的代码,在vs下运行正常,在android ndk下却崩溃: signal 7(SIGBUS),code 1 (BUS_ADRALN),fault addr 0xe6b82793 Func(sho ...

  8. struct内存对齐1:gcc与VC的差别

    struct内存对齐:gcc与VC的差别 内存对齐是编译器为了便于CPU快速访问而采用的一项技术,对于不同的编译器有不同的处理方法. Win32平台下的微软VC编译器在默认情况下采用如下的对齐规则:  ...

  9. [原创]使用GCC创建 Windows NT 下的内核DLL

    原文链接:使用GCC创建 Windows NT 下的内核DLL 在温习<<Windows 2000 Driving>>分层驱动程序一章的时候,看到了关于紧耦合驱动连接方式,这种 ...

随机推荐

  1. 进击的Python【第六章】:Python的高级应用(三)面向对象编程

    Python的高级应用(三)面向对象编程 本章学习要点: 面向对象编程介绍 面向对象与面向过程编程的区别 为什么要用面向对象编程思想 面向对象的相关概念 一.面向对象编程介绍 面向对象程序设计(英语: ...

  2. java提升路线书单(原文自知乎刘欣)

    复制黏贴自知乎刘欣大神,作为个人的书单与指导路线 原文链接:https://www.zhihu.com/question/19848946/answer/92536822   刘欣 追寻内心的真正兴趣 ...

  3. 模拟CSS3 多组位移运动属性的框架封装

    obj是将要运动的对象,json是运动完成时的位移结果. 封装要点: 1.定时器开关flag的定义要放在for in结构的外面,否则,每遍历一次,都会定义一个 新的flag 2.if(current ...

  4. 安卓开发error opening trace file: No such file or directory (2)报错原因

    error opening trace file: No such file or directory (2) 这个问题的出现是因为运行的测试机android系统版本和项目api不一致导致. 改成一样 ...

  5. Vue - 过滤器

    1.内部过滤器 1):字母操作: ---- 针对字符串 A:capitalize B:uppercase C:lowercase 2):json过ingfy滤器,可将表达式的值转化为Json字符串,本 ...

  6. my97DatePicker选择年、季度、月、周、日

    My97DatePicker是一款非常灵活好用的日期控件.使用非常简单. 下面总结下使用该日历控件选择年.季度.月.周.日的方法. .选择年 <input id="d1212" ...

  7. C#版 Winform界面 Socket编程 Client客户端

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  8. CodeSimth-.NetFrameworkDataProvider可能没有安装。解决方法

    原文地址:http://www.haogongju.net/art/2561889 1.下载System.Data.SQLite驱动:注意:根据自己的CPU选择是32位还是64位的驱动.建议选择4.0 ...

  9. CEF中select选项错位的解决方法

    使用cef加载页面,移动窗口后选项的位置并不会变化,仍保持上次打开的位置. 经过google查找到这是一个已经解决了的问题:https://bitbucket.org/chromiumembedded ...

  10. 使用VisualVM分析性能

    性能分析神器VisualVM VisualVM 是一款免费的,集成了多个 JDK 命令行工具的可视化工具,它能为您提供强大的分析能力,对 Java 应用程序做性能分析和调优.这些功能包括生成和分析海量 ...