字符的編碼(四)

(七)Unicode編碼方案概述

前面講過,隨著計算機(jī)發(fā)展到世界各地,于是各個國家和地區(qū)各自為政趣效,搞出了很多既兼容ASCII但又互相不兼容的各種編碼方案糟红。這樣一來同一個二進(jìn)制編碼就有可能被解釋成不同的字符艾帐,導(dǎo)致不同的字符集在交換數(shù)據(jù)時帶來極大的不便。

比如大陸和臺灣是只相隔150海里盆偿、使用著同一種語言的兄弟地區(qū)柒爸,也分別采用了不同的DBCS雙字節(jié)字符集編碼方案。

以前大陸地區(qū)必須裝上類似于“UCDOS希望漢字系統(tǒng)”這樣的中文處理系統(tǒng)專門來處理簡體漢字的顯示事扭、輸入問題捎稚。

而臺灣地區(qū)由于采用BIG5編碼方案(統(tǒng)一繁體字編碼,俗稱大五碼求橄,使用2個字節(jié)表示繁體漢字)今野,則必須安裝類似于“ET倚天漢字系統(tǒng)”這樣的繁體中文處理系統(tǒng)才可以正確顯示、輸入繁體漢字罐农。

因此条霜,要想打開一個文本文件,就必須首先知道它所采用的編碼方案涵亏,否則用錯誤的編碼方案進(jìn)行解碼宰睡,就會出現(xiàn)亂碼。為什么電子郵件常常出現(xiàn)亂碼溯乒?就是因?yàn)榘l(fā)信人和收信人使用的編碼方案不一樣夹厌。

想象一下,如果有一種統(tǒng)一的編碼方案裆悄,將世界上所有語言字符都納入其中矛纹,每一個字符都給予一個全球獨(dú)一無二的編碼,那么亂碼問題就會消失光稼。于是或南,全球所有國家和民族使用的所有語言字符的統(tǒng)一編碼方案誕生了孩等。

最初,由多語言軟件制造商組成了統(tǒng)一碼聯(lián)盟(The Unicode Consortium)采够,然后于1991年發(fā)布了The Unicode Standard(統(tǒng)一碼標(biāo)準(zhǔn))肄方,定義了一個全球統(tǒng)一的通用字符集,習(xí)慣簡稱為Unicode字符集(Unicode常被稱為統(tǒng)一碼蹬癌、萬國碼权她、單一碼,嚴(yán)格來說逝薪,這種稱呼不夠嚴(yán)謹(jǐn)或不夠準(zhǔn)確隅要,因?yàn)閁nicode字符集只是一個編號字符集,尚未經(jīng)過字符編碼方式CEF和字符編碼模式CES進(jìn)行編碼)董济。

接著步清,ISO及IEC也于1993年聯(lián)合發(fā)布了稱之為Universal Multiple-Octet Coded Character Set(通用多八位組編號字符集,習(xí)慣翻譯為“通用多八位編碼字符集”)虏肾、簡稱為UCS (Universal Character Set通用字符集)廓啊、標(biāo)準(zhǔn)號為ISO/IEC10646-1的全球統(tǒng)一的通用字符集。

后來封豪,統(tǒng)一碼聯(lián)盟與ISO/IEC雙方都意識到世界上沒有必要存在兩套全球統(tǒng)一的通用字符集谴轮,于是進(jìn)行整合,并為創(chuàng)立一個單一的全球統(tǒng)一的通用字符集而協(xié)同工作撑毛。到Unicode 2.0時书聚,Unicode字符集和UCS字符集(ISO/IEC 10646-1)基本保持了一致。

雖然現(xiàn)在兩個項(xiàng)目仍都存在藻雌,并獨(dú)立地公布各自的標(biāo)準(zhǔn),但統(tǒng)一碼聯(lián)盟和ISO/IEC都同意保持兩者的通用字符集相互兼容斩个,并共同調(diào)整未來的任何擴(kuò)展胯杭。

目前,Unicode的知名度要比UCS知名度大得多受啥,已成了全球統(tǒng)一的通用字符集或編碼方案的代名詞做个。并且在實(shí)踐中,Unicode也要比UCS應(yīng)用得更為廣泛得多滚局。

