gcc数据对齐之: howto 1.
GCC支持用__attribute__为变量、类型、函数、标签指定特殊属性。这些不是编程语言标准里的内容,而属于编译器对语言的扩展。 本文介绍其中的两个属性:aligned和packed。
aligned
aligned属性最常用在变量声明上。它的作用是告诉GCC,为变量分配内存时,要分配在对齐的内存地址上。什么是对齐的内存地址呢?
一般计算机的内存是以字节(byte,等于8bit)为最小单元的。内存地址相当于从0开始的字节偏移数。如果一个内存地址是N的倍数,我们就说它是N字节对齐的(N-byte aligned)。
对于C/C++中的基本数据类型,假设它的长度为n字节,那么该类型的变量会被编译器默认分配到n字节对齐的内存上。例如,char的长度是1字节,char类型变量的地址将是1字节对齐的(任意值均可); int的长度是4字节,所以int类型变量将被分配到4字节对齐的地址上。这种默认情况下的变量对齐方式又称作自然对齐(naturally aligned)。
但是,有时候我们希望改变这种默认情况。这时候就可以使用aligned属性了。例如:
int x __attribute__ ((aligned (16))) = 0;
告诉编译器把变量x分配在16字节对齐的内存地址上,而非默认的4字节对齐。
编译器之所以默认让变量自然对齐,是因为这种对齐情况下的内存访问是最高效的。受限于硬件实现,非对齐内存访问的性能会有所下降;甚至在有些处理器(如DSP、早期的ARM)上,非对齐的内存访问根本就不支持, 将直接引发错误。而我们之所以需要自己指定对齐方式,很多时候是程序优化的需求。例如,在x86平台上要使用SSE指令,所操作的数据在内存中就必须是16字节对齐的。
aligned不仅可以用作变量属性,还能用作函数属性和数据类型属性。它作为函数属性时的作用等价于对函数使用-falign-functions这一优化选项。 当它用作数据类型的属性时,相当于告诉编译器,这一类型的所有变量都要按指定字节数对齐。
aligned与结构体
结构体是一种数据类型,它与aligned有一些特殊的关系。上面提到,aligned可以用于指定数据类型的属性,因此可以用于结构体,例如:
struct __attribute__ ((aligned (8))) my_struct1 {
short f[3];
};
但这么用不仅会影响为该结构体变量分配内存的位置,还可能影响其内存占用。在上面的例子中,这个结构体内有3个两字节的short,本来只需要占用6字节的内存; 但由于指定了8字节对齐,编译器会在该结构体尾部填充额外的2个字节,使得这个结构体的大小为8字节。为什么要填充多余的字节呢?因为只有将该结构体补足8个字节,才能保证在这个结构体类型的数组中,数组中每个元素 都是8字节对齐的(考虑到数组元素在内存中的连续存放)。
aligned也可以用在结构体的成员上,这时就成了对变量指定属性。GCC的相关文档里提到,C语言规定结构体类型必须至少对齐到其所有成员变量对齐字节数的最小公倍数, 因此指定结构体的对齐完全可以通过指定其中成员变量的对齐来实现,不过前者明显可读性好些。
当aligned属性用于数据类型(比如结构体)的时候,只能增加对齐字节数而不能减小。例如,下面的结构体本身大小有6字节,虽然指定了4字节对齐,但并不能达到目的,最终GCC还是会按8字节对齐处理。
struct __attribute__ ((aligned (4))) my_struct2 {
short f[3];
};
如果要减小对齐字节数,需要用到下面介绍的packed属性。
packed
packed属性的主要目的是让编译器更紧凑地使用内存。当它用于变量时,告诉编译器该变量应该有尽可能小的对齐,也就是1字节对齐。当它用于结构体时 ,相当于给该结构体的每个成员加上了packed属性,这时该结构体将占用尽可能少的内存。例如:
struct __attribute__ ((packed)) my_struct3 {
char c;
int i;
};
这个结构体被指定了packed,所以它的成员变量将是1字节对齐的,也就是说成员i将紧跟着成员c,从而使得该结构体的实际大小为5字节。 如果不指定packed,由于要满足成员i的4字节对齐要求(它是int型的),编译器将在成员c之后填充3个字节,使得这个结构体实际大小变为8字节。
采用packed属性虽然可以节省内存,但它会导致非对齐的内存访问。例如上述结构体的int型成员变量i,它的内存地址将不是4的倍数,访问它时就是非对齐访问。 当用.或->操作符存取结构体成员时,编译器会保证存取到正确的值;但如果用指针直接访问非对齐的成员变量,就只能指望处理器支持非对齐访问了,否则将会出错。 这也是很多人认为给结构体指定packed属性不太安全的原因。
什么时候用packed?
上面说到使用packed可能“不安全”,但为什么还要用呢?什么时候会需要它呢?
使用packed最重要的场合莫过于处理跟文件格式或网络协议有关的二进制数据了。这些格式或协议是不能容忍多余字节的,所以当用结构体表示其数据时,必须阻止编译器填充字节。 这正是packed的设计初衷。
另外还可以用packed来节省内存,不过这是以牺牲性能为代价的,不是什么好方法。通过适当排列结构体成员的顺序可以使得需要填充的字节数尽可能少,这才是应该考虑的措施。
我在最近的一个项目中需要从RGB像素生成Bitmap(位图)文件。Bitmap文件的头信息是用结构体描述的。 一开始写入磁盘的文件总是不对,分析发现正是因为编译器在我的结构体里填充了多余字节来满足对齐的要求。简单一个packed就解决了这个问题。
但正如前面所说,使用packed属性具有潜在的问题。如果一个结构体被packed了,尽量不要使用指向其内部成员变量的指针,除非你真的知道你在做什么。
转载:
http://blog.shengbin.me/posts/gcc-attribute-aligned-and-packed
gcc数据对齐之: howto 1.的更多相关文章
- gcc数据对齐之: howto 2.
原文链接:http://www.catb.org/esr/structure-packing/ 谁应阅读本文 本文探讨如何通过手工重新打包C结构体声明,来减小内存空间占用.你需要掌握基本的C语言知识, ...
- gcc 数据对齐之:总结篇.
通过上面的分析,总结结构体对齐规则如下: 1.数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的对齐按照#pragm ...
- [翻译] GCC 内联汇编 HOWTO
目录 GCC 内联汇编 HOWTO 原文链接与说明 1. 简介 1.1 版权许可 1.2 反馈校正 1.3 致谢 2. 概览 3. GCC 汇编语法 4. 基本内联 5. 扩展汇编 5.1 汇编程序模 ...
- 数据对齐 posix_memalign 函数详解
对齐 数 据的对齐(alignment)是指数据的地址和由硬件条件决定的内存块大小之间的关系.一个变量的地址是它大小的倍数的时候,这就叫做自然对齐 (naturally aligned).例如,对于一 ...
- C++中数据对齐问题。struct、union、enum,类继承。再谈sizeof()
首先是struct,在C++中,结构体其实和class有很大的相似了.但是有一点不同的是,struct默认是public,而class中是private. 当然,struct继承等用法也是可以的. 共 ...
- data structure alignment(数据对齐)
概述: 数据对齐指数据在计算机内存中排放和获取的方式.包含三个方面:数据对齐(data alignment).数据结构填充(data alignment).打包(packing) 如果数据是自然对齐的 ...
- gcc数据结构对齐之:why.
gcc 支持 aligned 和 packed 属性指定数据对齐,那么在了解对齐规则之前,需要解决第一个以为,我们为什么需要数据对齐?请看下图: 相信学过汇编的朋友都很熟悉这张图,这张图就是CPU与内 ...
- C++中数据对齐
大体看了看数据对齐,不知道是否正确,总结如下: struct A { char name; double dHeight; int age; }; sizeof(A) = (1+7+8+4+4) = ...
- C/C++数据对齐汇总
C/C++数据对齐汇总 这里用两句话总结数据对齐的原则: (1)对于n字节的元素(n=2,4,8,...),它的首地址能被n整除,才干获得最好的性能: (2)如果len为结构体中长度最长的变量,s ...
随机推荐
- C语言写数据库(三)
遇到的问题以及解决思路方法 1.外部导入数据库文件 进入mysql,创建数据库sh_robot source /home/exbot/sh_robot.sql 查看数据库编码格式 show varia ...
- NOI数论姿势瞎总结(Pi也没有)
Miller-Rabin素数检测 费马小定理:没人不会吧. 二次探测:如果\(n\)是质数,\(x^2 \equiv 1\ (\mod n)\)的解只有\(x \equiv 1\)或\(x \equi ...
- 【Java基础】谈谈集合.List
摘自:https://www.cnblogs.com/54chensongxia/p/11722828.html 目录 1. ArrayList 1.1 ArrayList的构造 1.2 add方法 ...
- Java内存模型之可见性问题
本博客系列是学习并发编程过程中的记录总结.由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅. 并发编程系列博客传送门 前言 之前的文章中讲到,JMM是内存模型规范在Java语 ...
- Kotlin的高阶函数和常用高阶函数
Kotlin的高阶函数和常用高阶函数 文章来源:企鹅号 - Android先生 高阶函数的定义 将函数当做参数或者是返回值的函数 什么是高阶函数 可以看看我们常用的 函数: 首先我们可以知道, 是 的 ...
- 解析XML的几种方式:DOM、SAX、PULL
DOM解析 解析器读入整个文档,然后构建一个主流内存的树结构,然后代码就可以使用dom接口来操作这个树结构. 优点: 整个文档树在内存中,便于操作:支持删除.修改.重新排列等多种功能. 通过树形结构存 ...
- SSH上传文件出现error
如上图,因为我在SSH上的用户没有对Linux服务器的文件夹的修改权限,我在linux上把文件夹的权限改为可读可改,就可以了
- leetcode-easy-trees-108. Convert Sorted Array to Binary Search Tree
mycode 81.75 # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, x) ...
- golang defer那些坑
defer以下几个特性,使用时需要关注下. 即时的参数传递 调用os.Exit()时defer不会被执行 defer与return的先后顺序 1.即时的参数传递 定义defer时传入的参数,是作为拷贝 ...
- android 面试汇总<二>
Animation Q:Android中有哪几种类型的动画? 技术点:动画类型 参考回答: 常见三类动画 View动画(View Animation)/补间动画(Tween animation):对V ...