第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

大小端序之爭(zhēng)

1. 前言

在 C 語(yǔ)言中,內(nèi)置的基本類型有 char、short、int、long、double 等,對(duì)于整型類型來(lái)說(shuō),還區(qū)分 signed 和 unsigned。在 Java 語(yǔ)言中,內(nèi)置類型也有 char、short、int、long、double 等,只不過(guò) Java 沒(méi)有 unsigned 類型。char 類型在 C 語(yǔ)言是占用 1 字節(jié)長(zhǎng)度,而在 Java 語(yǔ)言中占用 2 字節(jié)長(zhǎng)度。而其他類型不管在 C 語(yǔ)言中,還是在 Java 語(yǔ)言中,都是占用多個(gè)字節(jié)長(zhǎng)度。

我們知道 CPU 訪問(wèn)內(nèi)存是通過(guò)地址總線完成的,一塊連續(xù)的內(nèi)存空間是經(jīng)過(guò)編址的,每一個(gè)地址編號(hào)對(duì)應(yīng) 1 字節(jié)長(zhǎng)度的內(nèi)存空間,地址空間是從低地址到高地址增長(zhǎng)的。如果要在內(nèi)存中存儲(chǔ) 0xAABBCCDD 這樣一個(gè)長(zhǎng)度為 4 字節(jié)的十六進(jìn)制整數(shù),需要 4 字節(jié)的內(nèi)存空間。內(nèi)存空間示意如下:

     100  101  102  103  -------> 內(nèi)存地址由低到高增長(zhǎng)的方向
   +----+----+----+----+
   |    |    |    |    |
   +----+----+----+----+

那么 0xAA 是存儲(chǔ)在地址編號(hào)為 100 的空間呢?還是存儲(chǔ)在地址編號(hào)為 103 的空間呢?這就是本節(jié)要討論的字節(jié)序的問(wèn)題。

字節(jié)序大端序(Big-Endian)和小端序(Little-Endian)之分。對(duì)于前面提到的十六進(jìn)制整數(shù) 0xAABBCCDD 來(lái)說(shuō),如果按照大端序在內(nèi)存中存儲(chǔ),那么從低地址到高地址的存儲(chǔ)順序依次是 0xAA、0xBB、0xCC、0xDD;如果按照小端序在內(nèi)存中存儲(chǔ),那么從低地址到高地址的存儲(chǔ)順序依次是 0xDD、0xCC、0xBB、0xAA。

文字描述還是有些抽象,我們通過(guò)一張圖來(lái)直觀感受一下內(nèi)存字節(jié)序。

2. 計(jì)算機(jī)的字節(jié)序

在操作系統(tǒng)課程中,我們學(xué)過(guò)現(xiàn)代操作系統(tǒng)的內(nèi)存管理機(jī)制是虛擬內(nèi)存管理機(jī)制,對(duì)于 32 位系統(tǒng)來(lái)說(shuō),每一個(gè)進(jìn)程都有 4G( 2^32)字節(jié)長(zhǎng)度的虛擬地址空間,也叫線性地址空間。我們先看一張圖。

圖片描述

圖中用內(nèi)存地址 0x90000001 ~ 0x9000000A 表示了 10 字節(jié)的內(nèi)存地址空間,每一個(gè)地址代表 1 字節(jié)的內(nèi)存。當(dāng)一個(gè)多字節(jié)整數(shù)存儲(chǔ)在內(nèi)存中時(shí),會(huì)涉及到字節(jié)序的問(wèn)題。

我們首先搞清楚兩個(gè)術(shù)語(yǔ):最高有效位最低有效位。我們知道,人類習(xí)慣的閱讀順序是從左到右,對(duì)于一個(gè)多位數(shù)字來(lái)說(shuō),經(jīng)常把它的最左邊叫做高位,把它的最右邊叫做低位。而在計(jì)算機(jī)中,對(duì)于一個(gè)多位數(shù)字的描述,也有類似的專業(yè)術(shù)語(yǔ),把左邊的最高位叫做最高有效位(MSB,most significant bit);把右邊最低位叫做最低有效位(LSB,least significant bit)。

下圖展示了在內(nèi)存中存儲(chǔ) 16 進(jìn)制整數(shù) 0xAABBCCDD 的不同方式。圖中用內(nèi)存地址 0x90000000 ~ 0x90000003 表示了長(zhǎng)度為 4 字節(jié)的內(nèi)存地址空間。

圖片描述

如果按照小端序來(lái)存儲(chǔ),0xAABBCCDD 在內(nèi)存中從低地址到高地址的存儲(chǔ)順序是 0xDD、0xCC、0xBB、0xAA,存儲(chǔ)順序和人類習(xí)慣的閱讀順序是相反的。

圖片描述