Unicode字符集的目標(biāo)是涵蓋目前人類使用的所有字符居暖,并為每個字符分配唯一的字符編號(即碼點(diǎn)編號、碼點(diǎn)值)藤肢,一一對應(yīng)于編號空間(Code Space代碼空間太闺、碼空間、碼點(diǎn)空間)里的碼點(diǎn)(Code Point代碼點(diǎn))嘁圈。

目前省骂,Unicode字符集將所有字符按照使用上的頻繁度劃分為17個平面(Plane層面)蟀淮,每個平面上的編號空間有2^16=65536個碼點(diǎn)。將來根據(jù)需要钞澳,還可擴(kuò)展為更多平面怠惶。

其中第0個平面BMP(Basic Multilingual Plane基本多語言平面、基本多文種平面轧粟、基本平面策治、平面0),基本涵蓋了當(dāng)今世界上正在使用中的常用字符兰吟。我們平常用到的Unicode字符览妖,一般都是位于BMP平面上的。

BMP平面以外其他的增補(bǔ)平面(也稱為輔助平面、SP平面)要么用來表示一些非常特殊的字符(比如不常用的象形文字卢佣、遠(yuǎn)古時期的文字等)菱魔,且多半只有專家在歷史和科學(xué)領(lǐng)域里才會用到它們;要么被留作擴(kuò)展之用府树。目前Unicode字符集的17個平面中尚有大量編號空間未被使用。

另外料按,BMP平面有一個專用區(qū)(Private Use Zone):0xE0000xF8FF(十進(jìn)制5734463743)奄侠,共6400個碼點(diǎn),被保留為專用(私用)载矿,因而永遠(yuǎn)不會被分配給任何字符垄潮;還有一個被稱為代理區(qū)(Surrogate Zone)的特殊區(qū)域:0xD800-0xDFFF(十進(jìn)制55296~57343),共2048個碼點(diǎn)闷盔,目的是用基本平面BMP中的兩個碼點(diǎn)“代理”表示BMP以外的其他增補(bǔ)平面SP的字符(解釋詳見后文)弯洗。

image.png

Unicode字符集的字符編碼方式一開始規(guī)定用兩個字節(jié)的碼元(即16位碼元)來統(tǒng)一表示所有的字符(即UTF-16編碼方式,UTF-16編碼方式要早于UTF-8編碼方式逢勾、UTF-32編碼方式出現(xiàn)牡整,詳見后文)。

對于ASCII字符溺拱,與前面介紹的ANSI編碼一樣逃贝,Unicode也保持其原編碼不變(準(zhǔn)確地說,應(yīng)該是保持其“編號不變”迫摔,因?yàn)樵趥鹘y(tǒng)字符編碼模型中沐扳,編號與編碼不作區(qū)分,說“編碼不變”也勉強(qiáng)可以)句占,只是在UTF-16字符編碼方式中將其長度由原來的8位擴(kuò)展為16位(注意沪摄,這里的字符編碼方式CEF還只是邏輯意義上的碼元序列,不是字符編碼模式CES——物理意義上的字節(jié)序列),而其他文化和語言的字符則全部重新統(tǒng)一編碼卓起。

由于ASCII字符只需要用到UTF-16的16位編碼中的低8位和敬,所以其高8位永遠(yuǎn)是0(實(shí)際上也只用到了低8位中的低7位,因此準(zhǔn)確地說其高9位永遠(yuǎn)是0)戏阅。

在Unicode標(biāo)準(zhǔn)最初推出的UTF-16字符編碼方式中昼弟,無論是半角的英文字母,還是全角的漢字奕筐,它們都表示統(tǒng)一的“一個字符”舱痘,同時其編碼也都是統(tǒng)一的“兩個字節(jié)”(也因此UTF-16屬于雙字節(jié)碼元編碼方式,而Unicode標(biāo)準(zhǔn)在UTF-16字符編碼方式之后所推出的UTF-8字符編碼方式則屬于單字節(jié)碼元編碼方式离赫,兩者之間的關(guān)系與區(qū)別詳見后文)芭逝。

請注意這里的“字符”和“字節(jié)”兩個術(shù)語意義上的不同:“字節(jié)”是一個與計算機(jī)相關(guān)的物理意義上的8位存儲單元,而“字符”則是一個與文化相關(guān)的邏輯意義上的文字符號渊胸。

