字符編碼方案的演變與字節(jié)序
一蛤铜、字符編碼方案的演變
1.
前文已經(jīng)提及,編號字符集CCS(簡稱字符集)與字符編碼方式CEF(簡稱編碼方式)這兩個概念勋功,在早期并沒有必要嚴(yán)格區(qū)分淑倾。
在Unicode編碼方案出現(xiàn)之前,字符集及其具體的編碼方式是綁定耦合在一起的米绕,因此,“字符集”馋艺、“編碼”或“編碼方式”甚至“編碼方案”這幾個概念經(jīng)常相互指代栅干、彼此混用。
比如捐祠,字符集里的字符編號(即碼點編號)在很多文章里也稱之為字符編碼碱鳞、字符碼、碼點踱蛀、碼位窿给、碼點值贵白、碼值等,字符編碼也稱之為編碼實現(xiàn)崩泡、編碼方案禁荒、編碼方式、編碼格式角撞、編碼形式呛伴、內(nèi)碼、編碼值靴寂、碼值(你沒看錯,字符編號與字符編碼都有可能被簡稱為“碼值”召耘,頭大了吧)百炬,等等,非澄鬯混亂剖踊。
2.
對于ASCII、GB2312衫贬、GBK德澈、GB18030、Big5之類采用傳統(tǒng)字符編碼模型的歷史遺留方案來說固惯,由于基本上一個字符集只使用一種編碼方式梆造,因此這種混用問題還不大。
但在Unicode這樣采用現(xiàn)代字符編碼模型的全新方案出現(xiàn)之后葬毫,很多人受上述這些歷史遺留方案的影響镇辉,從而導(dǎo)致無法正確地理解字符集和編碼方式的關(guān)系,這導(dǎo)致了概念混亂贴捡,引起了大量誤解忽肛。
然而,對于采用現(xiàn)代字符編碼模型的Unicode標(biāo)準(zhǔn)來說烂斋,字符集和編碼方式是必須明確區(qū)分的屹逛。從軟件工程的角度來講,傳統(tǒng)字符編碼模型中緊密綁定耦合在一起的字符集及編碼方式這兩個概念汛骂,在現(xiàn)代字符編碼模型中被分離解耦了罕模,而這種解耦帶來了極大的靈活性。
這意味著帘瞭,對于采用現(xiàn)代字符編碼模型的同一個字符集手销,可以采用多個不同的編碼方式對其字符編號進(jìn)行編碼。也因此图张,作為同一個Unicode字符集锋拖,目前就定義了UTF-8诈悍、UTF-16和UTF-32等(UTF,即Unicode/UCS Transformation Format)多種可選的編碼方式兽埃。
3.
所以侥钳,用Unicode來稱呼一個編碼方式已不合適,并且容易產(chǎn)生誤導(dǎo)柄错,引發(fā)混亂和導(dǎo)致困惑舷夺,而應(yīng)該用UTF-8、UTF-16和UTF-32來稱呼編碼方式售貌,以Unicode來稱呼字符集给猾,將包括Unicode字符集及各UTF編碼方式等在內(nèi)的整體稱之為字符編碼方案或字符編碼系統(tǒng)或字符編碼標(biāo)準(zhǔn)。
另外颂跨,同一字符編碼方式CEF的碼元序列敢伸,在計算機(jī)實際處理、存儲和傳輸時恒削,還需再次編碼轉(zhuǎn)換為字符編碼模式CES的字節(jié)序列池颈。
字符編碼方式CEF的碼元序列可理解為字符編碼的邏輯表示形式,相對而言钓丰,字符編碼模式CES的字節(jié)序列則可理解為字符編碼在計算機(jī)中的物理表示形式躯砰。
而字節(jié)序列,則涉及到了不同的字節(jié)序(Byte-Order携丁,主要分為大端序Big-Endian琢歇、小端序Little-Endian)。
二梦鉴、字節(jié)序
1.
字節(jié)序矿微,又稱字節(jié)順序,其英文為Byte-Order尚揣;另外英文中還可稱為Endianness涌矢,因此也翻譯為端序。
Endianness這個詞快骗,源自于1726年Jonathan Swift的名著:Gulliver's Travels(格列佛游記)娜庇。在書中有一個童話故事,大意是指Lilliput小人國的國王下了一道指令方篮,規(guī)定其人民在剝水煮蛋時必須從little-end(小端)開始剝名秀。這個規(guī)定惹惱了一群覺得應(yīng)該要從big-end(大端)開始剝的人。事情發(fā)展到后來演變成了一場戰(zhàn)爭藕溅。后來匕得,支持小端的人被稱為little-endian,反之則被稱為big-endian(在英語中后綴“-ian”表示“xx人”之意)。
1980年汁掠,Danny Cohen在他的論文“On Holy Wars and a Plea for Peace”中略吨,第一次使用了Big-endian和Little-endian這兩個術(shù)語,最終它們成為了異構(gòu)計算機(jī)系統(tǒng)之間進(jìn)行通訊考阱、交換數(shù)據(jù)時所要考慮的極其重要的一個問題翠忠。
(注:所謂異構(gòu)是指不同架構(gòu)、不同結(jié)構(gòu)乞榨、不同構(gòu)造等秽之,而這里的異構(gòu)計算機(jī)系統(tǒng),主要指的是采用不同CPU和/或不同操作系統(tǒng)的計算機(jī)系統(tǒng)吃既。)
2.
為什么會存在字節(jié)序的問題考榨?
當(dāng)然不會是像童話故事里那樣出于“無厘頭”的原因,而是因為歷史上設(shè)計不同計算機(jī)系統(tǒng)的人在當(dāng)時基于各自的理由和原因(這里的理由和原因網(wǎng)上存在著各種不同的說法鹦倚,但也或許根本就沒有具體理由和原因河质,只是設(shè)計人員的個人偏好,甚至是隨意決定的)申鱼,在各自計算機(jī)系統(tǒng)的設(shè)計上作出了不同的選擇愤诱。
字節(jié)序共分為三種:
大端序BE(Big-Endian云头,也稱高尾端序)捐友;
小端序LE(Little-Endian,也稱為低尾端序)溃槐;
中間序ME(Middle-Endian匣砖,也稱為混合序),不太常用昏滴,本文不作介紹猴鲫。
3.
字節(jié)序,具體來說谣殊,就是多字節(jié)數(shù)據(jù)(大于一個字節(jié)的數(shù)據(jù))在計算機(jī)中存儲拂共、讀取時其各個字節(jié)的排列順序。
字節(jié)序也被稱為端序姻几,這里的“端”宜狐,是指多字節(jié)數(shù)據(jù)中位于兩端的字節(jié),很多情況下還特指尾端字節(jié)(也稱為小端字節(jié))蛇捌。
所謂尾端字節(jié)或小端字節(jié)抚恒,假設(shè)按照人對文字通常從左到右(或從上到下)的讀寫順序來看的話,多字節(jié)數(shù)據(jù)位于右端(或下端)的低位字節(jié)就是尾端字節(jié)或小端字節(jié)络拌,而將位于左端(或上端)的高位字節(jié)稱為頭端字節(jié)或大端字節(jié)俭驮。
當(dāng)然,如果不按照通常從左到右的順序春贸,而是按照從右到左的順序混萝,那么多字節(jié)數(shù)據(jù)位于右端的高位字節(jié)就是頭端字節(jié)或大端字節(jié)遗遵,而將位于左端的低位字節(jié)稱為尾端字節(jié)或小端字節(jié)。
可見譬圣,不論讀寫順序如何瓮恭,所謂大端、頭端厘熟,指的是多字節(jié)數(shù)據(jù)中屯蹦,代表更大數(shù)值的那個字節(jié)所在的那一端,而相反的那一端則是小端绳姨、尾端登澜。
4.
而要徹底講清楚大端序(高尾端序)、小端序(低尾端序)飘庄,則需要從人讀寫二進(jìn)制數(shù)的方向和內(nèi)存地址的增長方向兩者相結(jié)合講起:
人讀寫二進(jìn)制數(shù)的方向為(這是確定不變的):左--->右脑蠕,大端/頭端/高位--->小端/尾端/低位;或上--->下跪削,大端/頭端/高位--->小端/尾端/低位谴仙;
內(nèi)存地址的增長方向則為(這是確定不變的):左--->右,低地址--->高地址碾盐;或上--->下晃跺,低地址--->高地址。
不過毫玖,計算機(jī)在內(nèi)存中存取數(shù)據(jù)的方向則不是確定不變的掀虎,而是分為兩種(注意,由于人的讀寫方向和內(nèi)存地址增長方向是確定不變的付枫,因此這里指的是計算機(jī)在內(nèi)存中“書寫”或”閱讀“數(shù)據(jù)的方向):
1)?左--->右烹玉,大端/頭端/高位--->小端/尾端/低位;或上--->下阐滩,大端/頭端/高位--->小端/尾端/低位二打;
這種情況下,站在人的讀寫方向和內(nèi)存地址增長方向(這兩者的方向剛好一致)的角度來看掂榔,則是:大端在左(或在上)继效,所以稱之為大端序;或者說尾端在內(nèi)存高地址衅疙,所以稱之為高尾端序(即內(nèi)存高地址存放多字節(jié)數(shù)據(jù)的尾端字節(jié)的字節(jié)順序)莲趣。
2)?右--->左,大端/頭端/高位--->小端/尾端/低位饱溢;或下--->上喧伞,大端/頭端/高位--->小端/尾端/低位。
這種情況下,站在人的讀寫方向和內(nèi)存地址增長方向(這兩者的方向剛好一致)的角度來看潘鲫,則是:小端在左(或在上)翁逞,所以稱之為小端序;或者說尾端在內(nèi)存低地址溉仑,所以稱之為低尾端序(即內(nèi)存低地址存放多字節(jié)數(shù)據(jù)的尾端字節(jié)的字節(jié)順序)挖函。
【特別提示:大端序、小端序特別容易搞混浊竟,不好記憶怨喘;因此,建議使用高尾端序振定、低尾端序必怜,本人是按下面這個方法來記憶的,很容易記缀笃怠:存儲的數(shù)據(jù)分頭和尾——左頭右尾梳庆,內(nèi)存的地址分低和高——左低右高;因此卑惜,“高尾端”表示內(nèi)存的高地址存儲數(shù)據(jù)的尾端膏执,“低尾端”表示內(nèi)存的低地址存儲數(shù)據(jù)的尾端÷毒茫】
5.
注意更米,這里的“數(shù)據(jù)”指的是數(shù)據(jù)類型意義上的數(shù)據(jù),因此抱环,準(zhǔn)確地說字節(jié)序只跟多字節(jié)的整型數(shù)據(jù)類型有關(guān)壳快,比如int纸巷、short镇草、long型;跟單字節(jié)的整型數(shù)據(jù)類型byte無關(guān)瘤旨。
那么梯啤,為什么就只跟多字節(jié)的整型數(shù)據(jù)有關(guān),而跟單字節(jié)的整型數(shù)據(jù)無關(guān)呢存哲?
因為在現(xiàn)代計算機(jī)中因宇,字節(jié)是計算機(jī)數(shù)據(jù)存儲的基本單位,對于整體上的單一字節(jié)(a byte)祟偷,涉及到的是其8個比特位的順序(位序察滑、比特序,由于一般直接由硬件處理修肠,程序員大致了解即可贺辰,這里不深入探討),顯然不存在字節(jié)序問題。
然而饲化,對于在數(shù)據(jù)類型上作為一個整體的多字節(jié)數(shù)據(jù)而言莽鸭,它是由各個可被單獨尋址的字節(jié)所組成的(處理器尋址的最小單位一般是1個字節(jié)),由于歷史的原因吃靠,其各個字節(jié)的存儲順序在不同的系統(tǒng)平臺(包括CPU和操作系統(tǒng))上是不同的硫眨。
6.
也就是說,如果計算機(jī)處理的數(shù)據(jù)是單字節(jié)數(shù)據(jù)類型(byte)巢块,是不存在字節(jié)序問題的礁阁,因為單個字節(jié)已經(jīng)是處理器尋址的最小單位以及存儲和傳輸?shù)淖钚卧耍鎯r單字節(jié)數(shù)據(jù)類型直接進(jìn)行族奢,讀取時也不存在根據(jù)前后2個字節(jié)才能解析出其值的情況氮兵,而構(gòu)造字節(jié)流時也不會從一個單字節(jié)數(shù)據(jù)類型的值當(dāng)中產(chǎn)生2個或以上的字節(jié)(既然是單字節(jié)數(shù)據(jù)類型,構(gòu)造字節(jié)流時當(dāng)然只可能產(chǎn)生1個字節(jié))歹鱼。
但是泣栈,如果計算機(jī)處理的數(shù)據(jù)是多字節(jié)數(shù)據(jù)類型(int、short弥姻、long等)南片,雖然由于構(gòu)成它們的2個或2個以上的字節(jié)是作為一個整體來進(jìn)行處理的(比如以匯編語言中的數(shù)據(jù)類型word或dword為單位進(jìn)行一次性處理,而不是以byte為單位分次處理庭敦;更深入地來講疼进,CPU一般是以字為一個整體來處理數(shù)據(jù)的,當(dāng)單個數(shù)據(jù)不足一個字長時秧廉,則將多個數(shù)據(jù)“拼成”一個字再進(jìn)行處理)伞广,但問題是字節(jié)才是CPU對內(nèi)存尋址的最小單位以及存儲和傳輸?shù)淖钚卧?/p>
因此,CPU在讀取作為一個整體來進(jìn)行處理的多字節(jié)數(shù)據(jù)類型的數(shù)據(jù)時疼电,必須根據(jù)前后2個或2個以上的字節(jié)來解析出一個多字節(jié)數(shù)據(jù)類型的值嚼锄;而且構(gòu)造字節(jié)序列時也會從一個多字節(jié)數(shù)據(jù)類型的值當(dāng)中產(chǎn)生2個或2個以上的字節(jié)。
7.
這樣一來蔽豺,多字節(jié)數(shù)據(jù)類型的數(shù)據(jù)內(nèi)部各字節(jié)間的排列順序区丑,是會影響從字節(jié)序列恢復(fù)到數(shù)值的;反之修陡,也會影響從數(shù)值到字節(jié)序列的構(gòu)造沧侥。
所以,在存儲和讀取多字節(jié)數(shù)據(jù)類型的數(shù)據(jù)時魄鸦,必須按照計算機(jī)系統(tǒng)所規(guī)定的字節(jié)序進(jìn)行(這一點程序員了解即可宴杀,計算機(jī)會自動處理);而尤其是在跨字節(jié)序不同的異構(gòu)計算機(jī)系統(tǒng)進(jìn)行通訊并交換數(shù)據(jù)時拾因,通訊的任何一方更是必須明確對方所采用的字節(jié)序旺罢,然后雙方將各自接收到的數(shù)據(jù)按各自的字節(jié)序?qū)?shù)據(jù)進(jìn)行轉(zhuǎn)換(有時候需要程序員專門編寫轉(zhuǎn)換程序)斯棒,否則通訊將會出錯,甚至直接失敗主经。
8.
實際上荣暮,int、short罩驻、long等數(shù)據(jù)類型一般是編程語言層面的概念穗酥,更進(jìn)一步而言,這其實涉及到了機(jī)器硬件層面(即匯編語言)中的數(shù)據(jù)類型byte字節(jié)惠遏、word字砾跃、dword雙字等在硬件中的表達(dá)與處理機(jī)制(實質(zhì)上字節(jié)序跟CPU寄存器的位數(shù)、存放順序密切相關(guān))节吮。具體可參看附文:《本質(zhì)啊本質(zhì)之一:數(shù)據(jù)類型的本質(zhì)》抽高、《寄存器與字、字節(jié)》透绩。
【附:本質(zhì)啊本質(zhì)之一:數(shù)據(jù)類型的本質(zhì)
CSDN博客 博主:band_of_brothers?發(fā)表于:2007-10-10 22:20
研究一個層面的問題翘骂,往往要從更深的層面找尋答案。這就如C語言與匯編帚豪、匯編與機(jī)器指令碳竟,然而終究要有個底限,這個底限以能使我們心安理得為準(zhǔn)狸臣,就好比公理之于數(shù)學(xué)莹桅、三大定律之于宏觀物理。
在這里就將機(jī)器指令作為最后的底限吧烛亦,盡管再深入下去還有微指令诈泼,但那畢竟是太機(jī)器了,可以了煤禽。以下所有從C代碼編譯生成匯編代碼用的是命令:cl xxx..c /Fa /Ze铐达。
類型的本質(zhì)
類型這個概念,好多地方都有講呜师,但說實話娶桦,你真的理解嗎贾节?什么是類型汁汗?類型是一個抽象的概念還是一個真實的存在?嗯栗涂?
開始:
1)“好多相同或相似事物的綜合”(辭海)知牌。
2)X86機(jī)器的數(shù)據(jù)類型有byte、word斤程、dword角寸、fword菩混、tword、qword扁藕,等等沮峡。
3)“給內(nèi)存塊一個明確的名字,就象郵件上的收件人一樣亿柑。給其一個明確的數(shù)據(jù)類型邢疙,就好象說,郵件是一封信望薄,還是一個包裹疟游。”
4)類型就是一次可以操作的塊的大小痕支,就是一個單位颁虐,就像克、千克卧须、噸一樣另绩。雙字一次操作32位;字花嘶,一次操作16位板熊;如果沒有各種類型,機(jī)器只有一個類型單位——字節(jié)察绷,那么當(dāng)需要一個4字節(jié)大小的塊時干签,就需要4次操作,而如果有雙字這個類型單位拆撼,那么只需要一次操作就可以了容劳。
5)類型,是機(jī)器層面支持的闸度,不是軟的竭贩,是硬的,有實實在在的機(jī)器碼為證莺禁。
類型的反匯編
W32dasm反匯編出來的東西留量,可以看出不同的類型,機(jī)器碼不同哟冬,說明類型是機(jī)器硬件級別支持的楼熄,不是通過軟件實現(xiàn)的,更不是一個抽象的概念浩峡。
Opcodes上關(guān)于mov的機(jī)器碼講的更清楚:
需要說明的是可岂,一些大的類型單位,如qword等翰灾,在mov等標(biāo)準(zhǔn)指令里是沒有的缕粹,在一些特殊指令里才能用到稚茅,如浮點指令:fmul qword ptr [0067FB08] 機(jī)器碼:DC0D08FB6700∑秸叮】
【附:寄存器與字亚享、字節(jié)
字節(jié):記為byte,一個字節(jié)由8個比特(bit)組成绘面,可以直接存在一個8位寄存器里
1 0 1 0 1 0 0 1
? ? 一個字節(jié)
字:記為word虹蒋,一個字由2個字節(jié)(共16比特)組成,可以直接存在一個16位寄存器里
?1 1 1 1 1 1 1 1? 0 0 0 0 0 0 0 0
??????高位字節(jié)? ? ? ?? 低位字節(jié)
一個8位寄存器用2位十六進(jìn)制數(shù)表示飒货,只能存1個字節(jié)(1個byte類型的數(shù)據(jù))
一個16位寄存器用4位十六進(jìn)制數(shù)表示魄衅,可存1個字(1個word類型的數(shù)據(jù))或2個字節(jié)(2個byte類型的數(shù)據(jù))
一個32位寄存器用8位十六進(jìn)制數(shù)表示,可存2個字(1個dword類型的數(shù)據(jù)或2個word類型的數(shù)據(jù))或4個字節(jié)(4個byte類型的數(shù)據(jù))】
(笨笨阿林原創(chuàng)文章塘辅,轉(zhuǎn)載請注明出處)
9.
下面簡要介紹一下字節(jié)序的三種類型:
a)?小端序Little-Endian(低尾端序)
就是低位字節(jié)(即小端字節(jié))存放在內(nèi)存的低地址晃虫,而高位字節(jié)(即大端字節(jié))存放在內(nèi)存的高地址。
這是最符合人的直覺思維的字節(jié)序(但卻不符合人的讀寫習(xí)慣)扣墩,因為從人的第一觀感來說哲银,低位字節(jié)的值小,對應(yīng)放在內(nèi)存地址也小的地方呻惕,也即內(nèi)存中的低位地址荆责;反之,高位字節(jié)的值大亚脆,對應(yīng)放在內(nèi)存地址大的地方做院,也即內(nèi)存中的高位地址。
b)?大端序Big-Endian(高尾端序)
就是高位字節(jié)(即大端字節(jié))存放在內(nèi)存的低地址濒持,低位字節(jié)(即小端字節(jié))存放在內(nèi)存的高地址键耕。
這是最符合人平時的讀寫習(xí)慣的字節(jié)序(但卻不符合人的直覺思維),因為不用像在Little-EndIan中還需考慮字節(jié)的高位柑营、低位與內(nèi)存的高地址屈雄、低地址的對應(yīng)關(guān)系,只需把數(shù)值按照人通常的書寫習(xí)慣官套,從高位到低位的順序直接在內(nèi)存中從左到右或從上到下(下圖中就是從上到下)按照由低到高的內(nèi)存地址酒奶,一個字節(jié)一個字節(jié)地填充進(jìn)去。
?c)?中間序Middle-Endian(混合序Mixed-Endian)
混合序具有更復(fù)雜的順序奶赔。以PDP-11為例惋嚎,32位的0x0A0B0C0D被存儲為:
混合序較少見,常見的多為大端序和小端序纺阔。
(注:其實還一種網(wǎng)絡(luò)字節(jié)序(network byte order網(wǎng)絡(luò)字節(jié)順序瘸彤、網(wǎng)絡(luò)序),這里不做深入介紹笛钝。)
10.
Intel和AMD的X86平臺质况,以及DEC(Digital Equipment Corporation數(shù)字設(shè)備公司,后與Compaq合并玻靡,之后Compaq又與HP合并)采用的是Little-Endian结榄,而像IBM、Sun的SPARC采用的就是Big-Endian囤捻。有的嵌入式平臺是Big-Endian的臼朗。JAVA字節(jié)序也是Big-Endian的。
當(dāng)然蝎土,這不代表所有情況视哑。有的CPU既能工作于小端序,又能工作于大端序誊涯,比如ARM挡毅、Alpha、摩托羅拉的Power PC暴构、SPARC V9跪呈、MIPS、PA-RISC和IA64等體系結(jié)構(gòu)(具體情形可參考處理器手冊)取逾,其字節(jié)序是可切換的耗绿,這種可切換的特性可以提高效率或者簡化網(wǎng)絡(luò)設(shè)備和軟件的邏輯。
這種可切換的字節(jié)序被稱為Bi-Endian(前綴“Bi-”表示“雙邊的砾隅、二重的误阻、兩個的”),用于硬件上意指計算機(jī)存儲時具有可以使用兩種不同字節(jié)序中任意一種的能力晴埂。具體這類CPU是大端序還是小端序堕绩,和具體設(shè)置有關(guān)。如Power PC可支持Little-Endian字節(jié)序邑时,但其默認(rèn)配置為Big-Endian字節(jié)序奴紧。
11.
一般來說,大部分用戶的操作系統(tǒng)晶丘,如Windows黍氮、FreeBsd、Linux是Little-Endian的浅浮;少部分沫浆,如Mac OS是Big-Endian的。
具體參見下表:
12.
所以說滚秩,Little Endian還是Big Endian與操作系統(tǒng)和CPU類型都有關(guān)系专执。因此在一個計算機(jī)系統(tǒng)中,有可能同時存在大端序和小端序兩種字節(jié)序的現(xiàn)象郁油。
這一現(xiàn)象為系統(tǒng)的軟硬件設(shè)計帶來了不小的麻煩本股,這要求系統(tǒng)設(shè)計工程師必須深入理解大端序和小端序的差別攀痊。大端序與小端序的差別體現(xiàn)在一個處理器的寄存器、指令集拄显、系統(tǒng)總線等各個層次中苟径。
其實,很多技術(shù)人員在實際的非跨平臺桌面應(yīng)用開發(fā)過程中都很少會直接和字節(jié)序打交道躬审,但在跨平臺及網(wǎng)絡(luò)應(yīng)用開發(fā)過程中因為涉及到異構(gòu)計算機(jī)系統(tǒng)間的通訊交流棘街,字節(jié)序是很難回避的問題。
(笨笨阿林原創(chuàng)文章承边,轉(zhuǎn)載請注明出處)