(C语言)内存地址对齐及大小端

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

(C语言)内存地址对齐及大小端我们常常看到“alignment”,“endian”之类的字眼,但很少有C语言教材提到这些概念。实际上它们是与处理器与内存接口,编译器类型密切相关的。

考虑这样一个例子:两个异构的CPU进行通信,定义了这样一个结果来传递消息:struct Message

{

short opcode;

char subfield;

long message_length;

char version;

short destination_processor;

}message;

用这样一个结构来传递消息貌似非常方便,但也引发了这样一个问题:若这两种不同的CPU对该结构的定义不一样,两者就会对消息有不同的理解,有可能导致二义性。

会引发二义性的有这两个方面:

1.内存地址对齐;

2.大小端定义;

本文先介绍内存地址对齐和大小端的概念,再回头来看这个例子就豁然开朗了。

1、内存地址对齐

“内存地址对齐”洋名叫做“Byte Alignment”。

大部分16位和32位的CPU不允许将字或者长字存储到内存中的任意地址。比如Motorola 68000不允许将16位的字存储到奇数地址中,将一个16位的字写到奇数地址将引发异常。

实际上,对于C中的字节组织,有这样的对齐规则:

单个字节(char型)能对齐到任意地址;

2字节(short型)以2字节边界对齐;

4字节(int或long型)以4字节边界对齐;

不同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可就麻烦了。

2、字节的“端”(Byte Endian)

是指字节在内存中的组织顺序(或方式),所以也称它为“Byte Ordering”。

对于数据中跨越多个字节的对象,我们必须为它建立这样的约定:

(1)它的地址是多少?

(2)它的字节在内存中是如何组织的?

针对第一个问题,有这样的解释:

对于跨越多个字节的对象,一般它所占的字节都是连续的,它的地址等于它所占字节最低地址(链表可能是个例外,但链表的地址可看作链表头的地址)。

比如:int x,它的地址为0x100。那么它占据了内存中的0x100,0x101,0x102,0x103这四个字节。

int x

上面只是内存字节组织的一种情况:多字节对象在内存中的组织一般有两种约定,考虑一个W位的整数,它的各位表达如下:

[X w-1, X w-2, ... , X1, X0]

它的MSB(Most Significant Byte,最高有效字节)为[X w-1, X w-2, ... X w-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中的数据类型都是从内存的低地址向高地址扩展,取址运算“&”

相关文档
最新文档