在Unicode標(biāo)準(zhǔn)推出之前旬盯,那些做多語言國際軟件的公司遇上過很大麻煩。他們?yōu)榱嗽诓煌膰忆N售同一套軟件翎猛,就不得不特別注意字符編碼的問題胖翰。不僅要處處小心不要搞錯,還要把軟件中的文字在不同的字符編碼中轉(zhuǎn)換來轉(zhuǎn)換去切厘,而Unicode標(biāo)準(zhǔn)的出現(xiàn)萨咳,提供了一個很好的一攬子解決方案。

于是從Windows NT開始疫稿,微軟趁機(jī)把操作系統(tǒng)改了一遍培他,把所有的核心代碼都改成了采用Unicode標(biāo)準(zhǔn)的版本(實(shí)際使用的就是Unicode標(biāo)準(zhǔn)的UTF-16字符編碼方式CEF所對應(yīng)的UTF-16字符編碼模式CES)。

從Windows NT開始遗座,Windows系統(tǒng)終于無需要加裝各種本土語言系統(tǒng)(比如“UCDOS希望漢字系統(tǒng)”之類的)舀凛,就可以直接顯示全世界上所有的字符了。當(dāng)然员萍,為了保持兼容性腾降,對于之前的ANSI編碼方案,Windows仍然是必須支持的碎绎。

Unicode在剛開始制訂UTF-16字符編碼時,并沒有考慮與任何一種現(xiàn)有的字符編碼保持完全兼容(與ASCII也只能算是間接兼容或者說半兼容抗果,畢竟ASCII字符的UTF-16編碼也同樣是16位的筋帖,前9位均為0),比如GBK與Unicode在漢字的編碼上完全是不一樣的冤馏,沒有任何一種簡單的算術(shù)方法可以將文本內(nèi)容在UTF-16編碼和GBK編碼之間進(jìn)行直接轉(zhuǎn)換日麸,要轉(zhuǎn)換的話只能通過查表這樣低效率的笨辦法一個字符對應(yīng)一個字符地來進(jìn)行。

即便是ASCII字符,也屬于不完全兼容代箭,因?yàn)閁TF-16也是用兩個字節(jié)來表示的墩划,雖然低7位與ASCII保持了一致,其余高位的9位均只是占位的0嗡综,但畢竟還是使用了16位共兩個字節(jié)編碼乙帮,不同于ASCII碼的單字節(jié)編碼。正是鑒于此(當(dāng)然除此之外還有其他原因)极景,于是后來又設(shè)計了UTF-8字符編碼方式察净,則保持了跟ASCII碼的完全兼容。

從字符集的角度上來講盼樟,Unicode字符集不同于ASCII這樣不能再增加字符的封閉字符集氢卡,而是一個開放的字符集,是可以不斷增加字符的晨缴。因此Unicode字符集也在不斷發(fā)展(比如隨著互聯(lián)網(wǎng)即時聊天工具的發(fā)展而流行起來的很多Emoji表情符就不斷地被增加到了Unicode字符集中)译秦,理論上支持的字符數(shù)量是沒有上限的,未來還可再擴(kuò)展击碗。

(注意筑悴,很多文章中,有時候稱字符集延都,有時候稱字符編碼方案雷猪,大致上來講,字符集與字符編碼方案經(jīng)常被視為同義詞晰房,尤其是在傳統(tǒng)字符編碼模型中求摇。但若深究起來的話,在現(xiàn)代字符編碼模型中殊者,由于字符集實(shí)際上為編號字符集的簡稱与境,因此字符編碼方案實(shí)際上涵蓋了字符集。具體可參看前面對于現(xiàn)代字符編碼模型的解釋猖吴。)

image.png

另外摔刁,與Unicode字符集基本保持兼容的ISO/IEC UCS字符集分為UCS-2(2-byte Universal Character Set)和UCS-4(4-byte Universal Character Set)兩部分,分別以2字節(jié)和4字節(jié)表示字符編號(即碼點(diǎn)編號)海蔽。

(注意共屈,UCS-2和UCS-4只是表示字符編號的字節(jié)數(shù)不同,與字符編碼方式CEF中的2字節(jié)與4字節(jié)沒有關(guān)系党窜,也因此不能分別等同于Unicode編碼方案中的UTF-16和UTF-32字符編碼方式CEF拗引,有不少文章中稱兩者等同,這是完全錯誤的幌衣。事實(shí)上矾削,由UCS-2和UCS-4所組成的UCS字符集跟Unicode字符集一樣,都可分別采用UTF-8、UTF-16和UTF-32字符編碼方式CEF對字符編號進(jìn)行編碼哼凯。)