如果按照大端序來(lái)存儲(chǔ),0xAABBCCDD 在內(nèi)存中從低地址到高地址的存儲(chǔ)順序是 0xAA、0xBB、0xCC、0xDD,存儲(chǔ)順序和人類習(xí)慣的閱讀順序是相同的。可以類比人類的閱讀順序,更容易理解,也便于記憶。

大小端序是由于 CPU 架構(gòu)的不同導(dǎo)致的,在歷史上 IBM System/360 、Motorola 6800 / 6801、SPARC 是大端序;Intel 架構(gòu)、ARM 架構(gòu)是小端序。另外,JAVA 存儲(chǔ)多字節(jié)整數(shù),也是采用大端序。

通過(guò)簡(jiǎn)單的程序,很容易測(cè)試出來(lái)我們當(dāng)前系統(tǒng)所采用的字節(jié)序類型。

3. 通過(guò) C 程序測(cè)試字節(jié)序

通過(guò) C 語(yǔ)言程序來(lái)測(cè)試字節(jié)序非常簡(jiǎn)單,大致思路如下:

  • 定義一個(gè)整形變量,然后將 0xAABBCCDD 賦值給該變量。
  • 按照從低地址到高地址的順序打印此變量的內(nèi)容。
  • 將打印結(jié)果的順序和 0xAABBCCDD 的順序進(jìn)行對(duì)比,觀察二者的變化。

代碼片段如下:

  1 #include <stdio.h>
  2
  3 void check_endian()
  4 {
  5     int n = 0xAABBCCDD;
  6
  7     unsigned char *ptr_n = (unsigned char*)&n;
  8
  9     for (int i=0; i < 4; ++i){
 10         printf("%X\n", *ptr_n++);
 11     }
 12 }

代碼中有兩個(gè)需要注意的地方:

Tips:

  1. 需要將 int 型變量 n 的地址賦值給了 unsigned char 型指針變量,如果是賦值給 char 型變量,那么打印結(jié)果是:
FFFFFFDD
FFFFFFCC
FFFFFFBB
FFFFFFAA

原因是 printf 在打印的時(shí)候會(huì)將 char 提升為 int,0xAA,0xBB 最高位是 1,所以會(huì)當(dāng)做符號(hào)位擴(kuò)展。如果是 unsigned char,會(huì)提升為 unsigned int,符號(hào)位擴(kuò)展是 0。

  1. 打印結(jié)果的時(shí)候用 %x 或者 %X 進(jìn)行格式化輸出。

C 語(yǔ)言程序輸出結(jié)果:

DD
CC
BB
AA

從輸出結(jié)果可以看出我的系統(tǒng)是以小端序來(lái)存儲(chǔ)整數(shù)的。

4. Java ByteOrder

我們知道 Java 是平臺(tái)無(wú)關(guān)的編程語(yǔ)言,它是運(yùn)行在 Java 虛擬機(jī)之上的,而 Java 虛擬機(jī)又是運(yùn)行在 Native 系統(tǒng)上的。那么,如何通過(guò) Java 程序檢測(cè)系統(tǒng)本身的字節(jié)序呢?可以通過(guò) java.nio.ByteOrder 類來(lái)測(cè)試當(dāng)前 Native 系統(tǒng)的字節(jié)序。調(diào)用 ByteOrder 的 nativeOrder 方法,就能返回系統(tǒng)本身的字節(jié)序。另外,ByteOrder 還定義了兩個(gè) ByteOrder 類型的常量常用:

  • ByteOrder.BIG_ENDIAN 表示大端序
  • ByteOrder.LITTLE_ENDIAN 表示小端序

檢測(cè)程序也很簡(jiǎn)單,如下:

public static void testByteOrder(){
    System.out.println("The native byte order: " + ByteOrder.nativeOrder());
}

檢測(cè)結(jié)果如下:

The native byte order: LITTLE_ENDIAN

5. Java ByteBuffer 的字節(jié)序

那么 JVM 作為一部獨(dú)立運(yùn)行的機(jī)器,它的字節(jié)序又是如何呢?通過(guò) Java 程序測(cè)試字節(jié)序的思路和 C 程序的一致,代碼片段如下:

 public static void checkEndian()
 {
     int x = 0xAABBCCDD;

     ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
     buffer.putInt(x);
     byte[] lbytes = buffer.array();
     for (byte b : lbytes){
         System.out.printf("%X\n", b);
     }
 }

關(guān)于 JAVA 程序需要說(shuō)明的是 JAVA 中沒(méi)有指針的概念,所以不能通過(guò)取地址的方式直接打印內(nèi)存的值。需要借助 JAVA 的 ByteBuffer,將 int 型數(shù)值存儲(chǔ)到 ByteBuffer 中,然后將 ByteBuffer 轉(zhuǎn)換成字節(jié)數(shù)組,通過(guò)打印數(shù)組的方式來(lái)達(dá)到我們的目的。引用 ByteBuffer 需要通過(guò)語(yǔ)句 import java.nio.ByteBuffer; 導(dǎo)入ByteBuffer 類。

