Windows+GCC下内存对齐的常见问题
结构/类对齐的声明方式
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下内存对齐的常见问题的更多相关文章
- windows和Linux内存的对齐方式
一.内存对齐的初步解说 内存对齐能够用一句话来概括: "数据项仅仅能存储在地址是数据项大小的整数倍的内存位置上" 比如int类型占用4个字节,地址仅仅能在0,4,8等位置上. 例1 ...
- Windows平台下的内存泄漏检测
在C/C++中内存泄漏是一个不可避免的问题,很多新手甚至有许多老手也会犯这样的错误,下面说明一下在windows平台下如何检测内存泄漏. 在windows平台下内存泄漏检测的原理大致如下. 1. 在分 ...
- C/C++: C++位域和内存对齐问题
1. 位域: 1. 在C中,位域可以写成这样(注:位域的数据类型一律用无符号的,纪律性). struct bitmap { unsigned a : ; unsigned b : ; unsigned ...
- C++位域和内存对齐问题
1. 位域: 1. 在C中,位域可以写成这样(注:位域的数据类型一律用无符号的,纪律性). struct bitmap { unsigned a : ; unsigned b : ; unsigned ...
- C语言内存对齐
转:http://blog.csdn.net/embeddedman/article/details/7429976 首先由一个程序引入话题: 1 //环境:vc6 + windows sp2 2 ...
- 对C语言内存对齐的初步了解
在解释内存对齐的作用前,先来看下内存对齐的规则: 1. 对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(#pragma pack()指定的数,这个数据成员的自身 ...
- C++成员变量内存对齐问题,ndk下非对齐的内存访问导致BUS_ADRALN
同样的代码,在vs下运行正常,在android ndk下却崩溃: signal 7(SIGBUS),code 1 (BUS_ADRALN),fault addr 0xe6b82793 Func(sho ...
- struct内存对齐1:gcc与VC的差别
struct内存对齐:gcc与VC的差别 内存对齐是编译器为了便于CPU快速访问而采用的一项技术,对于不同的编译器有不同的处理方法. Win32平台下的微软VC编译器在默认情况下采用如下的对齐规则: ...
- [原创]使用GCC创建 Windows NT 下的内核DLL
原文链接:使用GCC创建 Windows NT 下的内核DLL 在温习<<Windows 2000 Driving>>分层驱动程序一章的时候,看到了关于紧耦合驱动连接方式,这种 ...
随机推荐
- 更改pip安装源的镜像解决安装总是timeout的情况
由于国外的pip源总是由于各种原因不能被访问或者网速过慢,而造成的timeout错误 解决方法是修改pip的配置文件(如果没有配置文件在相应的地方新建,配置文件的路径和名字参考这里),设置安装包时候访 ...
- 进击的Python【第二十章】
1.Django请求的生命周期 路由系统 -> 试图函数(获取模板+数据=>渲染) -> 字符串返回给用户 2.路由系统 /index/ -> 函数或类.as_view() / ...
- 微信开发笔记:获取用户openid,以及用户头像昵称等信息
微信开发的时候有一个很便利的途径来进行一个用户的一步注册登录,就是使用用户的微信信息来直接进行登陆,可以省去很多不必要的麻烦.那具体这些信息是如何来获取的呢? 首先呢,我们需要对微信进行一个授权,让微 ...
- InnoDB还是MyISAM 再谈MySQL存储引擎的选择
两种类型最主要的差别就是Innodb 支持事务处理与外键和行级锁.而MyISAM不支持.所以MyISAM往往就容易被人认为只适合在小项目中使用. 我作为使用MySQL的用户角度出发,Innodb和My ...
- iOS之 利用通知(NSNotificationCenter)获取键盘的高度,以及显示和隐藏键盘时修改界面的注意事项
我们在开发中会遇到这样的情况:调用键盘时需要界面有一个调整,避免键盘遮掩输入框. 但实现时你会发现,在不同的手机上键盘的高度是不同的.这里列举一下: //获取键盘的高度 /* iphone 6: 中文 ...
- oracle(sql)基础篇系列(二)——多表连接查询、子查询、视图
多表连接查询 内连接(inner join) 目的:将多张表中能通过链接谓词或者链接运算符连接起来的数据查询出来. 等值连接(join...on(...=...)) --选出雇员的名字和雇员所 ...
- [转]WebPack 常用功能介绍
概述 Webpack是一款用户打包前端模块的工具.主要是用来打包在浏览器端使用的javascript的.同时也能转换.捆绑.打包其他的静态资源,包括css.image.font file.templa ...
- php备份数据库
php备份数据库原理和方法 原理 查找所有表 查找所有字段,列出所有字段名 字段类型等信息 查找所有数据 读取后注意特殊符号转换addslashes() 生成sql 把数据库格式化生成对应sql 相关 ...
- 【转】《从入门到精通云服务器》第六讲—OpenStack基础
前五期的<从入门到精通云服务器>受到了广泛好评,收到留言,有很多读者对云计算相关的技术非常感兴趣.应观众要求,我们这期要安利一条纯技术内容.准备好瓜子.花生,随小编一起进入OpenStac ...
- CATransition(os开发之画面切换) 的简单用法
CATransition 的简单用法 //引进CATransition 时要添加包“QuartzCore.framework”,然后引进“#import <QuartzCore/QuartzCo ...