详解 CPU 大段存储与小端存储


关于计算机界的大小端问题有著名的CPU两大派系,PowerPC系列采用大端(big endian)的方式存储数据,而X86系列则采用小端(little endian)方式存储数据。很显然如果你的程序只运行在PowerPC系列的CPU上,你完全可以不管什么是little endian,但是如果你PowerPC上的程序要和X86上的程序打交道,那么你就必须进行转换才能相互识别,

我们又该怎样去理解它呢?

  1. 我们应该知道数据在计算机中是以二进制形式存储的,1个字节有8位,每1位的值只能是0或者1;
  2. 我们还知道一个4位的二进制数和1位的十六进制数是1-1对应的,那么1个字节就可以拆分成2位的十六进制数;
  3. 我们还应该知道像0x1234abcd这样一个16进制数在内存中它是以4个字节存储的,既然这个数据在内存中跨越了多个字节,那么它肯定是有顺序的,我们叫它字节序。【一般操作系统都是小端,而通讯协议是大端的】
  4. 这里的端指的是数据的尾端,大指的是高内存地址,小指的是低内存地址

一般人的书写习惯:

  • 数据左边的为高位,右边的为低位,
  • 地址则相反:地址左边的为低位,右边的为高位

例如: 数据16位数据:0x12345678,12为数据的高位,78位数据的低位

大端模式:

数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。

例如:四字节的数据:0x12345678,
地址:低->高,低地址存放高位,高地址存放低位

存储为:
在这里插入图片描述

小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。

例如:四字节的数据0x12345678,
地址:低->高,高地址存放高位,低地址存放低位

存储为:
在这里插入图片描述

只需记住与我们阅读习惯一致的为大端模式。

  • 大端:把高位存储到低地址里面,把低位存储到高地址里面
  • 小端:把低位存储到低地址里面,把高位存储到高地址里面

CPU大小端判断

在这里插入图片描述
在这里插入图片描述

判断计算机是大端还是小端存储

  1. 字符指针判断

int 类型占4个字节,而 char 类型占一个字节的,如果我们把 &int 强转为 char* 类型的指针,只会保存一个字节的数据,那么我们只需要判断 char 存储的数据和 int 存储第一个字节的数据是否是一致即可判断。

代码实现:

#include <stdio.h>
#include <stdlib.h>
// 小端模式:LITTLE; 大端模式:BIG
int main()
{
    int a = 1;
    char pc = *(char*)(&a);
    if (pc == 1)
        printf("LITTLE\n");
    else
        printf("BIG\n");
    return 0;
}
  1. 联合体判断

由于联合体所有数据共享一块地址空间,存放数据的所有成员都是从低地址开始存放,所以我们可以在联合体内定义一个 int 和一个 char 类型变量。然后在外部实例化的时候创建 int 变量,用 char 变量调用,相当于隐式类型转化。如果结果为1,则低字节存放在低地址,既是小端机器,反之大端机器。

union A{
    char c;
    int i;
}s;

int main(){
    s.i = 0x12345678;
    if(s.c == 0x78)
        printf("小端:Little\n");
    else
        printf("大端:Big\n");
    return 0;
}
  • 为什么不能直接通过强制类型转换?

直接将 int 类型转换成 char 类型,得到的是 int 类型的低8个 bit 位。(至于这低8个 bit 位,如果存储在高位就拿高位,存储在低位就拿低位)。就跟大端小端没有关系了,大端小端是存进内存的方式。

int main(){
    int i = 0x12345678;
    char c = i;	// 方法不合理
    if(c == 0x78) 
        printf("小端:Little\n");
    else 
        printf("大端:Big\n");
    return 0;
}
大端与小端的优势

二者无所谓优势,无所谓劣势,各自优势便是对方劣势

  • 大端模式:符号位的判定固定为第一个字节,容易判断正负。
  • 小端模式:强制转换数据不需要调整字节内容,1、2、4字节的存储方式一样

数组在大端小端情况下的存储:

以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,

  • 我们可以用unsigned char buf[4]来表示value:
  • Big-Endian: 低地址存放高位,如下:
高地址
   ---------------
   buf[3] (0x78) -- 低位
   buf[2] (0x56)
   buf[1] (0x34)
   buf[0] (0x12) -- 高位
   ---------------
低地址

Little-Endian: 低地址存放低位,如下:

高地址
       ---------------
       buf[3] (0x12) -- 高位
       buf[2] (0x34)
       buf[1] (0x56)
       buf[0] (0x78) -- 低位
       --------------
低地址
知识点例题:
  1. temp=0X12345678;
低地址---------->高地址【大端模式】:
0X12|0X34|0X56|0X78|
低地址---------->高地址【小端模式】:
0X78|0X56|0X34|0X12|
  1. 假设变量x类型为int,位于地址0x100处,它的十六进制为0x01234567,地址范围为0x100~0x103字节,其内部排列顺序依赖于机器的类型。

大端法从首位开始是:0x100: 01, 0x101: 23, 0x102: 45, 0x103: 67
小端法从首位开始是:0x100: 67, 0x101: 45, 0x102: 23, 0x103: 01

  1. 将int型数组强制转换为char*,再求strlen。(涉及大小端~~)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(){
    int a[2000];
    char *p = (char*)a;
    for( int i = 0; i < 2000; i++){
        a[i] = -1 - i;
    }
    printf("%d\n", strlen(p));
    return 0;
}

正确答案: 1020 1020 1020(代码运行出来也是这个)

答案解析:

首先要明白负数在内存中的存储方式,还要知道int和char各占几位(都是最基本的啦)。这里很容易知道:

  • a[0] = -1 内存中应当是:11111111 11111111 11111111 11111111

  • a[1] = -2 内存中应当是:11111111 11111111 11111111 11111110

  • a[2] = -3 内存中应当是:11111111 11111111 11111111 11111101

  • ……

  • a[255] = -256 内存中应当是: 11111111 11111111 11111111 00000000

程序计算strlen( p )的时候遇到8个0就停止了,所以是255 * 4 + 3 = 1023.

为什么结果是1020呢?(PS:我的cpu是Intel的,Intel的cpu一般都是小端存储)这就涉及到内存的存储问题了。

众所周知,内存存储分为大端小端,大端就是我们人类理解的这样,将高位写在前面,将地位写在后面,小端存储则正好相反,所以a[255] = -256 在内存中的表示形式是: 00000000 11111111 11111111 11111111,这就是为什么答案是1020。当然了不同的机器会有不同,如果笔试的时候注明一下,应该效果会更好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值