本篇目的:
- 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]】
閑聊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);
在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)置對象:
沒驗證過,但是90%的可能不會產(chǎn)生裝箱和拆箱行為仪际!
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中的幾個核心宏绢馍,你會看到那是多么的美妙向瓷!
非常有深度,信息量巨大的一本書=⒂俊猖任!
.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)良蛮,一個運動抽碌,而小美女是著名的她: