大端小端区别、Union和Struct的内存分配

今天石硕问了我一个问题:

#include <stdio.h>
union TT{
int a;
char ch[2];
}t;

int main()
{
t.a = 0x1234;
printf(“%x\n”, t.ch[0]);
printf(“%x\n”, t.ch[1]);
return 0;
}

石硕:“这个程序你猜输出啥?”

 

我打开计算器,0x1234转换为二级制是0001 0010 0011 0100,因为int是4个字节,而char是1个字节,所以把0x1234拆成 00010010 和 00110100 ,分别是 0x12 和 0x34,于是我脱口而出:

“输出12和34”

 

结果石硕说不对,说是 34 和 12。

后来我用程序验证了一下,确实是34和12,

但是,为什么是34和12呢?

 

Google发现union在储存数据的时候竟然还有两种方式,一种是大端存储,另一种是小端存储,下面这篇文章值得一读:

(转载自cnblogs

嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。也就是说Big-endian模式符合人的习惯,而Little-endian更加方便计算机操作。

例如,16bit宽的数0x1234在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址 0x4000 0x4001
存放内容 0x34 0x12

而在Big-endian模式CPU内存中的存放方式则为:
内存地址 0x4000 0x4001
存放内容 0x12 0x34

32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
内存地址 0x4000 0x4001 0x4002 0x4003
存放内容 0x78 0x56 0x34 0x12

而在Big-endian模式CPU内存中的存放方式则为:
内存地址 0x4000 0x4001 0x4002 0x4003
存放内容 0x12 0x34 0x56 0x78

若判断处理器是Big还是Little模式,有两种方法。

1、

int i=1;
char *p=(char *)&i;
if(*p==1)
printf(“1”);
else
printf(“2”);

大小端存储问题,如果小端方式(i占至少两个字节的长度)则i所分配的内存最小地址那个字节中就存着1,其他字节是0.大端的话则1在i的最高地址字节处存放,char是一个字节,所以强制将char型量p指向i则p指向的一定是i的最低地址,那么就可以判断p中的值是不是1来确定是不是小端

2、

int checkCPU( )

{

{

union w

{

int a;

char b;

} c;

c.a = 1;

return(c.b ==1);

}

}

这个解法涉及到Union的内存分配模式。

Union的大小为其内部所有变量的最大值,并且按照类型最大值的整数倍进行内存对齐。

例如:

typedef Union
{
char c[10];
char cc1;
}u11;

首先按照char c[10]分配10个字节,然后按照char的1个字节对齐,最终sizeof(u11)=10;

typedef union
{
char c[10];
int i;
}u22;

首先按照char c[10]分配10个字节,然后按照int的4个字节对齐,最终sizeof(u22)=12;

typedef union
{
char c[10];
double d;
}u33;

首先按照char c[10]分配10个字节,然后按照double的4个自己对齐,最终sizeof=16;

union U1 {
char c;
int i;
double d;
} ;

按照double的8个字节分配,最终为8;
union U2 {
char c;
} ;

按照char c的一个字节分配,最终sizeof为1;

union U3 {
char c;
int i;
// double d;
} ;

按照int的4个字节分配,最终sizeof为4;

因此,举例中union分配的内存按照int分配4个字节,如果是小端模式则存放的方式为

地址A
————————————
|A |A+1 |A+2 |A+3 | int a;
|0x01 |0x00 |0x00 |0x00 |
————————————-
|A |char b;
|    |
———

如果是大端如何存储c.a的呢?

地址A

——————————————
|A |A+1 |A+2 |A+3 |int a;
|0x00 |0x00 |0x00 |0x01 |
——————————————
|A |char b;
|    |
———

因此我们就可以通过查看char b==1?来判断大小端了。

顺便说明一下struct的内存分配方式。

struct的内存大小为每个数据内存的加和,首先按照最大的数据类型进行单个分配,如果前一个数据占用不了所有的内存,而剩下的内存可以放下下一个数据,则第二个数据不另外分配内存,否则重新分配一个最大类型的内存单元。

struct{

char c;

double d;

};16

struct{

char c;

char c1;

double d;

};16

struct{

char c;

double d;

char c2;

};24

发表评论

电子邮件地址不会被公开。 必填项已用*标注