1、字符編碼方案的演變git
1.程序員
前文已經說起,編號字符集CCS(簡稱字符集)與字符編碼方式CEF(簡稱編碼方式)這兩個概念,在早期並無必要嚴格區分。編程
在Unicode編碼方案出現以前,字符集及其具體的編碼方式是綁定耦合在一塊兒的,所以,「字符集」、「編碼」或「編碼方式」甚至「編碼方案」這幾個概念常常相互指代、彼此混用。windows
好比,字符集裏的字符編號(即碼點編號)在不少文章裏也稱之爲字符編碼、字符碼、碼點、碼位、碼點值、碼值等,字符編碼也稱之爲編碼實現、編碼方案、編碼方式、編碼格式、編碼形式、內碼、編碼值、碼值(你沒看錯,字符編號與字符編碼都有可能被簡稱爲「碼值」,頭大了吧),等等,很是混亂。網絡
2.架構
對於ASCII、GB23十二、GBK、GB18030、Big5之類採用傳統字符編碼模型的歷史遺留方案來講,因爲基本上一個字符集只使用一種編碼方式,所以這種混用問題還不大。socket
但在Unicode這樣採用現代字符編碼模型的全新方案出現以後,不少人受上述這些歷史遺留方案的影響,從而致使沒法正確地理解字符集和編碼方式的關係,這致使了概念混亂,引發了大量誤解。編程語言
然而,對於採用現代字符編碼模型的Unicode標準來講,字符集和編碼方式是必須明確區分的。從軟件工程的角度來說,傳統字符編碼模型中緊密綁定耦合在一塊兒的字符集及編碼方式這兩個概念,在現代字符編碼模型中被分離解耦了,而這種解耦帶來了極大的靈活性。函數
這意味着,對於採用現代字符編碼模型的同一個字符集,能夠採用多個不一樣的編碼方式對其字符編號進行編碼。也所以,做爲同一個Unicode字符集,目前就定義了UTF-八、UTF-16和UTF-32等(UTF,即Unicode/UCS Transformation Format)多種可選的編碼方式。ui
3.
因此,用Unicode來稱呼一個編碼方式已不合適,而且容易產生誤導,引起混亂和致使困惑,而應該用UTF-八、UTF-16和UTF-32來稱呼編碼方式,以Unicode來稱呼字符集,將包括Unicode字符集及各UTF編碼方式等在內的總體稱之爲字符編碼方案或字符編碼系統或字符編碼標準。
另外,同一字符編碼方式CEF的碼元序列,在計算機實際處理、存儲和傳輸時,還需再次編碼轉換爲字符編碼模式CES的字節序列。
字符編碼方式CEF的碼元序列可理解爲字符編碼的邏輯表示形式,相對而言,字符編碼模式CES的字節序列則可理解爲字符編碼在計算機中的物理表示形式。
而字節序列,則涉及到了不一樣的字節序(Byte-Order,主要分爲大端序Big-Endian、小端序Little-Endian)。
2、字節序
1.
字節序,又稱字節順序,其英文爲Byte-Order;另外英文中還可稱爲Endianness,所以也翻譯爲端序。
Endianness這個詞,源自於1726年Jonathan Swift的名著:Gulliver's Travels(格列佛遊記)。在書中有一個童話故事,大意是指Lilliput小人國的國王下了一道指令,規定其人民在剝水煮蛋時必須從little-end(小端)開始剝。這個規定惹惱了一羣以爲應該要從big-end(大端)開始剝的人。事情發展到後來演變成了一場戰爭。後來,支持小端的人被稱爲little-endian,反之則被稱爲big-endian(在英語中後綴「-ian」表示「xx人」之意)。
1980年,Danny Cohen在他的論文「On Holy Wars and a Plea for Peace」中,第一次使用了Big-endian和Little-endian這兩個術語,最終它們成爲了異構計算機系統之間進行通信、交換數據時所要考慮的極其重要的一個問題。
(注:所謂異構是指不一樣架構、不一樣結構、不一樣構造等,而這裏的異構計算機系統,主要指的是採用不一樣CPU和/或不一樣操做系統的計算機系統。)
2.
爲何會存在字節序的問題?
固然不會是像童話故事裏那樣出於「無厘頭」的緣由,而是由於歷史上設計不一樣計算機系統的人在當時基於各自的理由和緣由(這裏的理由和緣由網上存在着各類不一樣的說法,但也或許根本就沒有具體理由和緣由,只是設計人員的我的偏好,甚至是隨意決定的),在各自計算機系統的設計上做出了不一樣的選擇。
字節序共分爲三種:
3.
字節序,具體來講,就是多字節數據(大於一個字節的數據)在計算機中存儲、讀取時其各個字節的排列順序。
字節序也被稱爲端序,這裏的「端」,是指多字節數據中位於兩端的字節,不少狀況下還特指尾端字節(也稱爲小端字節)。
所謂尾端字節或小端字節,假設按照人對文字一般從左到右(或從上到下)的讀寫順序來看的話,多字節數據位於右端(或下端)的低位字節就是尾端字節或小端字節,而將位於左端(或上端)的高位字節稱爲頭端字節或大端字節。
固然,若是不按照一般從左到右的順序,而是按照從右到左的順序,那麼多字節數據位於右端的高位字節就是頭端字節或大端字節,而將位於左端的低位字節稱爲尾端字節或小端字節。
可見,不論讀寫順序如何,所謂大端、頭端,指的是多字節數據中,表明更大數值的那個字節所在的那一端,而相反的那一端則是小端、尾端。
4.
而要完全講清楚大端序(高尾端序)、小端序(低尾端序),則須要從人讀寫二進制數的方向和內存地址的增加方向二者相結合講起:
人讀寫二進制數的方向爲(這是肯定不變的):左--->右,大端/頭端/高位--->小端/尾端/低位;或上--->下,大端/頭端/高位--->小端/尾端/低位;
內存地址的增加方向則爲(這是肯定不變的):左--->右,低地址--->高地址;或上--->下,低地址--->高地址。
不過,計算機在內存中存取數據的方向則不是肯定不變的,而是分爲兩種(注意,因爲人的讀寫方向和內存地址增加方向是肯定不變的,所以這裏指的是計算機在內存中「書寫」或「閱讀」數據的方向):
1) 左--->右,大端/頭端/高位--->小端/尾端/低位;或上--->下,大端/頭端/高位--->小端/尾端/低位;
這種狀況下,站在人的讀寫方向和內存地址增加方向(這二者的方向恰好一致)的角度來看,則是:大端在左(或在上),因此稱之爲大端序;或者說尾端在內存高地址,因此稱之爲高尾端序(即內存高地址存放多字節數據的尾端字節的字節順序)。
2) 右--->左,大端/頭端/高位--->小端/尾端/低位;或下--->上,大端/頭端/高位--->小端/尾端/低位。
這種狀況下,站在人的讀寫方向和內存地址增加方向(這二者的方向恰好一致)的角度來看,則是:小端在左(或在上),因此稱之爲小端序;或者說尾端在內存低地址,因此稱之爲低尾端序(即內存低地址存放多字節數據的尾端字節的字節順序)。
【特別提示:大端序、小端序特別容易搞混,很差記憶;所以,建議使用高尾端序、低尾端序,本人是按下面這個方法來記憶的,很容易記住:存儲的數據分頭和尾——左頭右尾,內存的地址分低和高——左低右高;所以,「高尾端」表示內存的高地址存儲數據的尾端,「低尾端」表示內存的低地址存儲數據的尾端。】
5.
注意,這裏的「數據」指的是數據類型意義上的數據,所以,準確地說字節序只跟多字節的整型數據類型有關,好比int、short、long型;跟單字節的整型數據類型byte無關。
那麼,爲何就只跟多字節的整型數據有關,而跟單字節的整型數據無關呢?
由於在現代計算機中,字節是計算機數據存儲的基本單位,對於總體上的單一字節(a byte),涉及到的是其8個比特位的順序(位序、比特序,因爲通常直接由硬件處理,程序員大體瞭解便可,這裏不深刻探討),顯然不存在字節序問題。
然而,對於在數據類型上做爲一個總體的多字節數據而言,它是由各個可被單獨尋址的字節所組成的(處理器尋址的最小單位通常是1個字節),因爲歷史的緣由,其各個字節的存儲順序在不一樣的系統平臺(包括CPU和操做系統)上是不一樣的。
6.
也就是說,若是計算機處理的數據是單字節數據類型(byte),是不存在字節序問題的,由於單個字節已是處理器尋址的最小單位以及存儲和傳輸的最小單元了,存儲時單字節數據類型直接進行,讀取時也不存在根據先後2個字節才能解析出其值的狀況,而構造字節流時也不會從一個單字節數據類型的值當中產生2個或以上的字節(既然是單字節數據類型,構造字節流時固然只可能產生1個字節)。
可是,若是計算機處理的數據是多字節數據類型(int、short、long等),雖然因爲構成它們的2個或2個以上的字節是做爲一個總體來進行處理的(好比以彙編語言中的數據類型word或dword爲單位進行一次性處理,而不是以byte爲單位分次處理;更深刻地來說,CPU通常是以字爲一個總體來處理數據的,當單個數據不足一個字長時,則將多個數據「拼成」一個字再進行處理),但問題是字節纔是CPU對內存尋址的最小單位以及存儲和傳輸的最小單元。
所以,CPU在讀取做爲一個總體來進行處理的多字節數據類型的數據時,必須根據先後2個或2個以上的字節來解析出一個多字節數據類型的值;並且構造字節序列時也會從一個多字節數據類型的值當中產生2個或2個以上的字節。
7.
這樣一來,多字節數據類型的數據內部各字節間的排列順序,是會影響從字節序列恢復到數值的;反之,也會影響從數值到字節序列的構造。
因此,在存儲和讀取多字節數據類型的數據時,必須按照計算機系統所規定的字節序進行(這一點程序員瞭解便可,計算機會自動處理);而尤爲是在跨字節序不一樣的異構計算機系統進行通信並交換數據時,通信的任何一方更是必須明確對方所採用的字節序,而後雙方將各自接收到的數據按各自的字節序對數據進行轉換(有時候須要程序員專門編寫轉換程序),不然通信將會出錯,甚至直接失敗。
8.
實際上,int、short、long等數據類型通常是編程語言層面的概念,更進一步而言,這其實涉及到了機器硬件層面(即彙編語言)中的數據類型byte字節、word字、dword雙字等在硬件中的表達與處理機制(實質上字節序跟CPU寄存器的位數、存放順序密切相關)。具體可參看附文:《本質啊本質之一:數據類型的本質》、《寄存器與字、字節》。
【附:本質啊本質之一:數據類型的本質
CSDN博客 博主:band_of_brothers 發表於:2007-10-10 22:20
研究一個層面的問題,每每要從更深的層面找尋答案。這就如C語言與彙編、彙編與機器指令,然而終究要有個底限,這個底限以能使咱們問心無愧爲準,就比如公理之於數學、三大定律之於宏觀物理。
在這裏就將機器指令做爲最後的底限吧,儘管再深刻下去還有微指令,但那畢竟是太機器了,能夠了。如下全部從C代碼編譯生成彙編代碼用的是命令:cl xxx..c /Fa /Ze。
類型的本質
類型這個概念,好多地方都有講,但說實話,你真的理解嗎?什麼是類型?類型是一個抽象的概念仍是一個真實的存在?嗯?
開始:
一、「好多相同或類似事物的綜合」(辭海)。
二、X86機器的數據類型有byte、word、dword、fword、tword、qword,等等。
三、「給內存塊一個明確的名字,就象郵件上的收件人同樣。給其一個明確的數據類型,就好象說,郵件是一封信,仍是一個包裹。」
四、類型就是一次能夠操做的塊的大小,就是一個單位,就像克、千克、噸同樣。雙字一次操做32位;字,一次操做16位;若是沒有各類類型,機器只有一個類型單位——字節,那麼當須要一個4字節大小的塊時,就須要4次操做,而若是有雙字這個類型單位,那麼只須要一次操做就能夠了。
五、類型,是機器層面支持的,不是軟的,是硬的,有實實在在的機器碼爲證。
類型的反彙編
W32dasm反彙編出來的東西,能夠看出不一樣的類型,機器碼不一樣,說明類型是機器硬件級別支持的,不是經過軟件實現的,更不是一個抽象的概念。
Opcodes上關於mov的機器碼講的更清楚:
須要說明的是,一些大的類型單位,如qword等,在mov等標準指令裏是沒有的,在一些特殊指令裏才能用到,如浮點指令:fmul qword ptr [0067FB08] 機器碼:DC0D08FB6700。】
【附:寄存器與字、字節
字節:記爲byte,一個字節由8個比特(bit)組成,能夠直接存在一個8位寄存器裏
1 0 1 0 1 0 0 1
一個字節
字:記爲word,一個字由2個字節(共16比特)組成,能夠直接存在一個16位寄存器裏
1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
高位字節 低位字節
一個8位寄存器用2位十六進制數表示,只能存1個字節(1個byte類型的數據)
一個16位寄存器用4位十六進制數表示,可存1個字(1個word類型的數據)或2個字節(2個byte類型的數據)
一個32位寄存器用8位十六進制數表示,可存2個字(1個dword類型的數據或2個word類型的數據)或4個字節(4個byte類型的數據)】
(笨笨阿林原創文章,轉載請註明出處)
9.
下面簡要介紹一下字節序的三種類型:
a) 小端序Little-Endian(低尾端序)
就是低位字節(即小端字節)存放在內存的低地址,而高位字節(即大端字節)存放在內存的高地址。
這是最符合人的直覺思惟的字節序(但卻不符合人的讀寫習慣),由於從人的第一觀感來講,低位字節的值小,對應放在內存地址也小的地方,也即內存中的低位地址;反之,高位字節的值大,對應放在內存地址大的地方,也即內存中的高位地址。
b) 大端序Big-Endian(高尾端序)
就是高位字節(即大端字節)存放在內存的低地址,低位字節(即小端字節)存放在內存的高地址。
這是最符合人平時的讀寫習慣的字節序(但卻不符合人的直覺思惟),由於不用像在Little-Endian中還需考慮字節的高位、低位與內存的高地址、低地址的對應關係,只需把數值按照人一般的書寫習慣,從高位到低位的順序直接在內存中從左到右或從上到下(下圖中就是從上到下)按照由低到高的內存地址,一個字節一個字節地填充進去。
c) 中間序Middle-Endian(混合序Mixed-Endian)
混合序具備更復雜的順序。以PDP-11爲例,32位的0x0A0B0C0D被存儲爲:
混合序較少見,常見的多爲大端序和小端序。
【附:網絡字節序(network byte order網絡字節順序、網絡序)。
另外,還一種網絡字節序(network byte order網絡字節順序、網絡序)。
網絡字節順序是TCP/IP中規定好的一種數據表示格式,它與具體的CPU類型、操做系統等無關,從而能夠保證數據在不一樣主機之間傳輸時可以被正確解釋。IP協議中定義大端序Big Endian爲網絡字節序。
不過,容易使人困惑的是,IP協議做爲網絡層協議,其面向的數據是報文,是沒有字節的概念的,也就無關字節序了。所以,英文版wikipedia上說:
In fact, the Internet Protocol defines a standard big-endian network byte order. This byte order is used for all numeric values in the packet headers and by many higher level protocols and file formats that are designed for use over IP.
也就是說,IP協議裏的字節序其實是用在分組頭裏的數值上的,例如每一個分組頭會包含源IP地址和目標IP地址,在尋址和路由的時候是須要用到這些值的。
好比,4個字節的32 bit值如下面的次序傳輸:首先是高位的0~7bit,其次8~15bit,而後16~23bit,最後是低位的24~31bit。這種傳輸次序稱做大端字節序。因爲TCP/IP頭部中全部的二進制整數在網絡中傳輸時都要求以這種次序,所以它又稱做網絡字節序。
再好比,以太網頭部中2字節的「以太網幀類型」字段,表示是的後面所跟數據幀的類型。對於ARP請求或應答的以太網幀類型來講,在網絡傳輸時,發送的順序是以大端方式進行的:0x08,0x06。其在內存中的映象以下所示:
內存低地址
------------------------
0x08 -- 高位字節
0x06 -- 低位字節
------------------------
內存高地址
該字段的值爲0x0806,也是以大端方式存放在內存中的。
其實,IP報文中的不少數據都是須要作字節序轉換的,好比包長度、check sum校驗和等,這些值大都是short(16bit)或者long(32bit)型,因此解析IP報文時也須要作網絡->主機字節序轉換,而生成報文字節流時則須要進行主機->網絡字節序轉換(計算機中的字節序被稱之爲主機字節序,簡稱主機序;相對於網絡傳輸中的字節序被稱之爲網絡字節序,簡稱網絡序)。
爲了進行網絡字節序與主機字節序的轉換,BSD sockets(Berkeley sockets)提供了四個轉換的函數:htons、ntohs、htonl、ntohl,其中h是host、n是network、s是short、l是long:
htons把unsigned short類型從主機字節序轉換到網絡字節序
htonl把unsigned long類型從主機字節序轉換到網絡字節序
ntohs把unsigned short類型從網絡字節序轉換到主機字節序
ntohl把unsigned long類型從網絡字節序轉換到主機字節序
在使用little endian的系統中,這些函數會把字節序進行轉換;在使用big endian類型的系統中,這些函數會定義成空宏。
Windows系統API中也提供了相似的轉換函數。而在.Net中,網絡字節序與主機字節序二者之間的轉換,由IPAddress類的靜態方法提供:HostToNetworkOrder和NetworkToHostOrder。】
10.
Intel和AMD的X86平臺,以及DEC(Digital Equipment Corporation,後與Compaq合併,以後Compaq又與HP合併)採用的是Little-Endian,而像IBM、Sun的SPARC採用的就是Big-Endian。有的嵌入式平臺是Big-Endian的。JAVA字節序也是Big-Endian的。
固然,這不表明全部狀況。有的CPU即能工做於小端,又能工做於大端,好比ARM、Alpha、摩托羅拉的Power PC、SPARC V九、MIPS、PA-RISC和IA64等體系結構(具體情形可參考處理器手冊),其字節序是可切換的,這種可切換的特性能夠提升效率或者簡化網絡設備和軟件的邏輯。
這種可切換的字節序被稱爲Bi-Endian(前綴「Bi-」表示「雙邊的、二重的、兩個的」),用於硬件上意指計算機存儲時具備可使用兩種不一樣字節序中任意一種的能力。具體這類CPU是大端仍是小端,和具體設置有關。如Power PC可支持Little-Endian字節序,但其默認配置爲Big-Endian字節序。
11.
通常來講,大部分用戶的操做系統,如windows、FreeBsd、Linux是Little-Endian的;少部分,如Mac OS是Big-Endian的。
具體參見下表:
12.
因此說,Little Endian仍是Big Endian與操做系統和CPU芯片類型都有關係。所以在一個計算機系統中,有可能同時存在大端和小端兩種模式的現象。
這一現象爲系統的軟硬件設計帶來了不小的麻煩,這要求系統設計工程師必須深刻理解大端和小端模式的差異。大端與小端模式的差異體如今一個處理器的寄存器、指令集、系統總線等各個層次中。
其實不少技術人員在實際的非跨平臺桌面應用開發過程當中都不多會直接和字節序打交道(由於由硬件直接處理),但在跨平臺及網絡應用開發過程當中由於涉及到異構計算機系統間的通信交流,字節序是很難迴避的問題。
(笨笨阿林原創文章,轉載請註明出處)