閑聊c/c++: 再談內(nèi)存(c/c++,java,c#,js,objc中的大小端以及裝箱拆箱和統(tǒng)一類型系統(tǒng))

2017.08.21-22.02.26-0-11.gif

本篇目的:

  • c/c++中如何判斷大小端的函數(shù)
  • c/c++中通過指針法莲组,移位法獲取多字節(jié)數(shù)據(jù)類型中的各個字節(jié)
  • java/c#/js中如何獲取多字節(jié)數(shù)據(jù)類型中的各個字節(jié)
  • 什么是裝箱和拆箱
  • 為什么要裝箱拆箱
  • java js c# objc中盡可能的避免裝箱拆箱的方法
  • 順便介紹js一些新的內(nèi)置類(有些還處于實驗性質(zhì))
  • 介紹一些simd相關(guān)知識

用c/c++實現(xiàn)測試大小端的函數(shù):

bool isLittleEdian() {
    union {
        unsigned int a;
        unsigned char b[4];
    }U;
    
    U.a = 1;

    if (U.b[0] == 1)
        return true;
    else
        return false;
}
  • 使用union關(guān)鍵來聲明一個聯(lián)合數(shù)據(jù)類型,它可以實現(xiàn):以一種數(shù)據(jù)類型存儲數(shù)據(jù),以另一種數(shù)據(jù)類型來讀取數(shù)據(jù)

例如我們用unsigned int a來進行賦值,卻使用unsgined char b[idx]數(shù)組索引來讀取某個unsigned char的數(shù)值

  • c/c++中,內(nèi)存是根據(jù)變量的順序來分配的翩活,從低到高阱洪。
    因此unsigned char b[4]在內(nèi)存中從低到高【左->右】的分布是:
    【b[0],b[1],b[2],b[3]】
內(nèi)存地址.png

閑聊c/c++: 談內(nèi)存(大/小端便贵,高/低字節(jié)菠镇,高/低地址)的結(jié)論:

  • 在小端模式中,數(shù)據(jù)的高字節(jié)保存在內(nèi)存的高地址中承璃,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的低地址

當我們U.a = 1賦值時利耍, 1 < 255,因此屬于最低字節(jié),其值保存在最低地址中(也就是b[0]的地址5831492所指向的byte中)盔粹。

因為: 低字節(jié)保存在內(nèi)存的低地址
所以: b[0] = 1

  • 在大端模式中隘梨,數(shù)據(jù)的高字節(jié)保存在內(nèi)存的低地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的高地址

因為: 低字節(jié)保存在內(nèi)存的高地址(5831495,指向的是b[3]所在的byte)
所以: b[3] = 1

應(yīng)該很清楚明了的吧舷嗡!

關(guān)鍵點:高低字節(jié)轴猎、高低地址!=选D聿薄!

除了union外中鼠,c/c++中還有多種方式從unsigned int 中獲取某個unsigned char的數(shù)值:

指針法:

//指針法
void printUInt4ComponentByPointer(unsigned int n) {
    unsigned char* ptr = (unsigned char*)(&n);
    unsigned char char0 = *ptr;   //最低地址
    unsigned char char1 = *(ptr + 1); //指針移動方式
    unsigned char char2 = ptr[2]; //使用[]變址操作符
    unsigned char char3 = ptr[3]; //使用[]變址操作符可婶,最高地址

    printf("[%u,%u,%u,%u]\n",char3,char2,char1,char0);
}

移位法:

void printUInt4ComponentByShift(unsigned int n) {

    unsigned char bytes[4];
    bytes[0] = (n >> 24) & 0xFF;
    bytes[1] = (n >> 16) & 0xFF;
    bytes[2] = (n >> 8) & 0xFF;
    bytes[3] = n & 0xFF;

    printf("[%u,%u,%u,%u]\n", bytes[0], bytes[1], bytes[2], bytes[3]);
}
//測試:
printUInt4ComponentByPointer(257);
printUInt4ComponentByShift(257);
pointer_shift_test.png

在java,c#(事實上c#在unsafe{}中支持指針操作的),js等這些語言支持移位操作援雇,只能使用移位法進行分量的提取操作(或許每個語言類庫有提供相關(guān)操作矛渴,請自己查api文檔)

public class Main {

    static void printUInt4ComponentByShift( int n) {

        byte[] bytes = new byte[4];

        bytes[0] = (byte)((n >> 24) & 0xFF);
        bytes[1] = (byte)((n >> 16) & 0xFF);
        bytes[2] = (byte)((n >> 8) & 0xFF);
        bytes[3] = (byte)(n & 0xFF);
        System.out.printf("[%d,%d,%d,%d]\n",bytes[0],bytes[1],bytes[2],bytes[3]);
    }

    public static void main(String[] args) {
        printUInt4ComponentByShift(257);//[0,0,1,1]
    }
}
  • 用java舉例子,其他語言都一樣(上面代碼稍作修改惫搏,用于c#,js,...等語言具有同等效果)
  • java中char是2個字節(jié)具温,表示一個utf16字符。byte才是一個字節(jié)筐赔,是值類型
  • 大寫的Byte是引用類型铣猩,用于容器存儲時的裝箱使用。自jdk1.5開始引入自動裝箱拆箱川陆。
  • 事實上剂习,java中,所有的基本數(shù)據(jù)類型都有對應(yīng)的首字母大寫的同名的類较沪,例如char->Char/byte->Byte.........,就是為了裝箱拆箱使用

裝箱(boxing)與拆箱(unboxing)

1. 什么是裝箱拆箱:

裝箱: 將值類型轉(zhuǎn)換為引用類型
拆箱: 將引用類型轉(zhuǎn)換為值類型

Integer i = 100; //裝箱
int j = i; //拆箱

用java代碼來分析一下:

  • 因為Integer是引用類型鳞绕,而100字面量是一個數(shù)值類型
    將一個數(shù)值類型賦值給引用類型時,就發(fā)生了裝箱

  • 這段代碼只能在jdk1.5以后使用尸曼,因為這是自動裝箱拆箱

  • 如果在jdk1.5之前们何,只能使用 Integer i = new Integer(100)以及int j = i.intValue()

  • 裝箱的流程如下:

    首先: 因為100是數(shù)值類型,因此內(nèi)存是分配在內(nèi)

    然后: 使用new 操作符控轿,查詢整個冤竹,是否有連續(xù)的拂封,可以分配sizeof(Integer)的內(nèi)存塊,如果有鹦蠕,則返回Integer的首地址,如果沒有連續(xù)容納的內(nèi)存塊冒签,就需要不停的往下查找,直到找到連續(xù)內(nèi)存塊才返回钟病,否則就報out of memory錯誤

    最后: 將棧上分配的100賦值給堆分配的Integer中的成員變量萧恕,完成裝箱工作

  • 拆箱的流程正好和裝箱相反

  • 由此可見,裝箱拆箱涉及到堆和棧的內(nèi)存分配肠阱,內(nèi)存拷貝以及內(nèi)存析構(gòu)票唆,是一個很耗時的操作!

  • 一次的裝箱拆箱不可怕屹徘,可怕的是成千上萬的數(shù)據(jù)需要拆箱裝箱走趋。例如java容器類,裝填的是Object噪伊,因此每次add基本數(shù)據(jù)類型時候簿煌,需要裝箱,每次get(i)基本數(shù)據(jù)類型時酥宴,需要拆箱啦吧,當每次你循環(huán)成千上萬個數(shù)據(jù)時,進行成千上萬次的裝箱或拆箱行為拙寡。js,objc也是如此授滓,c#比較特別,我們下面來說明肆糕!

2. 為什么要裝箱拆箱:

原因: 統(tǒng)一類型系統(tǒng)
來自.net本質(zhì)論的解釋:
裝箱與拆箱是我們能夠統(tǒng)一的來考察類型系統(tǒng)般堆,其中任何類型的值都可以按對象處理

  • java中所有的類都繼承自O(shè)bject
  • .net中,公共語言運行庫中的類都繼承自O(shè)bject
  • objc中诚啃,所有的類都繼承自NSObject
  • js中淮摔,所有一切都是object

所以不可避免的,這些語言都會產(chǎn)生裝箱和拆箱行為始赎,這是由其本質(zhì)所決定的:统取!T於狻魔招!

對于c#來說,比較特別五辽。在c#2.0之前办斑,和java一樣,需要裝箱和拆箱。但是c#2.0引入了泛型機制乡翅,而且c#本身支持struct定義值類型鳞疲,class定義引用類型,這些機制可以避免裝箱拆箱蠕蚜。因此在c#中尚洽,現(xiàn)在肯定都是用泛型容器類。

c#非泛型容器類則只能存儲object類型波势,和java一樣會自動進行裝箱拆箱工作翎朱。(其實可以查看java/.net中間語言代碼,就會非常清晰的了解整個機制和流程

隨便說一下尺铣,java的泛型系統(tǒng)到目前為止還是所謂的"擦除式"泛型系統(tǒng),也就是在編寫的時候争舞,是用泛型的方式寫代碼凛忿,但實際在生成字節(jié)碼運行時的時候,泛型表示全部變?yōu)閛bject類型表示竞川,因此也是無法消除裝箱拆箱的行為店溢。

為什么java會如此,很簡單委乌,為了兼容性床牧,Java虛擬機使用廣泛,而裝箱拆箱需要重寫底層機制遭贸,導致不兼容性8昕取!

.net是看到了java的不良之處壕吹,從一開始設(shè)計時候就考慮好了著蛙,在.net 2.0之前雖然沒支持泛型,但是struct/class可是一開始就已經(jīng)擁有了耳贬!

3. 如何避免裝箱拆箱?

  • c#中直接使用泛型類
  • objc中.m支持c語言踏堡,.mm支持c++,所以實在要追求效率咒劲,就使用c語言或c++ stl庫顷蟆,clang還是非常棒的東西!腐魂!facebook pop即使這么干的帐偎,很棒的庫,對吧挤渔!
  • java中肮街,覺得好像沒什么可能性了,即使用ndk判导,除非你全部使用ndk編寫嫉父,否則即使jni導出給java使用沛硅,也面臨這c++數(shù)據(jù)類型到j(luò)ava數(shù)據(jù)類型的包裝。所以并不合算
  • 來聊一下js吧绕辖,js一定會成為王者之劍∫〖。現(xiàn)在的js中增加了很多基本數(shù)據(jù)類型的Array內(nèi)置對象:
js基本數(shù)據(jù)類型數(shù)組.png

沒驗證過,但是90%的可能不會產(chǎn)生裝箱和拆箱行為仪际!

simd_data_type.png

simd-單指令多數(shù)據(jù)流指令集围小,游戲中的必備技術(shù)。16byte字節(jié)對齊(續(xù)談內(nèi)存树碱,我們來聊字節(jié)對齊和padding)肯适,特適合3D數(shù)學表示。(關(guān)于simd方面的參考資料成榜,最好的就是id soft(發(fā)表在intel的5篇論文框舔,詳細描述了Doom3中simd數(shù)學庫的性能優(yōu)化及測試,約翰卡馬克就是3D引擎之神,幾年前Doom3已經(jīng)開源了赎婚,可以在github中下載到刘绣,simd數(shù)學庫可以參考:Doom3 數(shù)學庫以及微軟的XNA Math庫,也是Simd實現(xiàn)

還有就是增加了紅黑色之類的數(shù)據(jù)結(jié)構(gòu)挣输。由此可見纬凤,js所圖宏大啊撩嚼!一定要跟蹤js的發(fā)展停士!

今天到此為止,下一篇我們聊一下nodejs的核心庫libuv中的幾個核心宏绢馍,你會看到那是多么的美妙向瓷!

一本touch到統(tǒng)一類型系統(tǒng)本質(zhì)的書.jpg

非常有深度,信息量巨大的一本書=⒂俊猖任!


.Net 本質(zhì)論作者: Don Box: 也是COM本質(zhì)論的作者
"關(guān)于COM,沒有任何人能闡釋得比Don Box更棒!"
這是微軟"COM guy" Charlie Kindel對Don Box的評價!
.net以前被稱為"COM+"


ID soft發(fā)布在intel上的論文瓷耙,一共7篇朱躺,詳細的測試,實現(xiàn)過程描述搁痛,涉及骨骼動畫长搀,陰影計算,骨骼動畫流水線等等鸡典,其實整個3D都是建立在數(shù)學基礎(chǔ)上的源请。因此如果想搞圖形開發(fā),這幾篇論文絕對就是最好的選擇!谁尸!
https://software.intel.com/en-us/articles/optimizing-the-rendering-pipeline-of-animated-models-using-the-intel-streaming-simd-extensions

我在unity3d中實現(xiàn)了Doom3場景地圖的渲染和Doom3 MD5骨骼動畫渲染舅踪,其實最終目的就是為了js webgl demo(我的閑聊js系列文章),兩個無頭男人是MD5骨骼動畫,一個靜態(tài)良蛮,一個運動抽碌,而小美女是著名的她:

unity_girl.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市决瞳,隨后出現(xiàn)的幾起案子货徙,更是在濱河造成了極大的恐慌,老刑警劉巖皮胡,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件痴颊,死亡現(xiàn)場離奇詭異,居然都是意外死亡胸囱,警方通過查閱死者的電腦和手機祷舀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來烹笔,“玉大人,你說我怎么就攤上這事抛丽“埃” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵亿鲜,是天一觀的道長允蜈。 經(jīng)常有香客問我,道長蒿柳,這世上最難降的妖魔是什么饶套? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮垒探,結(jié)果婚禮上妓蛮,老公的妹妹穿的比我還像新娘。我一直安慰自己圾叼,他們只是感情好蛤克,可當我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著夷蚊,像睡著了一般构挤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上惕鼓,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天筋现,我揣著相機與錄音,去河邊找鬼。 笑死矾飞,一個胖子當著我的面吹牛一膨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凰慈,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼汞幢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了微谓?” 一聲冷哼從身側(cè)響起森篷,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎豺型,沒想到半個月后仲智,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡姻氨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年钓辆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肴焊。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡前联,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出娶眷,到底是詐尸還是另有隱情似嗤,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布届宠,位于F島的核電站烁落,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏豌注。R本人自食惡果不足惜伤塌,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望轧铁。 院中可真熱鬧每聪,春花似錦、人聲如沸属桦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽聂宾。三九已至果善,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間系谐,已是汗流浹背巾陕。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工讨跟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鄙煤。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓晾匠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親梯刚。 傳聞我的和親對象是個殘疾皇子凉馆,可洞房花燭夜當晚...
    茶點故事閱讀 43,494評論 2 348

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