- 浏览: 371415 次
- 性别:
- 来自: 成都
-
文章分类
最新评论
-
tuspark:
关于javadoc这里讲解的更全面:《javadoc设置》。
Eclipse中生成javadoc【Z】 -
yuexiang1007:
帮我解决了问题,谢谢!!!
java.math.BigInteger使用心得总结 -
netwelfare:
个人感觉,文章对HashMap的遍历分析的有点浅,不如这里的介 ...
HashMap遍历的两种方式【Z】 -
memoryisking:
关于java.math.BigInteger讲解在这里可以看到 ...
java.math.BigInteger使用心得总结 -
巴尾的兔兔帅:
divide应该是除吧?不是减。dividepublic Bi ...
java.math.BigInteger使用心得总结
内存对齐
内存地址的对齐主要考虑三个因素:
1:对于每个成员的起始地址是他本身所占的整数倍
2:整个所占的内存是成员中占的地址内存最多的整数倍
3:有#pragma pack(int)进行设置,如果结构体某成员的sizeof大于你设置的,则按你的设置来对齐
系统默认是#pragma pack(4)
从计算结构变量字节大小的问题开始
问题:
在32位编译系统中,
typedef struct
{
int A 0-3
char B 4***//后面填充是为了满足第二条规则4的整数倍。
}T_s;
请问T_s的长度为几个字节?
答案:
题目不够严谨,跟编译器和编译选项有关的。
pc上的32位编译器一般缺省是4位对齐,所以长度会是8,此时在B后填充3字节,但对齐方式可以改的;
而有些嵌入系统的编译器缺省是不对齐的,所以长度为5。
比如在vc中,
如果
#pragma pack(4) //缺省
则sizeof(T_s)等于8
如果
#pragma pack
则sizeof(T_s)等于5
而且和cpu有关,在有的机器上int不一定就是32位
要因结构对齐而定,对齐方式可以是1, 2, 4, 8, or 16
1对齐:5;2对齐:6;4对齐:8;8对齐:8;16对齐:8
char 是8位的,结构长度是8字节,在B之前没有对齐被位,但在B之后要补3个字节以便于在数组中把下一个元素的A对齐到4字节边界,当然这都是一般编 译器在4字节对齐的情况下,如果某一编译器偏不这样实现,你也不能说它错了。因此如果我写一个编译器就把它的长度设为6字节,那么说它是6字节也正确。其 实字节对齐的知识对编写代码并没有什么帮助,并且也不应该利用这些知识。我以前曾经说过正确的态度是“不假设没有进行字节对齐,不假设编译时的对齐方式 (包括可以不进行对齐)”。
计算结构变量的大小就必须讨论数据对齐问题。为了CPU存取的速度最快(这同CPU取数操作有关,详细的介绍可以参考一些计算机原理方面的书),C++在处理数据时经常把结构变量中的成员的大小按照4或8的倍数计算,这就叫数据对齐(data alignment)。这样做可能会浪费一些内存,但理论上速度快了。当然这样的设置会在读写一些别的应用程序生成的数据文件或交换数据时带来不便。MS VC++中的对齐设定,有时候sizeof得到的与实际不等。一般在VC++中加上#pragma pack(n)的设定即可.或者如果要按字节存储,而不进行数据对齐,可以在Options对话框中修改Advanced compiler页中的Data alignment为按字节对齐。
看一个例子:
将A写入二进制文件
#pragma pack (1)
struct A
{
char a;
int b;
};
文件
61 06 00 00 00
#pragma pack (2)
struct A
{
char a;
int b;
};
文件
61 CC 06 00 00 00
#pragma pack (4)
struct A
{
char a;
int b;
};
文件
61 CC CC CC 06 00 00 00
#pragma pack (4)
struct A
{
char a;
int b;
};
文件
61 CC CC CC 06 00 00 00
#pragma pack (8)
struct A
{
char a;
int b;
};
文件
61 CC CC CC 06 00 00 00
该例子作者得出的结论:
结论是 实际的对齐长度 = [pack指定的对齐长度]和[struct中最长成员的长度]较小的一个。
[C] 结构对齐
http://community.csdn.net/Expert/FAQ/FAQ_Index.asp?id=182474
在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间;各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间
例如,下面的结构各成员空间分配情况
struct test
{
char x1;
short x2;
float x3;
char x4;
};
结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界,因此,编译器在x2和x1之 间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然对界地址上,在它们前面不需要额外的填充字节。在test结构中,成员x3要求 4字节对界,是该结构所有成员中要求的最大对界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了3个空字节。整个结构所占据空 间为12字节。
更改C编译器的缺省分配策略
一般地,可以通过下面的两种方法改变缺省的对界条件:
· 使用伪指令#pragma pack ([n])
· 在编译时使用命令行参数
#pragma pack ([n])伪指令允许你选择编译器为数据分配空间所采取的对界策略:
例如,在使用了#pragma pack (1)伪指令后,test结构各成员的空间分配情况就是按照一个字节对齐了
#pragma pack(push) //保存对齐状态
#pragma pack(1)
#pragma pack(pop)
编译器默认都是8字节对齐;
=============================================================================
http://data.gameres.com/message.asp?TopicID=13636
什么是内存对齐
考虑下面的结构:
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》。
字,双字,和四字在自然边界上不需要在内存中对齐。(对字,双字,和四字来说,自然边界分别是偶数地址,可以被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;
发表评论
-
C#设计模式[链接]
2011-01-05 14:19 882http://zhenyulu.cnblogs.com/cat ... -
C/C++预处理、编译、链接过程【Z】
2011-01-05 14:07 1810在Linux下进行C语言编程,必然要采用GNU GCC来编 ... -
c语言输出重定向【Z】
2010-10-03 22:41 3041可以使用重定向操作符将命令输入和输出数据流从默认位置重定向到不 ... -
C++标准库【Z】
2010-09-29 22:42 936C++标准库的内容分为10类: C1.语言支持 C2.输入/ ... -
c语言socket编程指南
2010-09-29 20:39 1306介绍 Socket 编程让你沮丧吗?从man page ... -
kmp算法【Z】
2010-09-24 19:02 829我们这里说的KMP不是拿来放电影的(虽然我很喜欢这个软件), ... -
虚拟继承、虚函数学习总结【Z】
2010-09-24 16:03 1516虚拟继承、虚函数学习总结 ... -
C++对象内存布局测试总结【Z】
2010-09-24 16:02 1143对于普通的C++对象内存布局,简单得不得了,就不做总结了。这里 ... -
C++隐式成员函数2【Z】
2010-09-24 15:37 9811 编译器自动生成的基本函数 C++编译器会在开发人员没有声 ... -
C++隐式成员函数【Z】
2010-09-24 15:27 1330这篇文章讲述的是C++提供的一些由编译器自 ... -
extern详解【Z】
2010-09-24 14:44 8231 基本解释 extern可 ... -
KMP字符串匹配算法【Z】
2010-09-14 22:44 1201最普通的字符串匹配 ... -
C语言变量存储类型
2010-09-14 19:58 1230C语言变量存储类型 auto static extern ... -
C/C++中的void
2010-09-14 19:54 10041.void的含义 (1)voi ... -
排序算法
2010-09-10 11:10 11001、稳定排序和非稳定排序 简单地说就是所有相等的数经过某种排 ... -
C++ Socket连接实现【转载】
2010-08-31 21:27 2957C++ socket程序 下面是一个C++调用windo ... -
虚函数/纯虚函数
2010-08-31 19:17 5591.首先:强调一个概念 定义一个函数 ... -
纯虚函数
2010-08-31 19:07 1053一、定义. 纯虚函数是在基类 中声明的虚函数,它在基类中没 ... -
C++ 对象的内存布局
2010-08-31 16:38 974转载-->C++ 对象的内存布局 ... -
虚继承
2010-08-31 16:06 827虚拟继承在一般的应用 ...
相关推荐
内存对齐,值得一读的内容, 内存对齐,值得一读的内容, 内存对齐,值得一读的内容, 内存对齐,值得一读的内容,
### 内存对齐原理详解 #### 一、什么是内存对齐? 在计算机科学中,内存对齐(Memory Alignment)是指数据类型与内存地址之间的关系。简单来说,就是指数据结构(尤其是结构体中的成员)在内存中的起始地址应该...
了解和掌握C语言内存对齐的机制对于编写高效且性能优越的程序至关重要。特别是对于初学指针和系统底层操作的C语言学生而言,深入理解内存对齐不仅可以帮助他们写出运行更快的代码,还能帮助他们更好地理解计算机底层...
sizeof(结构体)和内存对齐 sizeof(结构体)和内存对齐是C语言中一个重要的概念,它们之间存在着紧密的联系。sizeof(结构体)是指结构体在内存中的大小,而内存对齐是指编译器为了提高程序的效率和可移植性,对内存...
内存对齐是计算机编程中一个重要的概念,尤其是在优化程序性能和保证系统兼容性时不可或缺。内存对齐,简单来说,是指在计算机系统中,数据在内存中的存储位置必须按照特定的规则进行排列,以满足处理器的访问效率和...
内存对齐是计算机科学中关于数据结构存储的一种优化策略,主要应用于C++等低级编程语言。内存对齐的目标是确保数据在内存中的位置能够高效地被CPU访问,避免不必要的额外内存读取操作,提高程序运行速度。 在C++中...
在C语言中,内存对齐是一个重要的概念,它涉及到数据在计算机内存中的存储方式。内存对齐的主要目的是提高数据存取效率,减少CPU访问内存时的额外开销。当数据按照特定的规则排列在内存中,可以避免处理器进行不必要...
内存对齐是计算机科学中的一种优化技术,主要应用于编译器设计和编程语言实现中,目的是提高数据访问的效率和兼容性。它涉及到处理器架构、操作系统以及编程语言等多个层面。以下将详细介绍内存对齐的规则及其作用。...
面试的时候内存对齐问的比较多,特从网上总结的面试题1.
内存对齐详解 内存对齐是计算机科学中一个重要的概念,它指的是在计算机内存中对数据成员的对齐方式。对齐是指将数据成员存储在内存中的地址上对其进行调整,以便提高数据访问的效率和安全性。本文将详细解释内存...
### C++内存对齐原理与实践 #### 一、引言 C++作为一种高效且功能强大的编程语言,在处理数据时需要特别关注内存管理问题。内存对齐是C++中的一个重要概念,它涉及到如何在内存中排列数据以提高程序的执行效率。...
这个大小是经过内存对齐调整后的结果。那么,为什么需要内存对齐呢?内存对齐是为了提高程序的执行效率和可移植性。在不同的体系结构中,CPU 对不同类型的数据的处理速度是不同的。为了提高程序的执行效率,编译器会...
内存对齐是指在计算机内存中,按照一定的规则对数据进行排列,以便于访问和处理。这种规则涉及到数据的存储地址和大小,以确保数据的存取操作尽可能地高效。在C/C++中,内存对齐的概念是数据结构和硬件交互的关键...
内存对齐是C语言中一个重要的概念,它影响着数据在内存中的存储方式和访问效率。通过深入理解内存对齐的原理和应用,程序员可以写出更高效、更可靠的代码,提高程序的性能和稳定性。在实际编程中,合理地利用内存...
### C++内存对齐原理与实践 #### 一、引言 在C++编程中,内存对齐(Memory Alignment)是一项重要的技术,它涉及到如何在内存中存储数据以提高程序的运行效率。良好的内存对齐不仅可以减少数据访问的时间,还可以...
在IT行业中,内存对齐是程序设计中一个重要的概念,特别是在使用C++或者C这样的低级别编程语言时。本文将详细解析"3.2VC内存对齐准则",这一主题通常与微软的Visual C++(VC)编译器相关,因为不同的编译器可能有...
内存对齐是计算机科学中的一个重要概念,特别是在编程和系统设计中。它涉及到如何在内存中有效地安排数据结构,以优化性能、确保正确性并利用硬件特性。在这个“商业编程-源码-关于内存对齐.zip”文件中,我们很可能...
结构体内存对齐是计算机科学中关于数据结构在内存中存储的一种优化策略,主要涉及到C语言编程。内存对齐的目的是为了提高数据存取效率和兼容性,同时也与处理器架构和编译器有关。在C语言中,结构体的内存布局并不...
内存对齐是计算机科学中关于数据存储的一种策略,它的目的是优化数据访问速度并节省内存空间。在C语言中,`sizeof`操作符用于获取数据类型或变量所占用的内存大小。在描述的文档中,主要讨论了内存对齐在结构体`...