JAVA 測(cè)試結(jié)果:

AA
BB
CC
DD

從輸出結(jié)果可以看出 ByteBuffer 默認(rèn)是以大端序來(lái)存儲(chǔ)整數(shù)的,因?yàn)?Java 虛擬機(jī)本身采用的就是大端序,ByteBuffer 也要和整個(gè)系統(tǒng)保持一致。當(dāng)然,ByteBuffer 也提供了 ByteBuffer order()ByteBuffer order(ByteOrder bo) 方法,用來(lái)獲取和設(shè)置 ByteBuffer 的字節(jié)序。

另外,像一些多字節(jié) Buffer,如 IntBuffer、LongBuffer,它們的字節(jié)序規(guī)則如下:

  • 如果多字節(jié) Buffer 是通過(guò)數(shù)組(Array)創(chuàng)建的,那么它的字節(jié)序和底層系統(tǒng)的字節(jié)序一致。
  • 如果多字節(jié) Buffer 是通過(guò) ByteBuffer 創(chuàng)建的,那么它的字節(jié)序和 ByteBuffer 的字節(jié)序一致。

測(cè)試程序如下:

    public static void checkByteBuffer(){
        ByteBuffer byteBuffer = ByteBuffer.allocate(Long.BYTES);

        long [] longNumber = new long[]{
          0xAA,0xBB,0xCC,0xDD
        };
        LongBuffer lbAsArray = LongBuffer.wrap(longNumber);
        System.out.println("The byte order for LongBuffer wrap array: " + lbAsArray.order());
        LongBuffer lbAsByteBuffer = byteBuffer.asLongBuffer();
        System.out.println("The byte order for LongBuffer from ByteBuffer: " + lbAsByteBuffer.order());
    }

執(zhí)行結(jié)果:

The byte order for LongBuffer wrap array: LITTLE_ENDIAN
The byte order for LongBuffer from ByteBuffer: BIG_ENDIAN

如果在上面的 checkByteBuffer 方法中,首先將對(duì)象 byteBuffer 的字節(jié)序設(shè)置為 ByteOrder.LITTLE_ENDIAN(通過(guò) ByteBuffer 的 order 方法設(shè)置),然后再創(chuàng)建 lbAsByteBuffer 對(duì)象,那么 lbAsByteBuffer 的字節(jié)序該是什么呢?

6. 網(wǎng)絡(luò)字節(jié)序

前面兩小節(jié)討論的都是 CPU、Java 虛擬機(jī)的字節(jié)序,通常叫做主機(jī)(host)字節(jié)序。在網(wǎng)絡(luò)編程中,字節(jié)流在網(wǎng)絡(luò)中傳輸是遵循大端序的,也叫網(wǎng)絡(luò)字節(jié)序。

由于 Java 虛擬機(jī)的字節(jié)序和網(wǎng)絡(luò)字節(jié)序是一致的,對(duì)于 Java 程序員來(lái)說(shuō),通常不太關(guān)心字節(jié)序的問(wèn)題。然而,當(dāng) Java 程序和 C 程序進(jìn)行通信的時(shí)候,需要關(guān)心字節(jié)序的問(wèn)題。

7. 小結(jié)

本文主要是介紹了 CPU 架構(gòu)帶來(lái)的多字節(jié)數(shù)值在內(nèi)存中存儲(chǔ)時(shí)的字節(jié)序問(wèn)題,字節(jié)序分為大端序小端序。在計(jì)算機(jī)網(wǎng)絡(luò)中,大端序也叫做網(wǎng)絡(luò)字節(jié)序;相應(yīng)的主機(jī)上的存儲(chǔ)順序叫做主機(jī)字節(jié)序。

在 Java 程序中,由于 Java 程序是在 Java 虛擬機(jī)上運(yùn)行,Java 虛擬機(jī)的字節(jié)序是大端序。然而 Java 虛擬機(jī)運(yùn)行的 Native 系統(tǒng)的字節(jié)序是不確定的,可以通過(guò) java.nio.ByteOrder 的 nativeOrder 方法來(lái)確定。

對(duì)于 Java 網(wǎng)絡(luò)編程中廣泛應(yīng)用的 ByteBuffer,則默認(rèn)是大端序,當(dāng)然你也可以根據(jù)需要設(shè)置它的字節(jié)序。對(duì)于多字節(jié)數(shù)值 Buffer,比如 IntBuffer、LongBuffer,則需要根據(jù)他們創(chuàng)建時(shí)所依賴的結(jié)構(gòu),來(lái)判定它們的字節(jié)序。

本節(jié)內(nèi)容相對(duì)簡(jiǎn)單,學(xué)習(xí)起來(lái)也會(huì)輕松很多,但是非常重要,需要掌握。