其中欲间,UCS-2又被稱為基本多語言平面BMP(Basic Multilingual Plane),與Unicode的基本多語言平面BMP基本上保持了一致(但仍有部分不同断部,詳見后文解釋)猎贴;而UCS-4格式用四個字節(jié)中的31位來表示一個字符的碼點(diǎn)編號(即字符編號),這樣可表示21億個不同的字符(2^31=2147483648家坎;最高位為0嘱能,另有用途)。

(注:很多文章中的UCS-4這個概念實(shí)際上有廣義和狹義兩種含義上的不同虱疏,廣義UCS-4包含了UCS-2惹骂,狹義則不包含,需根據(jù)上下文作不同理解做瞪。)

不過对粪,實(shí)踐中UCS字符集應(yīng)用得不多,基本以Unicode字符集為主装蓬,因此不作詳細(xì)介紹著拭。

Unicode字符集不僅給每個字符根據(jù)其所在的碼點(diǎn)分配了一個唯一的碼點(diǎn)值(即碼點(diǎn)編號,不嚴(yán)格地來講牍帚,也勉強(qiáng)可認(rèn)為是字符編號儡遮,注意不要跟UTF-16、UTF-8等字符編號的編碼方式混淆了概念)暗赶,而且賦予了一個正式的名稱:在表示一個Unicode碼點(diǎn)編號(或UCS碼點(diǎn)編號)的十六進(jìn)制數(shù)的前面加上“U+”鄙币,稱之為碼點(diǎn)名稱,或字符名稱(考慮到有部分碼點(diǎn)實(shí)際上并未分配字符蹂随,因此應(yīng)稱為“碼點(diǎn)名稱”更為合適十嘿,但很多文章中都習(xí)慣稱為“字符名稱”)。

比如岳锁,U+0041表示英語大寫字母A绩衷,U+4E25表示漢字“嚴(yán)”。具體的字符對應(yīng)表激率,可以查詢<u style="text-decoration: none; border-bottom: 1px dashed grey;">unicode.org</u>咳燕,漢字也可查詢專門的<u style="text-decoration: none; border-bottom: 1px dashed grey;">中日韓漢字Unicode編碼表</u>。

Unicode字符集中的U+0000U+007F(即十進(jìn)制的0127)與ASCII字符集(即ISO/IEC 646標(biāo)準(zhǔn))是一致的乒躺,U+0000U+00FF(即十進(jìn)制的0255)與ISO/IEC 8859-1字符集(即Latin-1字符集)也是一致的迟郎。

(八)字符編碼方案的演變與字節(jié)序

一、字符編碼方案的演變

前文已經(jīng)提及聪蘸,編號字符集CCS(簡稱字符集)與字符編碼方式CEF(簡稱編碼方式)這兩個概念,在早期并沒有必要嚴(yán)格區(qū)分。

在Unicode編碼方案出現(xiàn)之前健爬,字符集及其具體的編碼方式是綁定耦合在一起的控乾,因此,“字符集”娜遵、“編碼”或“編碼方式”甚至“編碼方案”這幾個概念經(jīng)常相互指代蜕衡、彼此混用。

比如设拟,字符集里的字符編號(即碼點(diǎn)編號)在很多文章里也稱之為字符編碼慨仿、字符碼、碼點(diǎn)纳胧、碼位镰吆、碼點(diǎn)值、碼值等跑慕,字符編碼也稱之為編碼實(shí)現(xiàn)万皿、編碼方案、編碼方式核行、編碼格式牢硅、編碼形式、內(nèi)碼芝雪、編碼值减余、碼值(你沒看錯,字符編號與字符編碼都有可能被簡稱為“碼值”惩系,頭大了吧)位岔,等等,非城欤混亂赃承。

對于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)多種可選的編碼方式筐摘。

所以卒茬,用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ī)實(shí)際處理、存儲和傳輸時罗捎,還需再次編碼轉(zhuǎn)換為字符編碼模式CES的字節(jié)序列观谦。

字符編碼方式CEF的碼元序列可理解為字符編碼的邏輯表示形式,相對而言桨菜,字符編碼模式CES的字節(jié)序列則可理解為字符編碼在計算機(jī)中的物理表示形式豁状。

而字節(jié)序列,則涉及到了不同的字節(jié)序(Byte-Order倒得,主要分為大端序Big-Endian泻红、小端序Little-Endian)。

