先考虑一个程序:

在x86 系统下,输出的值为多少?

#include <stdio.h>

intmain()

{

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;

}

对于这个程序我们先看看什么是高端模式和低端模式:

1.15 union 关键字

union 关键字的用法与struct 的用法非常类似。

union 维护足够的空间来置放多个数据成员中的“一种”,而不是为每一个数据成员配置

空间,在union 中所有的数据成员共用一个空间,同一时间只能储存其中一个数据成员,所

有的数据成员具有相同的起始地址。例子如下:

union StateMachine

{

char character;

int number;

char *str;

double exp;

};

一个union 只配置一个足够大的空间以来容纳最大长度的数据成员,以上例而言,最大

长度是double 型态,所以StateMachine 的空间大小就是double 数据类型的大小。

在C++里,union 的成员默认属性页为public。union 主要用来压缩空间。如果一些数据

不可能在同一时间同时被用到,则可以使用union。

1.15.1,大小端模式对union 类型数据的影响

下面再看一个例子:

union

{

int i;

char a[2];

}*p, u;

p =&u;

p->a[0] = 0x39;

p->a[1] = 0x38;

p.i 的值应该为多少呢?

这里需要考虑存储模式:大端模式和小端模式。

大端模式(Big_endian):字数据的高字节存储在低地址中,而字数据的低字节则存放

在高地址中。

小端模式(Little_endian):字数据的高字节存储在高地址中,而字数据的低字节则存放

在低地址中。

union 型数据所占的空间等于其最大的成员所占的空间。对union 型的成员的存取都是

相对于该联合体基地址的偏移量为0 处开始,也就是联合体的访问不论对哪个变量的存取都

是从union 的首地址位置开始。如此一解释,上面的问题是否已经有了答案呢?

1.15.2,如何用程序确认当前系统的存储模式?

上述问题似乎还比较简单,那来个有技术含量的:请写一个C 函数,若处理器是

Big_endian 的,则返回0;若是Little_endian 的,则返回1。

先分析一下,按照上面关于大小端模式的定义,假设int 类型变量i 被初始化为1。

以大端模式存储,其内存布局如下图:

以小端模式存储,其内存布局如下图:

变量i 占4 个字节,但只有一个字节的值为1,另外三个字节的值都为0。如果取出低

地址上的值为0,毫无疑问,这是大端模式;如果取出低地址上的值为1,毫无疑问,这是

小端模式。既然如此,我们完全可以利用union 类型数据的特点:所有成员的起始地址一致。

到现在,应该知道怎么写了吧?参考答案如下:

int checkSystem( )

{

union check

{

int i;

char ch;

} c;

c.i = 1;

return (c.ch ==1);

}

现在你可以用这个函数来测试你当前系统的存储模式了。当然你也可以不用函数而直

接去查看内存来确定当前系统的存储模式。如下图:
int *ptr1=(int*)(&a+1)
int *ptr2=(int*)((int)a+1)
***********************************************************************************************
*    a[0]    *    a[1]    *    a[2]    *    a[3]    *
************************************************************************************************
*      |    |      |    *      |    |       |    *      |    |      |    *     |       |     |    *   
*      |    |      |    *      |    |       |    *      |    |      |    *     |       |     |    *
*************************************************************************************************

图中0x01 的值存在低地址上,说明当前系统为小端模式。

不过要说明的一点是,某些系统可能同时支持这两种存储模式,你可以用硬件跳线或

在编译器的选项中设置其存储模式。



Ok,现在我们再回过头来看开始的那个程序:

intmain()

{

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

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

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

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

return 0;

}

ptr1:将&a+1 的值强制转换成int*类型,赋值给int* 类型的变量ptr,ptr1 肯定指到数

组a 的下一个int 类型数据了。ptr1[-1]被解析成*(ptr1-1),即ptr1 往后退4 个byte。所以其

值为0x4。

ptr2:按照上面的讲解,(int)a+1 的值是元素a[0]的第二个字节的地址。然后把这个地址

强制转换成int*类型的值赋给ptr2,也就是说*ptr2 的值应该为元素a[0]的第二个字节开始的

连续4 个byte 的内容。

其内存布局如下图:


好,问题就来了,这连续4 个byte 里到底存了什么东西呢?也就是说元素a[0],a[1]里面

的值到底怎么存储的。这就涉及到系统的大小端模式了,如果懂汇编的话,这根本就不是问

题。既然不知道当前系统是什么模式,那就得想办法测试。大小端模式与测试的方法在前面讲解union 关键字时已经详细讨论过了,请翻到彼处参看。我们可以用下面这个函数来测试当前系统的模式。

int checkSystem( )

{

union check

{

int i;

char ch;

} c;

c.i = 1;

return (c.ch ==1);

}

如果当前系统为大端模式这个函数返回0;如果为小端模式,函数返回1。

也就是说如果此函数的返回值为1 的话,*ptr2 的值为0x2000000。

如果此函数的返回值为0 的话,*ptr2 的值为0x100。 阅读全文
类别:c语言学习 查看评论
posted on 2010-10-13 02:33  sinbad_li  阅读(911)  评论(0编辑  收藏  举报