`
逆风的香1314
  • 浏览: 1431721 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

大小端和存储器对齐

阅读更多
xiaoB.M. @ 2007-09-12 21:24

我们常常看到“alignment", "endian"之类的字眼, 但很少有C语言教材提到这些概念。 实际上它们是与处理器与内存接口, 编译器类型密切相关的。考虑这样一个例子: 两个异构的CPU进行通信, 定义了这样一个结果来传递消息:
struct Message
{
      short opcode;
      char subfield;
      long message_length;
      char version;
      short destination_processor;
}message;
     用这样一个结构来传递消息貌似非常方便, 但也引发了这样一个问题: 若这两种不同的CPU对该结构的定义不一样, 两者就会对消息有不同的理解。 有可能导致二义性。 会引发二义性的有这两个方面:
     1.内存地址对齐
     2.大小端定义
     本文先介绍内存地址对齐和大小端的概念, 再回头来看这个例子就豁然开朗了。
内存地址对齐
     洋名叫做" Byte Alignment"。
     大部分16位和32位的CPU不允许将字或者长字存储到内存中的任意地址。 比如Motorola 68000不允许将16位的字存储到奇数地址中, 将一个16位的字写到奇数地址将引发异常。
     实际上, 对于c中的字节组织, 有这样的对齐规则:    
     1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
     2) 结构体每个成员相对于结构首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
     3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。

     不同CPU的对其规则可能不同, 请参考手册。
     为什么会有上述的限制呢? 理解了内存组织, 就会清楚了
     CPU通过地址总线来存取内存中的数据,32位的CPU的地址总线宽度既为32位置, 标为A[0:31]。 在一个总线周期内,CPU从内存读/写32位。 但是CPU只能在能够被4整除的地址进行内存访问,这是因为: 32位CPU不使用地址总线的A1和A2(比如ARM,它的A[0:1]用于字节选择, 用于逻辑控制, 而不和存储器相连, 存储器连接到A[2:31])。访问内存的最小单位是字节(byte), A0和A1不使用, 那么对于地址来说, 最低两位是无效的, 所以它只能识别能被4整除的地址了。 在4字节中,通过A0和A1确定某一个字节。
     再看看刚才的message结构, 你想想它占了多少字节? 别想当然的以为是10个字节。 实际上它占了12个字节。 不信?
     用sizeof(message)看吧。 对于结构体, 编译器会针对起中的元素添加"pad"以满足字节对齐规则。
     message会被编译器改为下面的形式:
struct Message

{
     short opcode;
     char subfield;
     char pad1;      // Pad to start the long word at a 4
                    // byte boundary
     long message_length;
     char version;
     char pad2;      // Pad to start a short at a 2 byte boundary
     short destination_processor;
     char pad3[4];   // Pad to align the complete structure to a 16
                    // byte boundary
};
如果不同的编译器采用不同的对齐规则, 对传递message可就麻烦了。

大端(Big Endian)与小端(Little Endian)
Byte Endian是指字节在内存中的组织,所以也称它为Byte Ordering。  
     对于数据中跨越多个字节的对象, 我们必须为它建立这样的约定:
(1) 它的地址是多少?
(2) 它的字节在内存中是如何组织的?
     针对第一个问题,有这样的解释:
     对于跨越多个字节的对象,一般它所占的字节都是连续的, 它的地址等于它所占字节最低地址。(链表可能是个例外, 但链表的地址可看作链表头的地址)。
     比如: int x, 它的地址为0x100。 那么它占据了内存中的Ox100, 0x101, 0x102, 0x103这四个字节。
     上面只是内存字节组织的一种情况: 多字节对象在内存中的组织有一般有两种约定。 考虑一个W位的整数。 它的各位表达如下:
[Xw-1, Xw-2, ... , X1, X0]
     它的MSB (Most Significant Byte, 最高有效字节)为[Xw-1, Xw-2, ... Xw-8]; LSB (Least Significant Byte, 最低有效字节)为 [X7,X6,..., X0]。 其余的字节位于MSB, LSB之间。
     LSB和MSB谁位于内存的最低地址, 即谁代表该对象的地址? 这就引出了大端(Big Endian)与小端(Little Endian)的问题。
     如果LSB在MSB前面, 既LSB是低地址, 则该机器是小端; 反之则是大端。 DEC (Digital Equipment Corporation, 现在是Compaq公司的一部分)和Intel的机器一般采用小端。 IBM, Motorola, Sun的机器一般采用大端。 当然, 这不代表所有情况。 有的CPU即能工作于小端, 又能工作于大端, 比如ARM, PowerPC, Alpha。 具体情形参考处理器手册。
     举个例子来说名大小端: 比如一个int x, 地址为0x100, 它的值为0x1234567。 则它所占据的0x100, 0x101, 0x102, 0x103地址组织如下图:
     0x01234567的MSB为0x01, LSB为0x67。 0x01在低地址(或理解为"MSB出现在LSB前面,因为这里讨论的地址都是递增的), 则为大端; 0x67在低地址则为小端。
     认清这样一个事实: C中的数据类型都是从内存的低地址向高地址扩展,取址运算"&"都是取低地址。
     两个测试Bit Endian的小程序:
method_1

#i nclude <stdio.h>
int main(int argc, char *argv[])
{
     int c = 1;
     if ((*(char *)&c) == 1)
     {
         printf("little endian\n");
     }
     else
         printf("big endian");
     return 0;
}

     int c 在内存中的表达为: 0x00000001。 (这里假设int为4字节)。 用char可以截取一个字节。 LSB为0x01, 若它出现在c的低地址, 则为小端。

method_2

#i nclude <stdio.h>
int main(void)
{
     /* Each component to a union type is allocated storage at the
         beginning of the union */
     
     union
     {
        short n;
        char c[sizeof(short)];
     }un;
     un.n = 0x0102;

     if ((un.c[0] == 1 && un.c[1] == 2))
         printf("big endian\n");
     else if ((un.c[0] == 2 && un.c[1] == 1))
         printf("little endian\n");
     else
         printf("error!\n");
     return 0;
}
     union中元素的起始地址都是相同的——位于联合的开始。 用char来截取感兴趣的字节。
     区分大端与小端有什么用呢? 如果两个不同Endian的机器进行通信时, 就有必要区分了。



字节对齐
thieven 发表于 2005-12-8 21:26:00

有关字节对齐的介绍:


什么是对齐,以及为什么要对齐:
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。显然在读取效
率上下降很多。这也是空间和时间的博弈。
对齐的实现
通常,我们写程序的时候,不需要考虑对齐问题。编译器会替我们选择适合目标平台的对齐策略。当然,我们也可以通知给编译器传递预编译指令而改变对指定数据的对齐方法。
但是,正因为我们一般不需要关心这个问题,所以因为编辑器对数据存放做了对齐,而我们不了解的话,常常会对一些问题感到迷惑。最常见的就是struct数据结构的sizeof结果,出乎意料。为此,我们需要对对齐算法所了解。
对齐的算法:
由于各个平台和编译器的不同,现以本人使用的gcc version 3.2.2编译器(32位x86平台)为例子,来讨论编译器对struct数据结构中的各成员如何进行对齐的。
设结构体如下定义:
struct A
{
int a;
char b;
short c;
};
结构体A中包含了4字节长度的int一个,1字节长度的char一个和2字节长度的short型数据一个。所以A用到的空间应该是7字节。但是因为编译器要对数据成员在空间上进行对齐。
所以使用sizeof(strcut A)值为8。
现在把该结构体调整成员变量的顺序。
struct B
{
char b;
int a;
short c;
};
这时候同样是总共7个字节的变量,但是sizeof(struct B)的值却是12。
下面我们使用预编译指令#progma pack (value)来告诉编译器,使用我们指定的对齐值来取代缺省的。
#progma pack (2) /*指定按2字节对齐*/
struct C
{
char b;
int a;
short c;
};
#progma pack () /*取消指定对齐,恢复缺省对齐*/
sizeof(struct C)值是8。

修改对齐值为1:
#progma pack (1) /*指定按1字节对齐*/
struct D
{
char b;
int a;
short c;
};
#progma pack () /*取消指定对齐,恢复缺省对齐*/
sizeof(struct D)值为7。

对于char型数据,其自身对齐值为1,对于short型为2,对于int,float,double类型,其自身对齐值为4,单位字节。
这里面有四个概念值:
1.数据类型自身的对齐值:就是上面交代的基本数据类型的自身对齐值。
2.指定对齐值:#progma pack (value)时的指定对齐值value。
3.结构体或者类的自身对齐值:其成员中自身对齐值最大的那个值。
4.数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。
有了这些值,我们就可以很方便的来讨论具体数据结构的成员和其自身的对齐方式。有效对齐值N是最终用来决定数据存放地址方式的值,最重要。有效对齐N,就是表示“对齐在N上”,也就是说该数据的"存放起始地址%N=0".而数据结构中的数据变量都是按定义的先后顺序来排放的。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐排放,结构体本身也要根据自身的有效对齐值圆整(就是结构体成员变量占用总长度需要是对结构体有效对齐值的整数倍,结合下面例子理解)。这样就不能理解上面的几个例子的值了。
例子分析:
分析例子B;
struct B
{
char b;
int a;
short c;
};
假设B从地址空间0x0000开始排放。该例子中没有定义指定对齐值,在笔者环境下,该值默认为4。第一个成员变量b的自身对齐值是1,比指定或者默认指定对齐值4小,所以其有效对齐值为1,所以其存放地址0x0000符合0x0000%1=0.第二个成员变量a,其自身对齐值为4,所以有效对齐值也为4,所以只能存放在起始地址为0x0004到0x0007这四个连续的字节空间中,复核0x0004%4=0,且紧靠第一个变量。第三个变量c,自身对齐值为2,所以有效对齐值也是2,可以存放在0x0008到0x0009这两个字节空间中,符合0x0008%2=0。所以从0x0000到0x0009存放的都是B内容。再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求,0x0009到0x0000=10字节,(10+2)%4=0。所以0x0000A到0x000B也为结构体B所占用。故B从0x0000到0x000B共有12个字节,sizeof(struct B)=12;

同理,分析上面例子C:
#progma pack (2) /*指定按2字节对齐*/
struct C
{
char b;
int a;
short c;
};
#progma pack () /*取消指定对齐,恢复缺省对齐*/
第一个变量b的自身对齐值为1,指定对齐值为2,所以,其有效对齐值为1,假设C从0x0000开始,那么b存放在0x0000,符合0x0000%1=0;第二个变量,自身对齐值为4,指定对齐值为2,所以有效对齐值为2,所以顺序存放在0x0002、0x0003、0x0004、0x0005四个连续字节中,符合0x0002%2=0。第三个变量c的自身对齐值为2,所以有效对齐值为2,顺序存放
在0x0006、0x0007中,符合0x0006%2=0。所以从0x0000到0x00007共八字节存放的是C的变量。又C的自身对齐值为4,所以C的有效对齐值为2。又8%2=0,C只占用0x0000到0x0007的八个字节。所以sizeof(struct C)=8.

 
分享到:
评论

相关推荐

    cuda存储器

    常数存储器在某些情况下可以用来加速计算,但使用时需要注意其大小限制和正确地声明与访问。 6. **纹理存储器(Texture Memory)**:纹理存储器专门用于处理图像数据,提供了额外的缓存机制和过滤选项。它适用于...

    附录A-指令集1

    存储器寻址涉及到字节顺序(大端或小端)和对齐策略。对齐数据在存储器中的位置有助于减少硬件复杂性,提高运算效率,因为处理器在访问对齐的数据时通常能更快地完成操作。 寻址方式包括寄存器寻址、立即数寻址、...

    计算机应用基础(第3版)章节练习题答案.pdf

    文本对齐方式有左对齐、右对齐、居中、两端对齐和分散对齐。特殊字符如大于号和小于号可以通过特殊符号插入。视图模式包括页面视图、阅读版式视图等,用于不同用途的文档查看。段落缩进和制表符可以通过标尺进行调整...

    S3C44B0X存储控制器功能及应用

    该控制器支持小端和大端模式,这取决于外部ENDIAN引脚的状态,用户可以通过此引脚选择适合的数据对齐方式。处理器在复位时,默认根据ENDIAN引脚的状态来确定字节顺序,当ENDIAN为L时,系统工作在大端模式,反之则...

    大学计算机考试客观题——填空题.doc

    5. Word文档中的段落对齐方式包括左对齐、右对齐、居中、分散对齐和两端对齐。 6. Windows操作系统通过资源管理器来管理计算机的软硬件资源。 7. 许多系统设置功能集中在Windows XP的控制面板中。 8. 在Word中,通过...

    电大计算机应用基础理论题库.pdf

    14. 插入菜单用于绘制文本框,Word启动后默认打开文档1,段落对齐方式包括左对齐、居中、右对齐、两端对齐和分散对齐。 15. 页面视图可以显示图片和页眉页脚,打印预览则模拟实际打印效果,页面设置命令用于设定打印...

    计算机应用基础综合练习题.pdf

    段落对齐方式包括左对齐、右对齐、居中、两端对齐和分散对齐,其中两端对齐是默认设置。添加边框和选定操作对象是编辑文档的基本操作。 9. **表格处理**:在Word中,可以创建、编辑表格,单击单元格可选定,单元格...

    嵌入式学习资料 很好的东西

    结构变量`b`的大小取决于成员的大小和对齐方式,而变量`i`的存储顺序(大端或小端)取决于目标处理器的字节顺序。 总结来说,这份学习资料提供了一个深入理解计算机体系结构的起点,包括其基本组件、存储组织、数据...

    大工18秋《计算机应用基础》在线测试123满分答案.docx

    Word2010支持多种段落对齐方式,如右对齐、分散对齐、居中和两端对齐。 【Word2010页面布局】 页面布局菜单允许用户设置页边距、分栏等格式。 【Word2010字数统计】 Word2010具有字数统计功能,可以在“审阅”选项...

    大工春计算机应用基础作业123满分答案.pdf

    14. Word 2010的段落对齐方式包括右对齐、分散对齐、居中和两端对齐。 15. 十进制36转化为二进制是100100。 16. Word 2010具有文档字数统计功能。 17. 第四代计算机的主存储器使用半导体存储器。 18. 字符"A"的...

    大学计算机基础高等教育出版社课后答案.doc

    3. 文本对齐方式有两端对齐、居中对齐、右对齐和分散对齐。 4. 缩进类型包括首行缩进、悬挂缩进、左缩进和右缩进。 5. 在Excel中,工作表标签位于底部,用于切换不同工作表。 6. 数据筛选功能包括自动筛选和高级筛选...

    嵌入式考试习题复习资料

    小端存储器组织将高有效字节存放在低地址,低有效字节存放在高地址,而大端存储器组织则相反。数据边界对齐是指数据结构成员在内存中按照特定的对齐规则排列,以优化访问效率,通常与处理器架构和编译器设置有关。...

    大工15春《计算机应用基础》在线测试1、2、3全部答案.pdf

    - 段落对齐:Word 2010支持右对齐、分散对齐、居中、两端对齐等多种对齐方式。 - 数制转换:十进制36转化为二进制是100100。 - 字数统计:Word 2010具备字数统计功能。 - 第四代计算机存储器:主要使用半导体存储器...

    中职计算机应用基础试题.pdf

    30. **文本对齐方式**:Word提供了五种对齐方式,包括居中对齐、两端对齐、分散对齐、左对齐和右对齐。 **问答题解析:** 1. **绘制斜线表头步骤**:通常需要在表格的单元格中点击右键,选择“边框和底纹”,然后在...

    第四章ARM存储与异常中断处理.pptx

    这种存储方式对于处理多字节数据类型时的数据对齐和访问效率有着直接影响。 ARM的存储体系是一个层次化的结构,类似于金字塔形。从顶端开始,有寄存器、片上Cache、写缓存、TCM(紧耦合存储器)、片内SRAM,然后是...

    计算机组成原理简答题.doc

    本文将深入探讨简答题中涉及的一些核心概念,包括大小端对齐、指令周期、机器周期和时钟周期、总线判优、程序访问的局部性、指令和数据的区分,以及I/O编址方式。 1. 大小端对齐: 计算机中的数据存储方式有两种,...

    华中科技大学计算机组成原理慕课答案.doc

    10. **存储器对齐**:小端对齐模式下,低地址存储低位字节,高地址存储高位字节。4个连续存储单元存储32位数据,按照小端模式解析。 11. **标准移码**:标准移码是补码的所有位取反,不包括符号位。 12. **IEEE754...

    山东理工计算机组成原理研究生入学考试

    - 移码表示法主要用于表示浮点数的阶码E,便于比较两个数的大小和进行指数对齐操作。 2. **并行存储器结构** - 双端口存储器和多模块交叉存储器通过时间重叠和空间重叠技术实现了并行访问,提高了数据吞吐量。 3...

    北理工《计算机应用基础》在线作业-0004701.docx

    14. **段落对齐**:PowerPoint2003支持左对齐、居中、右对齐、两端对齐和分散对齐等多种对齐方式。 15. **多媒体技术应用**:多媒体技术广泛应用于娱乐、商业、信息和教育领域。 16. **裸机概念**:“裸机”指的是...

Global site tag (gtag.js) - Google Analytics