C语言之大小端问题

1:大小端名字的由来及发展
  (1)在乔纳森·斯威夫特的著名讽刺小说《格列夫游记》中,小人国内部分裂成Big-endian和Little-endian两派,区别在于一派要求从鸡蛋的大头把鸡蛋打破,另一派要求从鸡蛋的小头把鸡蛋打破。斯威夫特借以讽刺英国的政党之争,在计算机工业中指数据储存顺序的分歧。
    (2)后来计算机通信发展起来后,遇到一个问题就是:在串口等串行通信中,一次只能发送1个字节。这时候我要发送一个int类型的数就遇到一个问题。int类型有4个字节,我是按照:byte0 byte1 byte2 byte3这样的顺序发送,还是按照byte3 byte2 byte1 byte0这样的顺序发送。规则就是发送方和接收方必须按照同样的字节顺序来通信,否则就会出现错误。这就叫通信系统中的大小端模式。这是大小端这个词和计算机挂钩的最早问题。
  (3)现在我们讲的这个大小端模式,更多是指计算机存储系统的大小端。在计算机内存/硬盘/Nnad中。因为存储系统是32位的,但是数据仍然是按照字节为单位的。于是乎一个32位的二进制在内存中存储时有2种分布方式:高字节对应高地址(大端模式)、高字节对应低地址(小端模式)
  (4)现实的情况就是:有些CPU公司用大端(譬如C51单片机);有些CPU用小端(譬如ARM)。(大部分是用小端模式,大端模式的不算多)。于是乎我们写代码时,当不知道当前环境是用大端模式还是小端模式时就需要用代码来检测当前系统的大小端。
2:测试机器大小端模式的测试代码
2.1:使用union来测试机器的大小端
#include<stdio.h>
union myunion
{
    int a;
    char b;
};
 
int little_or_big(void)
{
    myunion u1;
    u1.a = 1;
    return u1.b;
}
int main(void)
{
    int i = little_or_big();
    if(1==i)
    {
        printf("小端模式");
    }
    else
    {
        printf("大端模式");
    }
    return 0;
}


分析:
  首先共用体元素a和b在访问时候都是从低地址开始访问的,u1.a = 1在内存中的存放有两种可能(内存地址从左到右递减),小端模式为 00  00 00 01;大端模式为: 01 00 00 00 ,而共用体u1中的b是char类似,所以我们用u1.b去访问时只能读取到最低地址的值(按char去解析时只会读取一个字节),所以,如果读出u1.b的值为1则说明当前机器是小端模式,读出u1.b的值为0,则说明当前机器是大端模式(这种测试方法要记住,面试时候经常考)
2.2:指针方式来测试大小端
123456 int little_or_big2(void)
{
    int a = 1;
    char  b = *((char *) &a);
    return b;
}


分析:
  首选定义变量a= 1,然后将a的指针强制类型转换成char *接着去解应用这个指针,并赋值给b,然后根据b的返回值来确定大小端。其实分析可以发现其本质都是一样的,都是先给一个内存里面存一个char 类型的1,然后使得另一个char 类型的变量b去读取这个内存的值,然后根据读取的值来判断大小端。
3:看似可行但实际不行的大小端测试方法
注:测试方法将测试代码分别放在kile4.0(大端)和gcc(小端)下面去运行,看运行后的结果。

3.1:位运算
理论分析:
  现在将0x1(0001)和0xf(1111)相与假设机器是大端模式,那么相与之后得到的结果是0001;假设机器是小端模式那么相与之后得到的结果是1000,
实际测试:
  理论可行但实际上不行的,原因是位与运算是编译器提供的运算,这个运算是高于内存层次的(或者说&运算在二进制层次具有可移植性,也就是说&的时候一定是高字节&高字节,低字节&低字节,和二进制存储无关)
3.2:移位
理论分析:
  同上假设的大端模式,那么存储的方式是0001,右移1位则把 1 移出去了,所以得到的结果就是0x0;假设是小端模式,那么存储方式是1000,右移1位,则结果是0100
实际测试:
  实际不行,原因同上因为C语言对运算符的级别是高于二进制层次的。右移运算永远是将低字节移除,而和二进制存储时这个低字节在高位还是低位无关的。
3.3:强制类型转换
这里就不再次分析, 实际测试时不行的,原因同上

相关推荐