二霞掺、字節(jié)序

字節(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人”之意)琴昆。


image.png

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)复颈。)

為什么會存在字節(jié)序的問題?

當(dāng)然不會是像童話故事里那樣出于“無厘頭”的原因沥割,而是因?yàn)闅v史上設(shè)計不同計算機(jī)系統(tǒng)的人在當(dāng)時基于各自的理由和原因(這里的理由和原因網(wǎng)上存在著各種不同的說法耗啦,但也或許根本就沒有具體理由和原因,只是設(shè)計人員的個人偏好机杜,甚至是隨意決定的)帜讲,在各自計算機(jī)系統(tǒng)的設(shè)計上作出了不同的選擇。

字節(jié)序共分為三種:

大端序BE(Big-Endian椒拗,也稱高尾端序)似将;
小端序LE(Little-Endian,也稱為低尾端序)陡叠;
中間序ME(Middle-Endian玩郊,也稱為混合序),不太常用枉阵,本文不作介紹译红。

字節(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é)所在的那一端,而相反的那一端則是小端商乎、尾端央拖。

而要徹底講清楚大端序(高尾端序)、小端序(低尾端序)鹉戚,則需要從人讀寫二進(jìn)制數(shù)的方向和內(nèi)存地址的增長方向兩者相結(jié)合講起:

人讀寫二進(jìn)制數(shù)的方向?yàn)?這是確定不變的):左--->右鲜戒,大端/頭端/高位--->小端/尾端/低位;或上--->下抹凳,大端/頭端/高位--->小端/尾端/低位遏餐;

內(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ù)的尾端炸枣÷驳龋】

注意,這里的“數(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)呢?

因?yàn)樵诂F(xiàn)代計算機(jī)中携兵,字節(jié)是計算機(jī)數(shù)據(jù)存儲的基本單位疾掰,對于整體上的單一字節(jié)(a byte),涉及到的是其8個比特位的順序(位序徐紧、比特序静檬,由于一般直接由硬件處理,程序員大致了解即可并级,這里不深入探討)拂檩,顯然不存在字節(jié)序問題。

然而嘲碧,對于在數(shù)據(jù)類型上作為一個整體的多字節(jié)數(shù)據(jù)而言稻励,它是由各個可被單獨(dú)尋址的字節(jié)所組成的(處理器尋址的最小單位一般是1個字節(jié)),由于歷史的原因愈涩,其各個字節(jié)的存儲順序在不同的系統(tǒng)平臺(包括CPU和操作系統(tǒng))上是不同的望抽。

也就是說,如果計算機(jī)處理的數(shù)據(jù)是單字節(jié)數(shù)據(jù)類型(byte)履婉,是不存在字節(jié)序問題的煤篙,因?yàn)閱蝹€字節(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é)。

這樣一來,多字節(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)行(這一點(diǎ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)換程序)乍迄,否則通訊將會出錯,甚至直接失敗士败。

實(shí)際上闯两,int、short谅将、long等數(shù)據(jù)類型一般是高級編程語言層面的概念生蚁,更進(jìn)一步而言,這其實(shí)涉及到了機(jī)器硬件層面(即匯編語言)中的數(shù)據(jù)類型byte字節(jié)戏自、word字、dword雙字等在硬件中的表達(dá)與處理機(jī)制(實(shí)質(zhì)上字節(jié)序跟CPU寄存器的位數(shù)伤锚、存放順序密切相關(guān))擅笔。具體可參看附文:《本質(zhì)啊本質(zhì)之一:數(shù)據(jù)類型的本質(zhì)》、《寄存器與字屯援、字節(jié)》猛们。

【附:本質(zhì)啊本質(zhì)之一:數(shù)據(jù)類型的本質(zhì)

CSDN博客 博主:<u style="text-decoration: none; border-bottom: 1px dashed grey;">band_of_brothers</u> 發(fā)表于:2007-10-10 22:20

研究一個層面的問題,往往要從更深的層面找尋答案狞洋。這就如C語言與匯編弯淘、匯編與機(jī)器指令,然而終究要有個底限吉懊,這個底限以能使我們心安理得為準(zhǔn)庐橙,就好比公理之于數(shù)學(xué)、三大定律之于宏觀物理借嗽。

在這里就將機(jī)器指令作為最后的底限吧态鳖,盡管再深入下去還有微指令,但那畢竟是太機(jī)器了恶导,可以了浆竭。以下所有從C代碼編譯生成匯編代碼用的是命令:cl xxx..c /Fa /Ze。

