确认主机是大端机还是小端机

何为大端、小端

        大端还是小端不好理解和记忆是因为当初翻译是有问题的。翻译成“高尾端”和“低尾端”会更好理解。

        如果把一个数看成一个字符串,比如11223344看成"11223344",末尾是个'\0','11'到'44'各占用一个存储单元,那么它的尾端很显然是44,如果44放在后面(按照数组的表示,数组开头地址最小,后面的地址可以用首地址+i来表示,后面的地址高于或者大于前面的地址)则表示高尾端;如果44放在了前面,则表示低尾端。如下图:

         如果用c语言里面的一个int a =1,来判断是大尾端还是小尾端,则我们要思考,如果int占用4个字节,则1的表示应该是0x00 00 00 01,那么这4个字节怎么存放呢?

          按照前面所说的,如果放到内存中00 00 00 01 则为高尾端,因为01处于0x 00 00 00 01的低位,但是内存中00 00 00 01的01确实高位,低位数据存放在高端内存,这就是高尾端了。

        很显然大端模式符合人的阅读习惯,但是实际上以数组为例,从左往右是地址增大的,而从数值的二进制来看,0x在左,则0x开头表示的十六进制的数字确实从左往右是从高位到低位的,高位在左,低位在右。所以这种适合人阅读的方式是大端模式。

  • 大端模式的字节序:先存高字节(高字节在低地址);
  • 位序:先存高位(高位在低地址);

也就是与我们平时的书写习惯是一致的。例如:0x12345678在大端模式的存储为(左低右高):

0001 0010 0011 0100 0101 0110 0111 1000

0位--4----8---12---16----20---24---28--31位 这个是内存中的样子,却不是CPU处理时的样子

        操作:大端模式只是存储方式,实际的运算还是由CPU负责。而CPU的运算指令都是逻辑运算,并不涉及存储方式。所以左移仍然是*2,右移也仍然是/2,bv&=~(1<<1)仍然是将第二位为0;

但是需要注意,在芯片手册上描述的寄存器也是存储顺序(不是数值顺序或逻辑位序),与位操作的位序不同。

        对应地,小端模式就是不适合人阅读的,比如0x00 00 00 01存到内存中后为01 00 00 00,则为小端。对于0x12345678,使用小端表示,就是:

小端:
0x78 0x56 0x34 0x12

  • 小端模式的字节序:先存低字节(低字节在低地址);
  • 位序:先存高位(高位在低地址,这个与大端一致);

0111 1000 0101 0110 0011 0100 0001 0010

0位--4----8---12---16----20---24---28--31位

 操作:小端模式只是存储方式,实际的运算还是由CPU负责。而CPU的运算指令都是逻辑运算,并不涉及存储方式。所以左移仍然是*2,右移也仍然是/2,bv&=~(1<<1)仍然是将第二位为0;

之所以研究这个,主要是因为我要在内核比较2个float类型的大小,而内核是不支持float类型的,所以核外的float,我们在内核中使用uint32_t来表示,这种方式只能使用memcpy()进行赋值,而不能使用类型强制转换等方式,否则会按照glibc中的强转的方式删掉小数点后面的内容。c语言中,内存中的float表示方式参见我的另一篇博客,基本是按照1.x*2^y*sign方式来表示的,而这里面的x和y以及sign都对应uint32_t中的某些位。比如0x12345678这是一个32位的float表示,最高位就是最左边的0001中的0,表示正数,后面的8位表示y+127的值,最后面的23位表示x。这里面有一个特殊情况,就是0.0,它的表示为32位0.

        因为这个32位的float是存储在内存中的,我们只是借助uint32_t这种形式将它保存下来,但是计算的时候却不能真的把它当成一个int来进行位运算。  以float类型的数字float a =1.0为例,它的2进制表示为0x3F 80 00 00,它在内存中存储为00 00 80 3F,也就是小端模式。从存储的角度来说,我们清楚,它的符号位应该就是24位,也就是0x3F的高位,即0011 1111的高位,为0。但是如果我们使用int b =0; memcpy(&b,&a,4);printf("%u",b>>7 &0x01);来打印标志位,则会出错。 将printf改成printf("%u",b>>31 &0x01);则是对的。也就是说>>和<<这种运算是CPU操作的,它与存储成什么格式无关,在CPU将操作数从内存中取走后,在CPU寄存器中表示出来都是一样的,也就是0x3F 80 00 00,也就是最高字节为3F,最低字节为00,最高字节的最高位0011中的第一个0位符号位。

        同样的,为了获取阶数,则使用b>>23 &0x0FF即可得到阶数+127后的值。

        结论:位运算和大端小端没关系!

使用C语言判断是大小端

        使用联合体
        最简单的方法,使用联合体。联合体的成员共用同一地址空间。在联合体中定义两种不同大小的类型。例如一个int类型和char类型。

在这里插入图片描述

        定义一个int类型的变量i,初始化为1。再定义一个char类型的变量c。i和c共用同一块空间,而且此时的c占用的地址一直都是低地址,如果是小端,i的低位就存储在低地址,这是c的值也会变为1;如果是大端,i的低位就存储在高地址,此时的c的值还是0。通过最后c的值是否为1来判断是大端还是小端

int main()
{
    union
    {
        int i;
        char c;
    }un;
    un.i = 1;
    if (un.c)
        cout << "小端" << endl;
    else
        cout << "大端" << endl;
    return 0;
}

类型强转

        分别定义一个int类型的变量和char类型的变量。int类型变量i赋值为1,char类型的变量c的值是通过i类型强转得来的。先取i的地址&i,再通过(char*)&i将i的地址转换为char*类型的指针,最后通过解引用(*(char*)&i)得到c的值

int main()
{
    int i = 1;
    char c = (*(char*)&i);
    if (c)
        cout << "小端" << endl;
    else
        cout << "大端" << endl;
    return 0;
}


#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)

参考链接:https://blog.csdn.net/qq_44443986/article/details/115523311

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值