计算机大小段对齐,大小端以及内存对齐问题

【用函数判断系统是Big Endian还是Little Endian】

//如果字节序为big-endian,返回true;

//反之为 little-endian,返回false

bool IsBig_Endian()

{

unsigned

short test = 0x1234;

if(*(

(unsigned char*) &test ) == 0x12)

return TRUE;

else

return FALSE;

}//IsBig_Endian()

或者

intcheckCPUendian()//返回1,为小端;反之,为大端;

{

union

{

unsigned inta;

unsigned charb;

}c;

c.a = 1;

return1 == c.b;

}

大小端的分度值是

byte,即每一个byte都是按照正常顺序,但是byte组装成一个int 或者是

long等时每个byte的摆放位置不同

二、内存对齐问题

1.结构体中的数据对齐

定义一个结构体类型的时候,并没有分配实际的地址空间(定义结构体变量在编译链接过程中给了实际地址,而结构体指针要显式的用malloc等形式申请栈内地址),通俗

的说就是定义类型是没有实际分配空间的,而定义变量的时候会分配空间。

结构体的大小不是结构体元素单纯相加就行的

因为我们现在主流的计算机使用的都是32Bit字长的CPU,对这类型的CPU取4个字节的数要比取一个字节要高效,也更方便。所以在结构体中每个成员的首地址都是4的整数倍的话,取数据元素是就会相对更高效,这就是内存对齐的由来。

每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma

pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的“对齐系数”。

在C语言中,可以定义结构体类型,将多个相关的变量包装成为一个整体使用。在结构体中的变量,可以是相同、部分相同,或完全不同的数据类型。在C语言中,结构体不能包含。在中,对象具有状态(属性)和行为,状态保存在成员变量中,行为通过成员方法(函数)来实现。C语言中的结构体只能描述一个对象的状态,不能描述一个对象的行为。在C++中,考虑到C语言到C++语言过渡的连续性,对结构体进行了扩展,C++的结构体可以包含函数,这样,C++的结构体也具有的功能,与class不同的是,结构体包含的函数默认为public,而不是private。

类与结构体在C++中只有两点区别,除此这外无任何区别。

(1)class中默认的成员访问权限是private的,而struct中则是public的。

(2)从class继承默认是private继承,而从struct继承默认是public继承。

基本概念

(1)数据类型自身的对齐值:任何基本数据类型T的自身对齐值就是T的大小,即sizeof(T)。char型、short型、int型、long型、float型、double型的自身对齐值分别为1、2、4、4、4、8,单位为字节。

(2)结构体或者类的自身对齐值:其成员变量自身对齐值中最大的那个值。

(3)指定对齐值:使用#pragma

pack(value)时指定的对齐值。

(4)数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中小的那个值。

(5)圆整值:编译器对结构体进行圆整(即,在结构体最末填充一定的字节)。圆整值大小为结构体的自身对齐值与#pragma

pack(value)中指定的对齐值中较小的那个值。

实例分析

structB

{

charb;

inta;

shortc;

};

假设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。

#pragma pack (2)

structC

{

charb;

inta;

shortc;

};

#pragma 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.

下面的C代码在VC++6.0下的运行结果是什么?请详细说明原因。

#include

int main()

{

int a[5]={1,2,3,4,5};

int *ptr1=(int *)(&a+1);

int *ptr2=(int *)((int )a+1);

printf("%x,%x",ptr1[-1],*ptr2);

return 0;

}

int main() { int

a[5]={1,2,3,4,5}; int *ptr1=(int *)(&a+1);

//

a本身就为一个数组的地址,其值=&a[0],即数组第一个元素的地址,对a再取址

// &a = a,似乎没什么不同,其实这里发生了对齐变更,a+1的对齐是以int为界的

//

而&a+1是以整个数组为单位的,即5个int=20字节,这是关键!!!

//

那么&a+1实际是跨越了整个数组,可得ptr1存储了数组a最后一个元素5紧跟着的一个int的地址

int *ptr2=(int *)((int

)a+1);

//

同理,(int)a先将其值(地址)转为整型值,再加一成为一个新的整数

// (int

*)将这个值转换为一个指向int型变量的指针(地址)

//

ptr2即存储了这个地址和指向的变量的类型

printf("%x,%x",ptr1[-1],*ptr2);

//

先解释一下a[-1],即将数组向前偏差一个类型大小的距离

//

数组a的内存布局(16进制):

小端模式下是:(假设内存分配的起始地址是:0ff00001)

0ff00001

01

0ff00002

00

0ff00003

00

0ff00004

00

0ff00005

02

0ff00006

00

0ff00007

00

0ff00008

00

0ff00009

03

0ff0000a

00

0ff0000b

00

0ff0000c

00

0ff0000d

04

0ff0000e

00

0ff0000f

00

0ff00010

00

0ff00011

05

0ff00012

00

0ff00013

00

0ff00014

00

// ptr1[-1]

//

数组a最后一个元素(5)紧跟着一个int的前面一个元素当然就是5了。

// *ptr2

//

ptr2的值为a保存的地址值的绝对值+1,这时的对齐是1byte。

//

那么在little-enddian架构下显示出来为02000000,低位在高地址。

return

0;

}

第一点:

C/C++ code

int *ptr1=(int

*)(&a+1);

首先,&a获得的是数组的地址,这样增长的单元则成了sizeof(a)=20

这样ptr1指向a[5](当然,数组的下标是从0开始,因此正常只有0-4有效.)

ptr1[-1]相当于ptrl-1,也就相当于a[4],因此输出为5.

第二点:

C/C++ code

int *ptr2=(int *)((int

)a+1);

(int )a==>能过强制类型转换,此时的(int)a只为一个般的int型值.其值为a[0]的地址.

(int)a+1==>其值为a[0]的地址+1

int *ptr2=(int *)((int )a+1);==>此时指向的地址为00 00 00

02

相当于数值02 00 00 00(十进制为:33554432)

最后由于

printf("%x",*ptr2);==>要求输出为16进制的格式,当然就是2000000

*/

/*

mark 4:

在《C和指针》165页第8章数组的总结中有这样一段话:

在绝大多数表达式中,数组名的值是指向数组第1个元素的指针。

这个规则只有两个例外。sizeof返回整个数组所占用的字节而不是一个指针所占用的字节。

单目操作符&返回一个指向数组的指针,而不是一个指向数组第1个元素的指针的指针。

int *ptr1=(int *)(&a+1);

//a是指向数组的指针

int *ptr2=(int *)((int )a+1);

//a是指向数组第1个元素的指针

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值