類型的本質(zhì)

類型這個概念,好多地方都有講邦泄,但說實(shí)話删窒,你真的理解嗎?什么是類型顺囊?類型是一個抽象的概念還是一個真實(shí)的存在肌索?嗯?

開始:

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ī)器層面支持的污筷,不是軟的工闺,是硬的乍赫,有實(shí)實(shí)在在的機(jī)器碼為證。


image.png

附:寄存器與字陆蟆、字節(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ù))】

數(shù)據(jù)類型介紹到此為止

下面簡要介紹一下字節(jié)序的三種類型:

image.png

常用的操作系統(tǒng)中,linux和windows是小端法來存儲數(shù)據(jù)對象的壶冒。

image.png

另外缕题,還一種網(wǎng)絡(luò)字節(jié)序(network byte order網(wǎng)絡(luò)字節(jié)順序、網(wǎng)絡(luò)序)胖腾。

網(wǎng)絡(luò)字節(jié)順序是TCP/IP中規(guī)定好的一種數(shù)據(jù)表示格式烟零,它與具體的CPU類型、操作系統(tǒng)等無關(guān)咸作,從而可以保證數(shù)據(jù)在不同主機(jī)之間傳輸時能夠被正確解釋锨阿。IP協(xié)議中定義大端序Big Endian為網(wǎng)絡(luò)字節(jié)序。

不過记罚,容易令人困惑的是墅诡,IP協(xié)議作為網(wǎng)絡(luò)層協(xié)議,其面向的數(shù)據(jù)是報文桐智,是沒有字節(jié)的概念的书斜,也就無關(guān)字節(jié)序了。因此酵使,英文版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協(xié)議里的字節(jié)序?qū)嶋H上是用在分組頭里的數(shù)值上的焙糟,例如每個分組頭會包含源IP地址和目標(biāo)IP地址口渔,在尋址和路由的時候是需要用到這些值的。

比如穿撮,4個字節(jié)的32 bit值以下面的次序傳輸:首先是高位的0~7bit缺脉,其次8~15bit,然后16~23bit悦穿,最后是低位的24~31bit攻礼。這種傳輸次序稱作大端字節(jié)序。由于TCP/IP頭部中所有的二進(jìn)制整數(shù)在網(wǎng)絡(luò)中傳輸時都要求以這種次序栗柒,因此它又稱作網(wǎng)絡(luò)字節(jié)序礁扮。

再比如,以太網(wǎng)頭部中2字節(jié)的“以太網(wǎng)幀類型”字段,表示是的后面所跟數(shù)據(jù)幀的類型太伊。對于ARP請求或應(yīng)答的以太網(wǎng)幀類型來說虚婿,在網(wǎng)絡(luò)傳輸時腰鬼,發(fā)送的順序是以大端方式進(jìn)行的:0x08,0x06。其在內(nèi)存中的映象如下所示:
內(nèi)存低地址


0x08 -- 高位字節(jié)

0x06 -- 低位字節(jié)


內(nèi)存高地址

該字段的值為0x0806嫌变,也是以大端方式存放在內(nèi)存中的。


image.png

image.png

其實(shí)榛丢,IP報文中的很多數(shù)據(jù)都是需要做字節(jié)序轉(zhuǎn)換的馏臭,比如包長度、check sum校驗(yàn)和等名扛,這些值大都是short(16bit)或者long(32bit)型谅年,所以解析IP報文時也需要做網(wǎng)絡(luò)->主機(jī)字節(jié)序轉(zhuǎn)換,而生成報文字節(jié)流時則需要進(jìn)行主機(jī)->網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換(計算機(jī)中的字節(jié)序被稱之為主機(jī)字節(jié)序罢洲,簡稱主機(jī)序踢故;相對于網(wǎng)絡(luò)傳輸中的字節(jié)序被稱之為網(wǎng)絡(luò)字節(jié)序,簡稱網(wǎng)絡(luò)序)惹苗。

為了進(jìn)行網(wǎng)絡(luò)字節(jié)序與主機(jī)字節(jié)序的轉(zhuǎn)換殿较,BSD sockets(Berkeley sockets)提供了四個轉(zhuǎn)換的函數(shù):htons、ntohs桩蓉、htonl淋纲、ntohl,其中h是host院究、n是network洽瞬、s是short、l是long:

