• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            Dict.CN 在線詞典, 英語(yǔ)學(xué)習(xí), 在線翻譯

            學(xué)海苦作舟,書(shū)山勤為徑

            留下點(diǎn)回憶

            常用鏈接

            統(tǒng)計(jì)

            積分與排名

            Denoise

            English study

            Web技術(shù)

            數(shù)據(jù)壓縮

            一些連接

            最新評(píng)論

            編寫(xiě)跨平臺(tái)的軟件入門(mén)

            一,???????????? 為什么要跨平臺(tái)?

            你想過(guò)把你的 Windows 上編寫(xiě)的程序在 Linux 編譯運(yùn)行嗎,以及在 Mac 或其他 OS 上運(yùn)行等等?反過(guò)來(lái)也一樣?這就需要涉及到跨平臺(tái)編程知識(shí)。這里需要注意的是,平時(shí)很多在一個(gè)平臺(tái)運(yùn)行的程序在跨平臺(tái)的時(shí)候變的不再正確。

            Java 并非真的是跨平臺(tái)的開(kāi)發(fā)環(huán)境,它是運(yùn)行在它自己的平臺(tái)上。這里主要關(guān)注 C C++ 的跨平臺(tái)開(kāi)發(fā)。

            下面主要就幾個(gè)方面來(lái)討論跨平臺(tái)編程的注意事項(xiàng):

            1.? 字節(jié)序

            2.? 字節(jié)填充

            3.? 其他

            二,???????????? 字節(jié)序

            大家都知道計(jì)算機(jī)使用兩種字節(jié)序,一種是 little-endian ,另一種是 big-endian 。這主要是由于當(dāng)前流行的 CPU 之間的差異造成的,基本上是 IBM-PowerPC 使用的大序,而其他 CPU 使用的小序。

            這里先來(lái)介紹一下 little-endian big-endian 之間的具體差異。

            X86 指令集合使用小序( little-endian )字節(jié)順序;這就意味著多個(gè)字節(jié)值的最重要字節(jié)在地址的最低位。小序很早就使用,因?yàn)橛布菀讓?shí)現(xiàn),但和今天的制造商技術(shù)有點(diǎn)不同;但在第一代 IBM PC 機(jī)的 Vaxen 8086 處理器使用是它如此流行的主要原因。

            看一個(gè)例子:

            short example[2] = {0x0001,0x3002};

            ?

            按照 16 進(jìn)制的形式來(lái)顯示上面數(shù)據(jù)在內(nèi)存中的存儲(chǔ)方式:

            01 00 02 03

            我們看到對(duì)于數(shù)組的第一個(gè)元素,高 8 位應(yīng)該是 0 ,而最終存儲(chǔ)的時(shí)候是在低 8 位的后面。

            而另一方面 PowerPC Sparc 芯片是 big-endian 的,也就是說(shuō),最重要的字節(jié)存儲(chǔ)在較低的地址。對(duì)于 CPU 需要額外的電路實(shí)現(xiàn)這個(gè)功能,但對(duì)于今天的處理器技術(shù)與緩存控制技術(shù)相比較顯的微不足道。使用 BIG-ENDIAN 的最大好處是在使用低級(jí)調(diào)式器時(shí)比較容易理解數(shù)據(jù)的存儲(chǔ),同樣對(duì)于文件十六進(jìn)制 DUMP 或網(wǎng)絡(luò) Sniffer 顯示也是一樣的。

            對(duì)于 BIG-ENDIAN ,上面的例子中內(nèi)存如下表示:

            00 01 03 02

            這里需要注意的是:由于 BIG-ENDIAN 格式的 RAW 數(shù)據(jù)比較容易調(diào)式,如果我們有機(jī)會(huì)設(shè)計(jì)一個(gè)新的文件格式,那么使用 BIG-ENDIAN 格式,而不是根據(jù) CPU 架構(gòu)來(lái)決定。

            下面看幾個(gè)關(guān)于字節(jié)序的問(wèn)題:

            1.? Long 型指針和 char 指針之間的轉(zhuǎn)換

            看下面這段代碼

            unsigned long value = 0x03020100;

            unsigned long *ptr = &value;

            unsigned char charVal;

            charVal = *(unsigned char *)ptr;

            程序的含義比較簡(jiǎn)單,主要是從一個(gè)指向 long 的指針強(qiáng)制轉(zhuǎn)換為一個(gè)指向 char 的指針,這里假設(shè)指針指向的是最不重要的字節(jié)地址。

            在一個(gè) little-endian 處理器上, charVal 0 ,而在一個(gè) big-endian 處理器上, charVal 的值是 3 。這樣的問(wèn)題是最難以發(fā)現(xiàn)的問(wèn)題之一。

            為了避免這個(gè)錯(cuò)誤,使用一個(gè)臨時(shí)變量可以解決這個(gè)問(wèn)題,如下:

            unsigned long temp = *ptr;

            charVal = (unsigned char)temp;

            上面的第二行代碼就保證將在任何架構(gòu)上都將最不重要的字節(jié)傳遞給 charVal ;編譯器處理具體的細(xì)節(jié)。

            2.? 讀寫(xiě)文件和寫(xiě)網(wǎng)絡(luò)數(shù)據(jù)

            在從文件讀數(shù)據(jù)或?qū)憯?shù)據(jù)到文件的時(shí)候以及網(wǎng)絡(luò),對(duì)于字節(jié)順序的處理一定要小心;一定記住不能將多個(gè)字節(jié)的數(shù)據(jù)寫(xiě)到文件或網(wǎng)絡(luò)上;例如:

            long val = 1;

            int result = write(fileDes,&val,sizeof(val));

            這段代碼在 little-endian big-endian 機(jī)器上執(zhí)行的結(jié)果是不一樣的,如果讀數(shù)據(jù)的時(shí)候使用如下代碼:

            long val ;

            int result = read(fileDes,&val,sizeof(long));

            如果這兩段代碼分別位于 little-endian big-endian 機(jī)器上,那么最終得到的 val 不是 1 ,而是 0x01000000

            解決多字節(jié)的讀寫(xiě)有很多辦法,這里提供兩種。

            方法 1

            寫(xiě)的代碼

            long val = 1;

            char buf[4];

            buf[0] = 0xff&val;

            buf[1] = (0xff00&val)>>8;

            buf[2] = (0xff0000&val)>>16;

            buf[3] = (0xff000000&val)>>24;

            int result = write(fileDes,buf,4);

            讀的代碼

            long val;

            char buf[4];

            int result = read(fileDes,buf,4);

            val = buf[0]|(buf[1]<<8)|(buf[2]<<16)|(buf[3]<<24);

            3.? 運(yùn)行時(shí)檢查字節(jié)順序

            bool gIsBigEndian;

            void InitializeEndianFlag()

            {

            Short one = 1;

            Char *cp = (char *)&one;

            If(*cp == 0)

            ??? gIsBigEndian = true;

            else

            ??? gIsBigEndian = false;

            return ;

            }

            4.? 字節(jié)交換對(duì)性能的影響

            由于字節(jié)順序的問(wèn)題導(dǎo)致在處理的時(shí)候需要進(jìn)行字節(jié)交換或類(lèi)似 2 中方法 1 的處理,這里稱(chēng)為交換。通常情況下,做字節(jié)順序的交換并不影響,因?yàn)榻粨Q兩個(gè)字節(jié)或四個(gè)字節(jié)值只需要很少的 CPU 指令,并且完全可以在寄存器中執(zhí)行。

            但如果有很多數(shù)據(jù)需要交換,例如:一個(gè) 1024*768 位圖的圖像,在這么大的循環(huán)中執(zhí)行是影響性能的。

            另外對(duì)于 3 的運(yùn)行時(shí)檢查字節(jié)序的代碼要查看具體的位置。如果僅僅調(diào)用一次或幾次,不會(huì)影響性能,如果對(duì)于上面的這個(gè)循環(huán)中調(diào)用,對(duì)性能的影響是顯著的,這個(gè)時(shí)候可以使用一個(gè)預(yù)編譯宏來(lái)分別處理。例如:

            #ifdef BIG_ENDIAN//big-endian

            #else//little-endian

            #endif//BIG_ENDIAN

            ?

            三,???????????? 字節(jié)填充

            另一個(gè)寫(xiě)可移植代碼的注意點(diǎn)是結(jié)構(gòu)體的字節(jié)對(duì)齊和填充。通常,在單個(gè)平臺(tái)上,如果需要保存一個(gè)結(jié)構(gòu)體到文件,那么是作為一個(gè)整體寫(xiě)到文件的,如下:

            struct myStruct{

            char theChar;

            long theLong;

            };

            struct myStruct foo;

            foo.the Char = 1;

            foo.theLong = 2;

            如果我們已經(jīng)將數(shù)據(jù)按照 big-endian 進(jìn)行了交換,然后直接將整個(gè)結(jié)構(gòu)體寫(xiě)到文件中。那么什么樣的數(shù)據(jù)會(huì)被寫(xiě)到磁盤(pán)上呢?

            int result = write(fileDes, &foo, sizeof(foo));

            實(shí)際上我們不知道具體寫(xiě)了什么數(shù)據(jù),因?yàn)槲覀冞€不知道這個(gè)代碼在什么平臺(tái)上運(yùn)行;實(shí)際上上面的 code 中會(huì)將垃圾數(shù)據(jù)寫(xiě)到文件里,垃圾數(shù)據(jù)多少由 foo 分配到的內(nèi)存決定的。

            一種可能我們認(rèn)為的情況是:

            ?01 00 00 00 02

            但我們可能得到的這樣的數(shù)據(jù):

            01 f 8 00 00 00 02

            甚至是:

            01 e6 a7 20 00 00 00 02

            這里到底發(fā)生了什么? sizeof(foo) 是編譯器和處理器依賴(lài)的。

            有些處理器不能從某些位置讀或?qū)懚鄠€(gè)字節(jié);幾乎所有的都不能從奇數(shù)地址來(lái)讀數(shù)據(jù)。通常他們只讀那些是 sizeof value )倍數(shù)的地址;對(duì)于四個(gè)字節(jié)只能讀地址是 4 個(gè)字節(jié)的倍數(shù),對(duì)于 2 個(gè)字節(jié)的 short 只能讀兩個(gè)字節(jié)倍數(shù)的地址。如果不遵從這個(gè)字節(jié)對(duì)齊的規(guī)律,處理器會(huì)拋出一個(gè)異常并且終止程序,有些系統(tǒng)上會(huì)鎖定機(jī)器(如果發(fā)生在 kernel 中)。

            有時(shí),讀沒(méi)有對(duì)齊的數(shù)據(jù)需要花費(fèi)額外的時(shí)間。例如: PowerPC 能夠讀任何偶數(shù)地址,但對(duì)于那些不能被 4 整除的地址需要耗費(fèi)額外的總線周期。為了讀一個(gè) long 數(shù)值( value )在 2 整除而不是 4 整除的地址,它將讀四個(gè)字節(jié)并包括需要讀的值的上面兩個(gè)字節(jié),拋棄 2 個(gè)字節(jié),然后讀另外四個(gè)包含 value 2 個(gè)字節(jié)的字節(jié),同樣拋棄另外兩個(gè)。這與讀 4 個(gè)字節(jié)對(duì)齊的地址相比需要多訪問(wèn)一次緩存。

            為了達(dá)到字節(jié)對(duì)齊的目的,編譯器會(huì)插入未命名的填充字節(jié)到結(jié)構(gòu)體中。至于插入幾個(gè)字節(jié)是通過(guò)編譯器和 OS 或庫(kù)內(nèi)存分配器一起決定的。

            Windows VC 編譯器中,可以使用 #pragma 來(lái)指定字節(jié)對(duì)齊的方式。

            總而言之,在定義結(jié)構(gòu)的時(shí)候要按照字節(jié)邊界對(duì)齊來(lái)定義,一般按照 4 個(gè)字節(jié),如果不夠就需要增加填充字段。

            另外對(duì)于結(jié)構(gòu)體寫(xiě)文件或輸出到網(wǎng)絡(luò)上,最好的辦法是按照成員來(lái)逐個(gè)寫(xiě)入或發(fā)送,這可以避免將垃圾數(shù)據(jù)存放到文件中或傳輸?shù)骄W(wǎng)絡(luò)上。

            ?

            四,???????????? 其他

            下面是幾個(gè)筆者在實(shí)際編寫(xiě)代碼中發(fā)生過(guò)的錯(cuò)誤,這里與大家一道分析一下。

            1.???????? 示例 1

            for(int i = 0;i<1000;i++)

            {

            ?? ….

            }

            ...

            for(int i = 0;i<1000;i++)

            {

            ...

            }

            上面這段代碼是很普通的 C++ 代碼,但這段代碼不一定可以在所有的編譯器中都能編譯通過(guò)。主要的原因在于變量 i 的聲明。

            C++ 標(biāo)準(zhǔn)說(shuō):在 for 循環(huán)內(nèi)部聲明的變量在 for 結(jié)束的時(shí)候無(wú)效,因此可以連續(xù)使用再次在 for 循環(huán)中使用該記數(shù)器變量。但很不幸的是很多編譯器都提供編譯選項(xiàng)來(lái)讓你覺(jué)得變量是否在 for 循環(huán)以后仍然有效。 VC 中默認(rèn)編譯選項(xiàng) /Ze 用來(lái)指定 for 循環(huán)變量的局部性,但并非所有的編譯器都是將這個(gè)選項(xiàng)作為默認(rèn)編譯參數(shù);所以為了能讓你的代碼可以在任意平臺(tái)編譯通過(guò),使用 C 風(fēng)格的會(huì)有保證一點(diǎn);如下:

            int i = 0;

            for(i = 0;i<1000;i++)

            {

            ?? ….

            }

            ...

            for(i = 0;i<1000;i++)

            {

            ...

            }

            ?

            2.???????? 示例 2 int 型變量的使用

            Int 型變量是一個(gè)奇怪的東西,它在 16 位機(jī)器上是 2 個(gè)字節(jié),在 32 位機(jī)上是 4 個(gè)字節(jié);將來(lái)可能在 64 位機(jī)上是 8 個(gè)字節(jié)。所以如果你的代碼中有對(duì) int 的使用,而你想代碼可以在很多平臺(tái)上運(yùn)行,那么一定要注意了。看一下下面的情況:

            for(int i = 0;i<65540;i++)

            {

            ?? ….

            }

            這個(gè)代碼可能在不同的平臺(tái)上得到不同的結(jié)果。如果這個(gè)代碼是在 16 位機(jī)器上運(yùn)行,那么得到的結(jié)果與 32 位機(jī)器上可能不同。

            同樣在使用 int 型變量寫(xiě)文件和輸出到網(wǎng)絡(luò)時(shí)都要小心這個(gè)問(wèn)題。最好的辦法是,在這些情況下不要使用 int 型變量; int 型變量?jī)H僅在程序內(nèi)部使用。

            3.???????? 關(guān)于 Bit field 的問(wèn)題

            C 語(yǔ)法中有 bit field 的語(yǔ)法,可以根據(jù)需要來(lái)定義一個(gè)符號(hào)具體占用的 bit 數(shù),例如:

            typedef struct tagTest
            {
            ???char a:4;
            ?? char b:2;
            ?? char c:2;
            }TagTest,*PTagTest;

            實(shí)際上 tagTest 的字節(jié)數(shù)是 1 個(gè)字節(jié),成員 a 占用 4 位, b 和各占用兩位。這樣的好處是可以針對(duì)每個(gè)成員賦值而設(shè)置指定的位的值,例如:

            tagTest myTest;
            myTest.a = 10;
            myTest.b = 2;
            myTest.c = 1;

            假如你在 Windows 上是使用 VC 來(lái)編譯連接上面的程序,不管如何處理,你不會(huì)發(fā)生任何問(wèn)題。但現(xiàn)在我們假設(shè)將 myTest 放入緩沖區(qū)中,然后在 MAC 機(jī)器上取出來(lái),那么會(huì)發(fā)生什么來(lái)?看代碼:

            Windows:

            char buf[10];

            buf[0] = myTest;

            buf[2]=...

            int result = send(fd,buf,10,..);

            ?

            MAC:

            char buf[10];

            int ret = 0;

            int result = recv(fd,buf,10,..);

            PTagTest pTest = (PTagTest)&buf[0];

            ?

            if(pTest->a == 10)

            ?? ret = 1;

            else

            ??? ret = 0;

            ...

            那么 ret 的值是什么呢?我們期望是 1 但,結(jié)果不是 1 。如果你通過(guò)調(diào)試器來(lái)觀察一下 pTest 各成員的值你發(fā)現(xiàn):

            pTest->a = 6; pTest->b =2 ; pTest->c =2;

            細(xì)心的讀者可能發(fā)現(xiàn)這里的問(wèn)題所在,原因在于不同的編譯器對(duì) bit field 進(jìn)行了不同的處理。在 Windows 平臺(tái)上, c 被放在字節(jié)的最高兩位,而 a 被放在字節(jié)的最低 4 位,在 MAC 上正好相反。但一定要注意,這是編譯器行為,而不是數(shù)據(jù)在傳輸過(guò)程中發(fā)生了字節(jié)的位交換。在 Windows 發(fā)送到網(wǎng)絡(luò)的時(shí)候, buf[0] 的內(nèi)容二進(jìn)制表示為:

            01 10 1010

            MAC recv 之后, buf[0] 的內(nèi)容仍然與上面的相同。

            為了避免這個(gè)問(wèn)題,請(qǐng)不要在寫(xiě)文件或網(wǎng)絡(luò)輸出的時(shí)候使用 BIT FILED 語(yǔ)法,如果一定要使用請(qǐng)注意編譯器對(duì)位處理的區(qū)別。

            n???????? 小結(jié)

            其實(shí)實(shí)際工作中,大家認(rèn)為自己的代碼都不需要在多個(gè)平臺(tái)上運(yùn)行,而認(rèn)為跨平臺(tái)編碼與自己無(wú)關(guān);其實(shí)不然,好的編碼習(xí)慣是慢慢養(yǎng)成的,如果大家都知道這些跨平臺(tái)編碼的細(xì)節(jié),在開(kāi)始寫(xiě)代碼的時(shí)候就開(kāi)始避免這樣的問(wèn)題,一旦有一天我們的代碼需要跨平臺(tái)運(yùn)行或一點(diǎn)我們要寫(xiě)跨平臺(tái)代碼時(shí),我們就不會(huì)無(wú)從下手,而是順其自然,因?yàn)槲覀円呀?jīng)具備了這樣的習(xí)慣。

            當(dāng)然這里的介紹只是一個(gè)開(kāi)始,跨平臺(tái)編碼涉及的問(wèn)題還很多,由于筆者經(jīng)驗(yàn)的限制不能一一描述。

            ?

            本文參考: http://www.goingware.com/tips/getting-started/

            posted on 2006-11-23 10:12 笨笨 閱讀(2290) 評(píng)論(4)  編輯 收藏 引用 所屬分類(lèi): 編碼

            評(píng)論

            # re: 編寫(xiě)跨平臺(tái)的軟件入門(mén) 2006-11-25 15:55 Fany

            謝謝,好東西啊。  回復(fù)  更多評(píng)論   

            # re: 編寫(xiě)跨平臺(tái)的軟件入門(mén) 2006-11-29 11:37 笨笨

            謝謝  回復(fù)  更多評(píng)論   

            # re: 編寫(xiě)跨平臺(tái)的軟件入門(mén) 2006-12-01 23:15 xiaoE

            牛人~
            我還沒(méi)有入門(mén)  回復(fù)  更多評(píng)論   

            # re: 編寫(xiě)跨平臺(tái)的軟件入門(mén) 2007-06-13 23:54 nines

            寫(xiě)得比較好  回復(fù)  更多評(píng)論   

            久久综合久久综合久久| 少妇人妻综合久久中文字幕| 国内精品伊人久久久久AV影院| 久久人人爽爽爽人久久久| 久久水蜜桃亚洲av无码精品麻豆| AV无码久久久久不卡蜜桃| 亚洲精品国产成人99久久| 久久久久国产精品三级网| 欧美日韩久久中文字幕| 久久棈精品久久久久久噜噜| 久久99精品国产麻豆婷婷| 狠狠色狠狠色综合久久| 亚洲一区中文字幕久久| 99久久综合国产精品免费| 91精品国产综合久久香蕉 | 久久久综合九色合综国产| 精品久久久无码中文字幕| 久久精品国产亚洲AV电影| 亚洲国产成人乱码精品女人久久久不卡| 久久精品国产亚洲AV大全| 国产精品久久久久久久app | 亚洲欧美一级久久精品| 99久久夜色精品国产网站| 国产aⅴ激情无码久久| 无码任你躁久久久久久| 午夜不卡888久久| 久久国产免费观看精品3| 99久久国产亚洲综合精品| 日本久久久久久久久久| 国产高清美女一级a毛片久久w| 久久精品国产亚洲av水果派| 午夜精品久久久久久久久| 99精品久久精品一区二区| 久久久久久亚洲精品影院| 亚洲国产成人久久综合一区77| 久久精品一区二区影院| 久久久久国产一区二区| 久久久99精品成人片中文字幕| 久久久久国产精品三级网| 久久AAAA片一区二区| 欧美精品一区二区久久|