htons把unsigned short類型從主機(jī)字節(jié)序轉(zhuǎn)換到網(wǎng)絡(luò)字節(jié)序

htonl把unsigned long類型從主機(jī)字節(jié)序轉(zhuǎn)換到網(wǎng)絡(luò)字節(jié)序

ntohs把unsigned short類型從網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換到主機(jī)字節(jié)序

ntohl把unsigned long類型從網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換到主機(jī)字節(jié)序

在使用little endian的系統(tǒng)中业汰,這些函數(shù)會把字節(jié)序進(jìn)行轉(zhuǎn)換伙窃;在使用big endian類型的系統(tǒng)中,這些函數(shù)會定義成空宏样漆。

Windows系統(tǒng)API中也提供了類似的轉(zhuǎn)換函數(shù)为障。而在.Net中,網(wǎng)絡(luò)字節(jié)序與主機(jī)字節(jié)序兩者之間的轉(zhuǎn)換放祟,由IPAddress類的靜態(tài)方法提供:HostToNetworkOrder和NetworkToHostOrder鳍怨。】

Intel和AMD的X86平臺跪妥,以及DEC(Digital Equipment Corporation鞋喇,后與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é)序。

一般來說贫堰,大部分用戶的操作系統(tǒng)穆壕,如windows、FreeBsd其屏、Linux是Little-Endian的喇勋;少部分,如Mac OS是Big-Endian的偎行。

具體參見下表:


image.png

所以說茄蚯,Little Endian還是Big Endian與操作系統(tǒng)和CPU芯片類型都有關(guān)系。因此在一個計算機(jī)系統(tǒng)中睦优,有可能同時存在大端和小端兩種模式的現(xiàn)象。

這一現(xiàn)象為系統(tǒng)的軟硬件設(shè)計帶來了不小的麻煩壮不,這要求系統(tǒng)設(shè)計工程師必須深入理解大端和小端模式的差別汗盘。大端與小端模式的差別體現(xiàn)在一個處理器的寄存器、指令集询一、系統(tǒng)總線等各個層次中隐孽。

其實(shí)很多技術(shù)人員在實(shí)際的非跨平臺桌面應(yīng)用開發(fā)過程中都很少會直接和字節(jié)序打交道(因?yàn)橛捎布苯犹幚?癌椿,但在跨平臺及網(wǎng)絡(luò)應(yīng)用開發(fā)過程中因?yàn)樯婕暗疆悩?gòu)計算機(jī)系統(tǒng)間的通訊交流,字節(jié)序是很難回避的問題菱阵。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末踢俄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子晴及,更是在濱河造成了極大的恐慌都办,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虑稼,死亡現(xiàn)場離奇詭異琳钉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蛛倦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進(jìn)店門歌懒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人溯壶,你說我怎么就攤上這事及皂。” “怎么了且改?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵验烧,是天一觀的道長。 經(jīng)常有香客問我钾虐,道長噪窘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任效扫,我火速辦了婚禮倔监,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘菌仁。我一直安慰自己浩习,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布济丘。 她就那樣靜靜地躺著谱秽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪摹迷。 梳的紋絲不亂的頭發(fā)上疟赊,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天,我揣著相機(jī)與錄音峡碉,去河邊找鬼近哟。 笑死,一個胖子當(dāng)著我的面吹牛鲫寄,可吹牛的內(nèi)容都是我干的吉执。 我是一名探鬼主播疯淫,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼戳玫!你這毒婦竟也來了熙掺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤咕宿,失蹤者是張志新(化名)和其女友劉穎币绩,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荠列,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡类浪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了肌似。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片费就。...
    茶點(diǎn)故事閱讀 39,764評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖川队,靈堂內(nèi)的尸體忽然破棺而出力细,到底是詐尸還是另有隱情,我是刑警寧澤固额,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布眠蚂,位于F島的核電站,受9級特大地震影響斗躏,放射性物質(zhì)發(fā)生泄漏逝慧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一啄糙、第九天 我趴在偏房一處隱蔽的房頂上張望笛臣。 院中可真熱鬧,春花似錦隧饼、人聲如沸沈堡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诞丽。三九已至,卻和暖如春拐格,著一層夾襖步出監(jiān)牢的瞬間僧免,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工捏浊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留懂衩,地道東北人。 一個月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像勃痴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子热康,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評論 2 354

推薦閱讀更多精彩內(nèi)容