• <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>
            隨筆 - 74, 文章 - 0, 評(píng)論 - 26, 引用 - 0
            數(shù)據(jù)加載中……

            新時(shí)代的門前 32位世界中的64位編程

            為16、32、64位架構(gòu)編寫可移植代碼

              與16位相比,32位意味著程序更快、可直接尋址訪問(wèn)更多的內(nèi)存和更好的處理器架構(gòu)。鑒于此,越來(lái)越多的程序員已經(jīng)開(kāi)始考慮利用64位處理器所帶來(lái)的巨大優(yōu)勢(shì)了。

              克雷研究(Cray Research 應(yīng)為品牌名)計(jì)算機(jī)已經(jīng)開(kāi)始使用64位字,并可訪問(wèn)更大的內(nèi)存地址。然而,作為正在向開(kāi)發(fā)標(biāo)準(zhǔn)軟件和與其他操作系統(tǒng)相互努力的一部分,我們已經(jīng)停止了移植那些原本基于32位處理器的代碼。事實(shí)上,我們不斷遇到我們稱之為“32位主義”的代碼,這些代碼都是在假定機(jī)器字長(zhǎng)為32位的情況下編寫的,因而很難移植這種代碼,所以必須確立一些簡(jiǎn)單的指導(dǎo)方針,以便助于編寫跨16、32、64位處理器平臺(tái)的代碼。

              由于有一些遺留問(wèn)題,C語(yǔ)言在數(shù)據(jù)類型和數(shù)據(jù)構(gòu)造方面,顯得有點(diǎn)過(guò)剩了。可以使用的不僅僅是char、short、int和long類型,還有相應(yīng)的unsigned(無(wú)符號(hào))類型,當(dāng)然你可在結(jié)構(gòu)(structure)和聯(lián)合(union)中混合使用,可以在聯(lián)合中包含結(jié)構(gòu),再在結(jié)構(gòu)中包含聯(lián)合,如果還嫌數(shù)據(jù)類型不夠復(fù)雜,還可以轉(zhuǎn)換成比特位,當(dāng)然也可以把一種數(shù)據(jù)類型轉(zhuǎn)換成另一種你想要的數(shù)據(jù)類型。正是因?yàn)檫@些工具太強(qiáng)大,如果不安全地使用它們,就有可能會(huì)傷到自己了。

              高級(jí)代碼的高級(jí)結(jié)構(gòu)

              在Kernighan和Plauger經(jīng)典的《The Elements of Programming Style》一書(shū)中,他們的建議是“選擇使程序看上去簡(jiǎn)單的數(shù)據(jù)表示法”。對(duì)個(gè)人而言,這意味著為高級(jí)編程使用高級(jí)數(shù)據(jù)結(jié)構(gòu),而低級(jí)編程使用低級(jí)數(shù)據(jù)結(jié)構(gòu)。

              在我們移植的程序中,有一個(gè)常見(jiàn)的32位主義bug,不如拿它來(lái)做個(gè)例子,科內(nèi)爾大學(xué)編寫的用于網(wǎng)間互訪的路由協(xié)議引擎,在伯克利網(wǎng)絡(luò)環(huán)境下(指TCP/IP),自然是使用inet_addr( )來(lái)把表示Internet地址的字符串轉(zhuǎn)換成二進(jìn)制形式。Internet地址碰巧也是32位的,就如運(yùn)行伯克利網(wǎng)絡(luò)系統(tǒng)的其他計(jì)算機(jī)一樣,都是有著同樣的字寬。

              但也存在著有關(guān)Internet地址的高級(jí)定義:in_ addr結(jié)構(gòu)。這個(gè)結(jié)構(gòu)定義包括了子域s_ addr,它是一個(gè)包含了Internet地址的unsigned long標(biāo)量。inet_addr()接受一個(gè)指向字符的指針,并且返回一個(gè)unsigned long,在轉(zhuǎn)換地址字符串過(guò)程中如果發(fā)生錯(cuò)誤,inet_addr將返回-1。

              程序Gated讀取以文本格式存放Internet地址的配置文件,并把它們放入sockaddr_in(這是一個(gè)包含了結(jié)構(gòu)in_addr的高級(jí)結(jié)構(gòu))。例1(a)中的代碼可以在32位電腦上正常運(yùn)行,但移植到克雷研究計(jì)算機(jī)上,卻無(wú)法運(yùn)行,為什么呢?

              例1:高級(jí)代碼的高級(jí)結(jié)構(gòu)

              (a)

            struct sockaddr_in saddrin
            char *str;

            if ((saddrin.sin_addr.s_addr = inet_addr(str)) == (unsigned long)-1) {
             do_some_error_handling;
            }

              (b)

            struct sockaddr_in saddrin
            char *str;

            if (inet_aton(str, &saddrin.sin_addr) ! = OK) {
             do_some_error_handling;
            }

              因?yàn)橹灰猧net_addr能夠正確地解析字符串,那么一切OK。當(dāng)inet_addr在64位計(jì)算機(jī)上返回一個(gè)錯(cuò)誤時(shí),這段代碼卻未能捕捉到。你必須要考慮比較語(yǔ)句中的數(shù)據(jù)位寬,來(lái)確定到底是哪出了錯(cuò)。

              首先,inet_addr返回錯(cuò)誤值——unsigned long -1,在64位中表示為比特位全為1,這個(gè)值被存儲(chǔ)在結(jié)構(gòu)in_addr下的子域s_addr中,而in_addr必須是32位來(lái)匹配Internet地址,所以它是一個(gè)32比特位的unsigned int(在我們的編譯器上,int是64位)。現(xiàn)在我們存儲(chǔ)進(jìn)32個(gè)1,存儲(chǔ)進(jìn)的值將與unsigned long -1比較。當(dāng)我們存儲(chǔ)32個(gè)1于unsigned int時(shí),編譯器自動(dòng)把32位提升為64位;這樣,數(shù)值0x00000000 ffffffff與0xffffffff ffffffff的比較當(dāng)然就失敗了。這是一個(gè)很難發(fā)現(xiàn)的bug,特別是在這種因?yàn)?2位到64位的隱式提升上。

              那我們對(duì)這個(gè)bug怎么辦呢?一個(gè)解決方法是在語(yǔ)句中比較0xffffffff,而不是-1,但這又會(huì)使代碼更加依賴于特定大小的對(duì)象。另一個(gè)方法是,使用一個(gè)中間的unsigned long變量,從而在把結(jié)果存入sockaddr_in前,執(zhí)行比較,但這會(huì)讓程序代碼更復(fù)雜。

              真正的問(wèn)題所在是,我們期望一個(gè)unsigned long值與一個(gè)32位量(如Internet地址)相等。Internet地址必須以32位形式進(jìn)行存儲(chǔ),但有些時(shí)候用一個(gè)標(biāo)量,來(lái)訪問(wèn)這個(gè)地址的一部分,是非常方便的。在32位字長(zhǎng)的電腦中,用一個(gè)long數(shù)值(常被當(dāng)作32位)來(lái)訪問(wèn)這個(gè)地址,看上去沒(méi)什么問(wèn)題。讓我們暫時(shí)不想一個(gè)低級(jí)的數(shù)據(jù)項(xiàng)(32位Internet地址)是否與一個(gè)機(jī)器字相等,那么高級(jí)數(shù)據(jù)類型結(jié)構(gòu)in_addr就應(yīng)該被一直使用。因?yàn)閕n_addr中沒(méi)有無(wú)效值,那么應(yīng)有一個(gè)單獨(dú)的狀態(tài)用作返回值。

              解決方案是定義一個(gè)新的函數(shù),就像inet_addr那樣,但返回一個(gè)狀態(tài)值,而且接受一個(gè)結(jié)構(gòu)in_addr作為參數(shù),參見(jiàn)例1(b)。因?yàn)楦呒?jí)的數(shù)據(jù)元素可以一直使用,而返回的值是沒(méi)有溢出的,所以這個(gè)代碼是可以跨架構(gòu)移植的,而不管字長(zhǎng)是多少。雖然伯克利發(fā)布了NET2,其中的確定義了一個(gè)新的函數(shù)inet_aton(),但如果試著改變inet_addr()中的代碼,將會(huì)損壞許多程序。

              低級(jí)代碼的低級(jí)結(jié)構(gòu)

              低級(jí)編程意味著直接操縱物理設(shè)備或者特定協(xié)議的通訊格式,例如,設(shè)備驅(qū)動(dòng)程序經(jīng)常使用非常精確的位模式來(lái)操縱控制寄存器。此外,網(wǎng)絡(luò)協(xié)議通過(guò)特定的比特位模式傳輸數(shù)據(jù)項(xiàng)時(shí),也必須適當(dāng)?shù)剞D(zhuǎn)譯。

              為了操縱物理數(shù)據(jù)項(xiàng),此處的數(shù)據(jù)結(jié)構(gòu)必須準(zhǔn)確地反映被操縱的內(nèi)容。比特位是一個(gè)不錯(cuò)的選擇,因?yàn)樗鼈冋弥付吮忍氐奈粩?shù)及排列。事實(shí)上,正是這種精確度,使比特位相對(duì)于short、int、long,更好地映像了物理結(jié)構(gòu)(short、int、long會(huì)因?yàn)殡娔X的不同而改變,而比特位不會(huì))。

              當(dāng)映像一個(gè)物理結(jié)構(gòu)時(shí),是通過(guò)定義格式來(lái)使比特位達(dá)到這種精度的,這就使你必須一直使用一種編碼風(fēng)格來(lái)訪問(wèn)結(jié)構(gòu)。此時(shí)的每個(gè)位域都是命名的,你寫出的代碼可直接訪問(wèn)這些位域。當(dāng)訪問(wèn)物理結(jié)構(gòu)時(shí),有件事可能你并不想去做,那就是使用標(biāo)量數(shù)組(short、int、or long),訪問(wèn)這些數(shù)組的代碼都假定存在一個(gè)特定的比特位寬,當(dāng)移植這些代碼到一臺(tái)使用不同字寬的電腦上時(shí),就可能不正確了。

              在我們移植PEX圖像庫(kù)時(shí)遇到的一個(gè)問(wèn)題,就涉及到其映像的協(xié)議消息結(jié)構(gòu)。在某臺(tái)電腦上,如果int整型的長(zhǎng)度與消息中的元素一樣,那例2(a)中的代碼就會(huì)工作得很正常。32位的數(shù)據(jù)元素在32字長(zhǎng)的電腦上沒(méi)有問(wèn)題,拿到64位的克雷計(jì)算機(jī)上,它就出錯(cuò)了。對(duì)例2(b)而言,不單要改變結(jié)構(gòu)定義,還要改變所有涉及到coord數(shù)組的代碼。這樣,擺在我們面前就有兩個(gè)選擇,要么重寫涉及此消息的所有代碼,要么定義一個(gè)低級(jí)的結(jié)構(gòu)和一個(gè)高級(jí)的結(jié)構(gòu),然后用一段特殊的代碼把數(shù)據(jù)從一個(gè)拷貝到另一個(gè)當(dāng)中,不過(guò)我也不期望可以找出每個(gè)對(duì)zcoord = draw_ msg.coord[2]的引用,而且,當(dāng)現(xiàn)在需要移植到一個(gè)新架構(gòu)上時(shí),把所有的代碼都改寫成如例2(c)所示無(wú)疑是一項(xiàng)艱苦的工作。這個(gè)特殊的問(wèn)題是由于忽視字長(zhǎng)的不同而帶來(lái)的,所以不能假設(shè)在可移植的代碼中機(jī)器字長(zhǎng)、short、int、long都是具有同樣的大小。

              例2:低級(jí)代碼的低級(jí)結(jié)構(gòu)

              (a)

            struct draw_msg {
             int objectid;
             int coord[3];
            }

              (b)

            struct draw_msg {
             int objectid:32;
             int coord1:32;
             int coord2:32;
             int coord3:32;
            }

              (c)

            int *iptr, *optr, *limit;
            int xyz[3];

            iptr = draw_msg.coord;
            limit = draw_msg.coord + sizeof(draw_msg.coord);

            optr = xyz;
            while (iptr < limit)
            *optr++ = *iptr++;
            結(jié)構(gòu)打包和字對(duì)齊

               正是因?yàn)榫幾g器會(huì)對(duì)結(jié)構(gòu)進(jìn)行打包,所以不同計(jì)算機(jī)上字長(zhǎng)的變化,還導(dǎo)致了另一個(gè)問(wèn)題。C編譯器在字(word)的邊界上對(duì)齊字長(zhǎng),當(dāng)具有一個(gè)字長(zhǎng)的數(shù)據(jù)后面緊接著一個(gè)較小的數(shù)據(jù)時(shí),這種方法會(huì)產(chǎn)生內(nèi)存空缺(不過(guò)也有例外,比如說(shuō)當(dāng)有足夠多的小數(shù)據(jù)剛好可以填充一個(gè)字時(shí))。

               一些聰明的程序員在聲明聯(lián)合時(shí),往往在其中會(huì)帶有兩個(gè)或更多的結(jié)構(gòu),其中一個(gè)結(jié)構(gòu)剛好填充聯(lián)合,另一個(gè)則可以用來(lái)從不同的角度來(lái)看待這個(gè)聯(lián)合,參見(jiàn)例3(a)。假設(shè)這段代碼是為16位字長(zhǎng)的計(jì)算機(jī)所寫,int為16位,long為32位,那么存取這個(gè)結(jié)構(gòu)的代碼將會(huì)得到正常的映射關(guān)系(如圖1),而例3(b)中的代碼也會(huì)按預(yù)期的那樣工作。可是,如果這段代碼一旦移植到另一臺(tái)具有32位字長(zhǎng)的計(jì)算機(jī)上時(shí),映射關(guān)系就改變了。如果新計(jì)算機(jī)上的編譯器允許你使用16位的int,那么字的對(duì)齊就會(huì)像圖2所示了,或者如果編譯器遵循K&R約定,那么int將會(huì)和一個(gè)字(32比特)一樣長(zhǎng),對(duì)齊就如圖3所示,在任一情況下,這都將導(dǎo)致問(wèn)題。




              例3:結(jié)構(gòu)打包和字對(duì)齊

              (a)

            union parse_hdr {
             struct hdr {
              char data1;
              char data2;
              int data3;
              int data4;
            } hdr;
            struct tkn {
             int class;
             long tag;
            } tkn;
            } parse_item;

              (b)

            char *ptr = msgbuf;

            parse_item.hdr.data1 = *ptr++;
            parse_item.hdr.data2 = *ptr++;
            parse_item.hdr.data3 = (*ptr++ << 8 | *ptr++);
            parse_item.hdr.data4 = (*ptr++ << 8 | *ptr++);

            if (parse.tkn.class >= MIN_TOKEN_CLASS && parse.tkn.class <= MAX_TOKEN_CLASS) {
             interpret_tag(parse.tkn.tag);
            }

              在第一個(gè)情況中(圖2),tag域不是像期望的那樣線性拉長(zhǎng),而被填充了一些垃圾。而在第二個(gè)情況中(圖3),無(wú)論是class還是tag域,都不再有意義,兩個(gè)char值因?yàn)楸淮虬梢粋€(gè)int,所以也都不再正確。再次強(qiáng)調(diào),首先不要假設(shè)標(biāo)準(zhǔn)數(shù)據(jù)類型大小一樣,其次還要了解它們是怎樣被映射成其他數(shù)據(jù)類型的,這才是書(shū)寫可移植代碼的最好方法。

              機(jī)器尋址特性

              幾乎所有的處理器都在字邊界上以字為單位進(jìn)行尋址,而且通常都為此作了一些優(yōu)化。另有一些的處理器允許其他類型的尋址,如以字節(jié)為單位尋址、或在半個(gè)字邊界上以半字為單位尋址,甚至還有一些處理器有輔助硬件允許在奇數(shù)邊界上同時(shí)以字和半字進(jìn)行尋址。

              尋址機(jī)制在不同計(jì)算機(jī)上會(huì)有所變化,最快的尋址模式是在字邊界上以字為單位進(jìn)行尋址。其他方式的尋址需要輔助硬件,通常都會(huì)對(duì)內(nèi)存訪問(wèn)增加了一些時(shí)鐘周期。而這些過(guò)多的模式和特殊硬件的支持,是與RISC處理器的設(shè)計(jì)初衷背道而馳的,就拿克雷計(jì)算機(jī)來(lái)說(shuō),就只支持在字邊界上以字為單位進(jìn)行尋址。

              在那些不提供多種數(shù)據(jù)類型尋址方式的計(jì)算機(jī)上,編譯器可以提供一些模擬。例如:編譯器可以生成一些指令,當(dāng)讀取一個(gè)字時(shí),通過(guò)移位和屏蔽,來(lái)找到所想要的位置,以此來(lái)模擬在字中的半字尋址,但這會(huì)需要額外的時(shí)鐘周期,并且代碼體積會(huì)更大。

              從這點(diǎn)上來(lái)說(shuō),位域的效率是非常低的,在以位域來(lái)取出一個(gè)字時(shí),它們產(chǎn)生的代碼體積最大。當(dāng)你存取同一個(gè)字中的其他位域時(shí),又需要對(duì)包含這個(gè)位域字的內(nèi)存,重新進(jìn)行一遍尋址,這就是典型的以空間換時(shí)間。

              當(dāng)在設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu)時(shí),我們總是想用可以保存數(shù)據(jù)的最小數(shù)據(jù)類型,來(lái)達(dá)到節(jié)省空間的目的。我們有時(shí)小氣得經(jīng)常使用char和short,來(lái)存取位特域,這就像是為了節(jié)省一角錢,而花了一元錢。儲(chǔ)存空間上的高效,會(huì)付出在程序速度和體積上隱藏的代價(jià)。

              試想你只為一個(gè)緊湊結(jié)構(gòu)分配了一小點(diǎn)的空間,但卻產(chǎn)生了大量的代碼來(lái)存取結(jié)構(gòu)中的域,而且這段代碼還是經(jīng)常執(zhí)行的,那么,會(huì)因?yàn)榉亲謱ぶ罚鴮?dǎo)致代碼運(yùn)行緩慢,而且充斥了大量用于提取域的代碼,程序體積也因此增大。這些額外代碼所占的空間,會(huì)讓你前面為節(jié)省空間所做的努力付之東流。

              在高級(jí)數(shù)據(jù)結(jié)構(gòu)中,特定的比特定位已不是必須的了,應(yīng)在所有的域中都使用字(word),而不要操心它們所占用的空間。特別是在程序某些依賴于機(jī)器的部分,應(yīng)該為字準(zhǔn)備一個(gè)typedef,如下:

            /*在這臺(tái)計(jì)算機(jī)上,int是一個(gè)字長(zhǎng)*/
            typedef word int;

              在高級(jí)結(jié)構(gòu)中,對(duì)所有域都使用字有如下好處:

              a.. 對(duì)其他計(jì)算機(jī)架構(gòu)的可移植性

              b.. 編譯器可能生成最快的代碼

              c.. 處理器可能最快地訪問(wèn)到所需內(nèi)存

              d.. 絕對(duì)沒(méi)有結(jié)構(gòu)對(duì)齊的意外發(fā)生

              必須也承認(rèn),在某些時(shí)候,是不能做到全部使用字的。例如,有一個(gè)很大的結(jié)構(gòu),但不會(huì)被經(jīng)常存取,如果使用了數(shù)千個(gè)字的話,體積將會(huì)增大25%,但使用字通常會(huì)節(jié)省空間、提高執(zhí)行速度,而且更具移植性。

              以下是我們的結(jié)論:

              書(shū)寫跨平臺(tái)移植的代碼,其實(shí)是件簡(jiǎn)單的事情。最基本的規(guī)則是,盡可能地隱藏機(jī)器字長(zhǎng)的細(xì)節(jié),用非常精確的數(shù)據(jù)元素位大小來(lái)映射物理數(shù)據(jù)結(jié)構(gòu)。或者像前面所建議的,為高級(jí)編程使用高級(jí)數(shù)據(jù)結(jié)構(gòu),而低級(jí)編程使用低級(jí)數(shù)據(jù)結(jié)構(gòu),當(dāng)闡明高級(jí)數(shù)據(jù)結(jié)構(gòu)時(shí),對(duì)標(biāo)準(zhǔn)C的標(biāo)量類型,不要作任何假設(shè)

            posted @ 2006-07-07 13:51 井泉 閱讀(298) | 評(píng)論 (0)編輯 收藏

            C語(yǔ)言編程---性能優(yōu)化

            使用宏定義

              在C語(yǔ)言中,宏是產(chǎn)生內(nèi)嵌代碼的唯一方法。對(duì)于嵌入式系統(tǒng)而言,為了能達(dá)到性能要求,宏是一種很好的代替函數(shù)的方法。

              寫一個(gè)"標(biāo)準(zhǔn)"宏MIN ,這個(gè)宏輸入兩個(gè)參數(shù)并返回較小的一個(gè):

              錯(cuò)誤做法:

            #define MIN(A,B)  ( A <= B ? A : B )

              正確做法:

            #define MIN(A,B) ((A)<= (B) ? (A) : (B) )

              對(duì)于宏,我們需要知道三點(diǎn):

              (1)宏定義"像"函數(shù);

              (2)宏定義不是函數(shù),因而需要括上所有"參數(shù)";

              (3)宏定義可能產(chǎn)生副作用。

              下面的代碼:

            least = MIN(*p++, b);

              將被替換為:

            ( (*p++) <= (b) ?(*p++):(b) )

              發(fā)生的事情無(wú)法預(yù)料。

              因而不要給宏定義傳入有副作用的"參數(shù)"。

              使用寄存器變量

              當(dāng)對(duì)一個(gè)變量頻繁被讀寫時(shí),需要反復(fù)訪問(wèn)內(nèi)存,從而花費(fèi)大量的存取時(shí)間。為此,C語(yǔ)言提供了一種變量,即寄存器變量。這種變量存放在CPU的寄存器中,使用時(shí),不需要訪問(wèn)內(nèi)存,而直接從寄存器中讀寫,從而提高效率。寄存器變量的說(shuō)明符是register。對(duì)于循環(huán)次數(shù)較多的循環(huán)控制變量及循環(huán)體內(nèi)反復(fù)使用的變量均可定義為寄存器變量,而循環(huán)計(jì)數(shù)是應(yīng)用寄存器變量的最好候選者。

              (1) 只有局部自動(dòng)變量和形參才可以定義為寄存器變量。因?yàn)榧拇嫫髯兞繉儆趧?dòng)態(tài)存儲(chǔ)方式,凡需要采用靜態(tài)存儲(chǔ)方式的量都不能定義為寄存器變量,包括:模塊間全局變量、模塊內(nèi)全局變量、局部static變量;

              (2) register是一個(gè)"建議"型關(guān)鍵字,意指程序建議該變量放在寄存器中,但最終該變量可能因?yàn)闂l件不滿足并未成為寄存器變量,而是被放在了存儲(chǔ)器中,但編譯器中并不報(bào)錯(cuò)(在C++語(yǔ)言中有另一個(gè)"建議"型關(guān)鍵字:inline)。

              下面是一個(gè)采用寄存器變量的例子:

            /* 求1+2+3+….+n的值 */
            WORD Addition(BYTE n)
            {
             register i,s=0;
             for(i=1;i<=n;i++)
             {
              s=s+i;
             }
             return s;
            }

              本程序循環(huán)n次,i和s都被頻繁使用,因此可定義為寄存器變量。

              內(nèi)嵌匯編

              程序中對(duì)時(shí)間要求苛刻的部分可以用內(nèi)嵌匯編來(lái)重寫,以帶來(lái)速度上的顯著提高。但是,開(kāi)發(fā)和測(cè)試匯編代碼是一件辛苦的工作,它將花費(fèi)更長(zhǎng)的時(shí)間,因而要慎重選擇要用匯編的部分。

              在程序中,存在一個(gè)80-20原則,即20%的程序消耗了80%的運(yùn)行時(shí)間,因而我們要改進(jìn)效率,最主要是考慮改進(jìn)那20%的代碼。

              嵌入式C程序中主要使用在線匯編,即在C程序中直接插入_asm{ }內(nèi)嵌匯編語(yǔ)句:

            /* 把兩個(gè)輸入?yún)?shù)的值相加,結(jié)果存放到另外一個(gè)全局變量中 */
            int result;
            void Add(long a, long *b)
            {
             _asm
             {
              MOV AX, a
              MOV BX, b
              ADD AX, [BX]
              MOV result, AX
             }
            }

              利用硬件特性

              首先要明白CPU對(duì)各種存儲(chǔ)器的訪問(wèn)速度,基本上是:

            CPU內(nèi)部RAM > 外部同步RAM > 外部異步RAM > FLASH/ROM

              對(duì)于程序代碼,已經(jīng)被燒錄在FLASH或ROM中,我們可以讓CPU直接從其中讀取代碼執(zhí)行,但通常這不是一個(gè)好辦法,我們最好在系統(tǒng)啟動(dòng)后將FLASH或ROM中的目標(biāo)代碼拷貝入RAM中后再執(zhí)行以提高取指令速度;

              對(duì)于UART等設(shè)備,其內(nèi)部有一定容量的接收BUFFER,我們應(yīng)盡量在BUFFER被占滿后再向CPU提出中斷。例如計(jì)算機(jī)終端在向目標(biāo)機(jī)通過(guò)RS-232傳遞數(shù)據(jù)時(shí),不宜設(shè)置UART只接收到一個(gè)BYTE就向CPU提中斷,從而無(wú)謂浪費(fèi)中斷處理時(shí)間;

              如果對(duì)某設(shè)備能采取DMA方式讀取,就采用DMA讀取,DMA讀取方式在讀取目標(biāo)中包含的存儲(chǔ)信息較大時(shí)效率較高,其數(shù)據(jù)傳輸?shù)幕締挝皇菈K,而所傳輸?shù)臄?shù)據(jù)是從設(shè)備直接送入內(nèi)存的(或者相反)。DMA方式較之中斷驅(qū)動(dòng)方式,減少了CPU 對(duì)外設(shè)的干預(yù),進(jìn)一步提高了CPU與外設(shè)的并行操作程度。

              活用位操作

              使用C語(yǔ)言的位操作可以減少除法和取模的運(yùn)算。在計(jì)算機(jī)程序中數(shù)據(jù)的位是可以操作的最小數(shù)據(jù)單位,理論上可以用"位運(yùn)算"來(lái)完成所有的運(yùn)算和操作,因而,靈活的位操作可以有效地提高程序運(yùn)行的效率。舉例如下:

            /* 方法1 */
            int i,j;
            i = 879 / 16;
            j = 562 % 32;
            /* 方法2 */
            int i,j;
            i = 879 >> 4;
            j = 562 - (562 >> 5 << 5);

              對(duì)于以2的指數(shù)次方為"*"、"/"或"%"因子的數(shù)學(xué)運(yùn)算,轉(zhuǎn)化為移位運(yùn)算"<< >>"通常可以提高算法效率。因?yàn)槌顺\(yùn)算指令周期通常比移位運(yùn)算大。

              C語(yǔ)言位運(yùn)算除了可以提高運(yùn)算效率外,在嵌入式系統(tǒng)的編程中,它的另一個(gè)最典型的應(yīng)用,而且十分廣泛地正在被使用著的是位間的與(&)、或(|)、非(~)操作,這跟嵌入式系統(tǒng)的編程特點(diǎn)有很大關(guān)系。我們通常要對(duì)硬件寄存器進(jìn)行位設(shè)置,譬如,我們通過(guò)將AM186ER型80186處理器的中斷屏蔽控制寄存器的第低6位設(shè)置為0(開(kāi)中斷2),最通用的做法是:

            #define INT_I2_MASK 0x0040
            wTemp = inword(INT_MASK);
            outword(INT_MASK, wTemp &~INT_I2_MASK);

              而將該位設(shè)置為1的做法是:

            #define INT_I2_MASK 0x0040
            wTemp = inword(INT_MASK);
            outword(INT_MASK, wTemp | INT_I2_MASK);

              判斷該位是否為1的做法是:

            #define INT_I2_MASK 0x0040
            wTemp = inword(INT_MASK);
            if(wTemp & INT_I2_MASK)
            {
            … /* 該位為1 */
            }

              上述方法在嵌入式系統(tǒng)的編程中是非常常見(jiàn)的,我們需要牢固掌握。

              總結(jié)

              在性能優(yōu)化方面永遠(yuǎn)注意80-20準(zhǔn)備,不要優(yōu)化程序中開(kāi)銷不大的那80%,這是勞而無(wú)功的。

              宏定義是C語(yǔ)言中實(shí)現(xiàn)類似函數(shù)功能而又不具函數(shù)調(diào)用和返回開(kāi)銷的較好方法,但宏在本質(zhì)上不是函數(shù),因而要防止宏展開(kāi)后出現(xiàn)不可預(yù)料的結(jié)果,對(duì)宏的定義和使用要慎而處之。很遺憾,標(biāo)準(zhǔn)C至今沒(méi)有包括C++中inline函數(shù)的功能,inline函數(shù)兼具無(wú)調(diào)用開(kāi)銷和安全的優(yōu)點(diǎn)。

              使用寄存器變量、內(nèi)嵌匯編和活用位操作也是提高程序效率的有效方法。

              除了編程上的技巧外,為提高系統(tǒng)的運(yùn)行效率,我們通常也需要最大可能地利用各種硬件設(shè)備自身的特點(diǎn)來(lái)減小其運(yùn)轉(zhuǎn)開(kāi)銷,例如減小中斷次數(shù)、利用DMA傳輸方式等。

            posted @ 2006-07-07 13:16 井泉 閱讀(220) | 評(píng)論 (0)編輯 收藏

            幾個(gè)windows 快捷鍵

            Posted on 2006-06-24 16:55 Gin 閱讀(125) 評(píng)論(0)? 編輯 收藏
            摘自網(wǎng)上的,呵,這幾個(gè)是自己比較少用到的

            F2           ??? 當(dāng)你選中一個(gè)文件的話,這意味著“重命名”
            ALT+ ENTER或 ALT+雙擊????? 查看項(xiàng)目的屬性
            F10或ALT?????????????????????????????????? 激活當(dāng)前程序的菜單欄
            CTRL+ ESC??????????????????????????????? 顯示“開(kāi)始”菜單
            ALT+空格鍵?????????????????????????????? 顯示當(dāng)前窗口的系統(tǒng)菜單
            CTRL+F5        ????? 強(qiáng)行刷新
            ALT+RIGHT ARROW     顯示前一頁(yè)(前進(jìn)鍵)
            ALT+LEFT ARROW     顯示后一頁(yè)(后退鍵)
            ALT+ESC         切換當(dāng)前程序
            CTRL+N         新建一個(gè)新的文件
            CTRL+O         打開(kāi)“打開(kāi)文件”對(duì)話框
            CTRL+P         打開(kāi)“打印”對(duì)話框

            SHIFT+F10??????????????????????????? 顯示某個(gè)鏈接的快捷菜單
            CTRL+W?????????????????????????????? 關(guān)閉當(dāng)前窗口
            ALT+D????????????????????????????????? 選擇地址欄中的文字
            CTRL+ENTER???????????????????? 在地址欄中將"www."添加到鍵入的文本的前面,將".com"添加到文本的后面
            CTRL+D??????????????????????????????? 將當(dāng)前Web 頁(yè)添加到收藏夾中
            CTRL+B?????????????????????????????? 打開(kāi)"整理收藏夾"對(duì)話框
            CTRL+I?????????????????????????????? 在瀏覽欄中打開(kāi)收藏夾
            ALT+ENTER
            將 Windows 下運(yùn)行的命令行窗口在窗口和全屏幕狀態(tài)間切換;查看選定的文件的屬性;選定任務(wù)欄時(shí)打開(kāi)"任務(wù)欄和開(kāi)始菜單"屬性
            Alt+空格→X?????????????????????? 最大化當(dāng)前窗口
            Alt+空格→N?????????????????????? 最小化當(dāng)前窗口

            posted @ 2006-07-05 10:35 井泉 閱讀(259) | 評(píng)論 (0)編輯 收藏

            體驗(yàn)C++中接口與實(shí)現(xiàn)分離的技術(shù)

            在用C++寫要導(dǎo)出類的庫(kù)時(shí),我們經(jīng)常只想暴露接口,而隱藏類的實(shí)現(xiàn)細(xì)節(jié)。也就是說(shuō)我們提供的頭文件里只提供要暴露的公共成員函數(shù)的聲明,類的其他所有信息都不會(huì)在這個(gè)頭文件里面顯示出來(lái)。這個(gè)時(shí)候就要用到接口與實(shí)現(xiàn)分離的技術(shù)。

              下面用一個(gè)最簡(jiǎn)單的例子來(lái)說(shuō)明。

              類ClxExp是我們要導(dǎo)出的類,其中有一個(gè)私有成員變量是ClxTest類的對(duì)象,各個(gè)文件內(nèi)容如下:

              lxTest.h文件內(nèi)容:

            class ClxTest
            {
             public:
              ClxTest();
              virtual ~ClxTest();
              void DoSomething();
            };

              lxTest.cpp文件內(nèi)容:

            #include "lxTest.h"

            #include <iostream>
            using namespace std;

            ClxTest::ClxTest()
            {}

            ClxTest::~ClxTest()
            {}

            void ClxTest::DoSomething()
            {
             cout << "Do something in class ClxTest!" << endl;
            }

            ////////////////////////////////////////////////////////////////////////////

              lxExp.h文件內(nèi)容:

            #include "lxTest.h"

            class ClxExp
            {
             public:
              ClxExp();
              virtual ~ClxExp();
              void DoSomething();
             private:
              ClxTest m_lxTest;
              void lxTest();
            };

              lxExp.cpp文件內(nèi)容:

            #include "lxExp.h"

            ClxExp::ClxExp()
            {}

            ClxExp::~ClxExp()
            {}

            // 其實(shí)該方法在這里并沒(méi)有必要,我這樣只是為了說(shuō)明調(diào)用關(guān)系
            void ClxExp::lxTest()
            {
             m_lxTest.DoSomething();
            }

            void ClxExp::DoSomething()
            {
             lxTest();
            }

              為了讓用戶能使用我們的類ClxExp,我們必須提供lxExp.h文件,這樣類ClxExp的私有成員也暴露給用戶了。而且,僅僅提供lxExp.h文件是不夠的,因?yàn)閘xExp.h文件include了lxTest.h文件,在這種情況下,我們還要提供lxTest.h文件。那樣ClxExp類的實(shí)現(xiàn)細(xì)節(jié)就全暴露給用戶了。另外,當(dāng)我們對(duì)類ClxTest做了修改(如添加或刪除一些成員變量或方法)時(shí),我們還要給用戶更新lxTest.h文件,而這個(gè)文件是跟接口無(wú)關(guān)的。如果類ClxExp里面有很多像m_lxTest那樣的對(duì)象的話,我們就要給用戶提供N個(gè)像lxTest.h那樣的頭文件,而且其中任何一個(gè)類有改動(dòng),我們都要給用戶更新頭文件。還有一點(diǎn)就是用戶在這種情況下必須進(jìn)行重新編譯!

              上面是非常小的一個(gè)例子,重新編譯的時(shí)間可以忽略不計(jì)。但是,如果類ClxExp被用戶大量使用的話,那么在一個(gè)大項(xiàng)目中,重新編譯的時(shí)候我們就有時(shí)間可以去喝杯咖啡什么的了。當(dāng)然上面的種種情況不是我們想看到的!你也可以想像一下用戶在自己程序不用改動(dòng)的情況下要不停的更新頭文件和編譯時(shí),他們心里會(huì)罵些什么。其實(shí)對(duì)用戶來(lái)說(shuō),他們只關(guān)心類ClxExp的接口DoSomething()方法。那我們?cè)趺床拍苤槐┞额怌lxExp的DoSomething()方法而不又產(chǎn)生上面所說(shuō)的那些問(wèn)題呢?答案就是--接口與實(shí)現(xiàn)的分離。我可以讓類ClxExp定義接口,而把實(shí)現(xiàn)放在另外一個(gè)類里面。下面是具體的方法:

              首先,添加一個(gè)實(shí)現(xiàn)類ClxImplement來(lái)實(shí)現(xiàn)ClxExp的所有功能。注意:類ClxImplement有著跟類ClxExp一樣的公有成員函數(shù),因?yàn)樗麄兊慕涌谝耆恢隆?br />
              lxImplement.h文件內(nèi)容:

            #include "lxTest.h"

            class ClxImplement
            {
             public:
              ClxImplement();
              virtual ~ClxImplement();

              void DoSomething();
             
             private:
              ClxTest m_lxTest;
              void lxTest();
            };

              lxImplement.cpp文件內(nèi)容:

            #include "lxImplement.h"

            ClxImplement::ClxImplement()
            {}

            ClxImplement::~ClxImplement()
            {}

            void ClxImplement::lxTest()
            {
             m_lxTest.DoSomething();
            }

            void ClxImplement::DoSomething()
            {
             lxTest();
            }

              然后,修改類ClxExp。

              修改后的lxExp.h文件內(nèi)容:

            // 前置聲明
            class ClxImplement;

            class ClxExp
            {
             public:
              ClxExp();
              virtual ~ClxExp();
              void DoSomething();
             private:
              // 聲明一個(gè)類ClxImplement的指針,不需要知道類ClxImplement的定義
              ClxImplement *m_pImpl;
            };

              修改后的lxExp.cpp文件內(nèi)容:

            // 在這里包含類ClxImplement的定義頭文件
            #include "lxImplement.h"

            ClxExp::ClxExp()
            {
             m_pImpl = new ClxImplement;
            }

            ClxExp::~ClxExp()
            {
             delete m_pImpl;
            }

            void ClxExp::DoSomething()
            {
             m_pImpl->DoSomething();
            }

              通過(guò)上面的方法就實(shí)現(xiàn)了類ClxExp的接口與實(shí)現(xiàn)的分離。請(qǐng)注意兩個(gè)文件中的注釋。類ClxExp里面聲明的只是接口而已,而真正的實(shí)現(xiàn)細(xì)節(jié)被隱藏到了類ClxImplement里面。為了能在類ClxExp中使用類ClxImplement而不include頭文件lxImplement.h,就必須有前置聲明class ClxImplement,而且只能使用指向類ClxImplement對(duì)象的指針,否則就不能通過(guò)編譯。

              在發(fā)布庫(kù)文件的時(shí)候,我們只需給用戶提供一個(gè)頭文件lxExp.h就行了,不會(huì)暴露類ClxExp的任何實(shí)現(xiàn)細(xì)節(jié)。而且我們對(duì)類ClxTest的任何改動(dòng),都不需要再給用戶更新頭文件(當(dāng)然,庫(kù)文件是要更新的,但是這種情況下用戶也不用重新編譯!)。這樣做還有一個(gè)好處就是,可以在分析階段由系統(tǒng)分析員或者高級(jí)程序員來(lái)先把類的接口定義好,甚至可以把接口代碼寫好(例如上面修改后的lxExp.h文件和lxExp.cpp文件),而把類的具體實(shí)現(xiàn)交給其他程序員開(kāi)發(fā)。

            posted @ 2006-07-05 09:59 井泉 閱讀(204) | 評(píng)論 (0)編輯 收藏

            C++ Primer讀書(shū)筆記

            前些日子開(kāi)始看《C++ Primer》,順便做一些筆記,既有書(shū)上的,也有自己理解的。
            因?yàn)閯倢W(xué)C++不久,筆下難免有謬誤之處,行文更是凌亂;
            所幸不是用來(lái)顯配的東西,發(fā)在linuxsir只是為了方便自己閱讀記憶,以防只顧上網(wǎng)忘了正事。
            書(shū)看了不到一半,所以大約才寫了一半,慢慢補(bǔ)充。
            =========================================


            ==========================================
            轉(zhuǎn)載務(wù)必注明原作者
            neplusultra 2005.2.3
            ==========================================


            const要注意的問(wèn)題
              1、下面是一個(gè)幾乎所有人剛開(kāi)始都會(huì)搞錯(cuò)的問(wèn)題:
            已知:typedef char *cstring;
            在以下聲明中,cstr的類型是什么?
            extern const cstring cstr;

            錯(cuò)誤答案:const char *cstr;
            正確答案:char *const cstr;

              錯(cuò)誤在于將typedef當(dāng)作宏擴(kuò)展。const 修飾cstr的類型。cstr是一個(gè)指針,因此,這個(gè)定義聲明了cstr是一個(gè)指向字符的const指針。
              2、指針是const還是data為const?
            辨別方法很簡(jiǎn)單,如下:
            代碼:
            char *p="hello"; //non-const pointer, non-const data; const char *p="hello"; // non-const pointer, const data; char * const p="hello"; // const pointer , non-const data; const char * const p="hello"; // const pointer, const data;
              要注意的是,"hello"的類型是const char * ,按C++standard規(guī)則,char *p="hello" 是非法的(右式的const char* 不能轉(zhuǎn)換為左式的char *),違反了常量性。但是這種行為在C中實(shí)在太頻繁,因此C++standard對(duì)于這種初始化動(dòng)作給予豁免。盡管如此,還是盡量避免這種用法。
              3、const初始化的一些問(wèn)題
            const 對(duì)象必須被初始化:
            代碼:
            const int *pi=new int; // 錯(cuò)誤,沒(méi)有初始化 const int *pi=new int(100); //正確 const int *pci=new const int[100]; //編譯錯(cuò)誤,無(wú)法初始化用new表達(dá)式創(chuàng)建的內(nèi)置類型數(shù)組元素。

            什么時(shí)候需要copy constructor,copy assignment operator,destructor
              注意,若class需要三者之一,那么它往往需要三者。
            當(dāng)class的copy constructor內(nèi)分配有一塊指向hcap的內(nèi)存,需要由destructor釋放,那么它也往往需要三者。

            為什么需要protected 訪問(wèn)級(jí)別
              有人認(rèn)為,protected訪問(wèn)級(jí)別允許派生類直接訪問(wèn)基類成員,這破壞了封裝的概念,因此所有基類的實(shí)現(xiàn)細(xì)節(jié)都應(yīng)該是private的;另外一些人認(rèn)為,如果派生類不能直接訪問(wèn)基類的成員,那么派生類的實(shí)現(xiàn)將無(wú)法有足夠的效率供用戶使用,如果沒(méi)有protected,類的設(shè)計(jì)者將被迫把基類成員設(shè)置為public。
              事實(shí)上,protected正是在高純度的封裝與效率之間做出的一個(gè)良好折衷方案。

            為什么需要virtual member function又不能濫用virtual
              若基類設(shè)計(jì)者把本應(yīng)設(shè)計(jì)成virtual的成員函數(shù)設(shè)計(jì)成非virtual,則繼承類將無(wú)法實(shí)現(xiàn)改寫(overridden),給繼承類的實(shí)現(xiàn)帶來(lái)不便;
              另一方面,一旦成員函數(shù)被設(shè)計(jì)成virtual,則該類的對(duì)象將額外增加虛擬指針(vptr)和虛擬表格(vtbl),所以倘若出于方便繼承類overridden的目的而使所有成員函數(shù)都為virtual,可能會(huì)影響效率,因?yàn)槊總€(gè)virtual成員函數(shù)都需付出動(dòng)態(tài)分派的成本。而且virtual成員函數(shù)不能內(nèi)聯(lián)(inline),我們知道,內(nèi)聯(lián)發(fā)生在編譯時(shí)刻,而虛擬函數(shù)在運(yùn)行時(shí)刻才處理。對(duì)于那些小巧而被頻繁調(diào)用、與類型無(wú)關(guān)的函數(shù),顯然不應(yīng)該被設(shè)置成virtual。

            關(guān)于引用的一些注意點(diǎn)
              1、把函數(shù)參數(shù)聲明為數(shù)組的引用:當(dāng)函數(shù)參數(shù)是一個(gè)數(shù)組類型的引用時(shí),數(shù)組長(zhǎng)度成為參數(shù)和實(shí)參類型的一部分,編譯器檢查數(shù)組實(shí)參的長(zhǎng)度和與在函數(shù)參數(shù)類型中指定的長(zhǎng)度是否匹配。
            代碼:
            //參數(shù)為10個(gè)int數(shù)組 void showarr(int (&arr)[10]); void func() { int i,j[2],k[10]; showarr(i); //錯(cuò)誤!實(shí)參必須是10個(gè)int的數(shù)組 showarr(j); //錯(cuò)誤!實(shí)參必須是10個(gè)int的數(shù)組 showarr(k); //正確! } //更靈活的實(shí)現(xiàn),借助函數(shù)模板。下面是一個(gè)顯示數(shù)組內(nèi)容的函數(shù)。 template <typename Type , int size> void printarr(const Type (& r_array)[size]) { for(int i=0;i<size;i++) std::cout<< r_array[i] <<' '; std::cout << std::endl; } void caller() { int ar[5]={1,2,5,3,4}; //數(shù)組可以任意大小。 printarr(ar); //正確!自動(dòng)正確調(diào)用printarr() }
              2、
              3、

            goto語(yǔ)句的一些要注意的地方
              1、label語(yǔ)句只能用作goto的目標(biāo),且label語(yǔ)句只能用冒號(hào)結(jié)束,且label語(yǔ)句后面不能緊接右花括號(hào)'}',如
            代碼:
            label8: }
              辦法是在冒號(hào)后面加一個(gè)空語(yǔ)句(一個(gè)';'即可),如
            代碼:
            label7: ;}
               2、goto語(yǔ)句不能向前跳過(guò)如下聲明語(yǔ)句:
            代碼:
            goto label6; int x=1; //錯(cuò)誤,不能跳過(guò)該聲明! cout<<x<<endl; //使用x label6: //其他語(yǔ)句
            但是,把int x=1; 改為int x; 則正確了。另外一種方法是:
            代碼:
            goto label6; { int x=1; //正確,使用了語(yǔ)句快 cout<<x<<endl; } label6: //其他語(yǔ)句
              3、goto語(yǔ)句可以向后(向程序開(kāi)頭的方向)跳過(guò)聲明定義語(yǔ)句。
            代碼:
            begin: int i=22; cout<< i <<endl; goto begin; //非常蹩腳,但它是正確的

            變量作用域
              1、花括號(hào)可以用來(lái)指明局部作用域。
              2、在for、if、switch、while語(yǔ)句的條件/循環(huán)條件中可以聲明變量,該變量?jī)H在相應(yīng)語(yǔ)句塊內(nèi)有效。
              3、extern為聲明但不定義一個(gè)對(duì)象提供了一種方法;它類似于函數(shù)聲明,指明該對(duì)象會(huì)在其他地方被定義:或者在此文本的其他地方,或者在程序的其他文本文件中。例如extern int i; 表示在其他地方存在聲明 int i;
              extern 聲明不會(huì)引起內(nèi)存分配,他可以在同一個(gè)文件或同一個(gè)程序中出現(xiàn)多次。因此在全局作用域中,以下語(yǔ)句是正確的:
            代碼:
            extern int c; int c=1; //沒(méi)錯(cuò) extern int c; //沒(méi)錯(cuò)
              但是,extern聲明若指定了一個(gè)顯式初始值的全局對(duì)象,將被視為對(duì)該對(duì)象的定義,編譯器將為其分配存儲(chǔ)區(qū);對(duì)該對(duì)象的后續(xù)定義將出錯(cuò)。如下:
            代碼:
            extern int i=1; int i=2; //出錯(cuò)!重復(fù)定義

            auto_ptr若干注意點(diǎn)
              1、auto_ptr的主要目的是支持普通指針類型相同的語(yǔ)法,并為auto_ptr所指對(duì)象的釋放提供自動(dòng)管理,而且auto_ptr的安全性幾乎不會(huì)帶來(lái)額外的代價(jià)(因?yàn)槠洳僮髦С侄际莾?nèi)聯(lián)的)。定義形式有三種:
            代碼:
            auto_ptr<type_pointed_to>identifier(ptr_allocated_by_new); auto_ptr<type_pointed_to>identifier(auto_ptr_of_same_type); auto_ptr<type_pointed_to>identifier;
              2、所有權(quán)概念。auto_ptr_p1=auto_ptr_p2的后果是,auto_ptr_p2喪失了其原指向?qū)ο蟮乃袡?quán),并且auto_ptr_p2.get()==0。不要讓兩個(gè)auto_ptr對(duì)象擁有空閑存儲(chǔ)區(qū)內(nèi)同一對(duì)象的所有權(quán)。注意以下兩種種初始化方式的區(qū)別:
            代碼:
            auto_ptr<string>auto_ptr_str1(auto_ptr_str2.get()); //注意!用str2指針初始化str1, 兩者同時(shí)擁有所有權(quán),后果未定義。 auto_ptr<string>auto_ptr_str1(auto_ptr_str2.release());//OK!str2釋放了所有權(quán)。

              3、不能用一個(gè)指向“內(nèi)存不是通過(guò)應(yīng)用new表達(dá)式分配的”指針來(lái)初始化或者賦值auto_ptr。如果這樣做了,delete表達(dá)式會(huì)被應(yīng)用在不是動(dòng)態(tài)分配的指針上,這將導(dǎo)致未定義的程序行為。

            C風(fēng)格字符串結(jié)尾空字符問(wèn)題

            代碼:
            char *str="hello world!"; //str末尾自動(dòng)加上一個(gè)結(jié)尾空字符,但strlen不計(jì)該空字符。 char *str2=new char[strlen(str)+1] // +1用來(lái)存放結(jié)尾空字符。


            定位new表達(dá)式
              頭文件:<new>
              形式:new (place_address) type-specifier
              該語(yǔ)句可以允許程序員將對(duì)象創(chuàng)建在已經(jīng)分配好的內(nèi)存中,允許程序員預(yù)分配大量的內(nèi)存供以后通過(guò)這種形式的new表達(dá)式創(chuàng)建對(duì)象。其中place_address必須是一個(gè)指針。例如:
            代碼:
            char *buf=new char[sizeof(myclass-type)*16]; myclass-type *pb=new (buf) myclass-type; //使用預(yù)分配空間來(lái)創(chuàng)建對(duì)象 // ... delete [] buf; // 無(wú)須 delete pb。


            名字空間namespace

              1、namespace的定義可以是不連續(xù)的(即namespace的定義是可以積累的),即,同一個(gè)namespace可以在不同的文件中定義,分散在不同文件中的同一個(gè)namespace中的內(nèi)容彼此可見(jiàn)。這對(duì)生成一個(gè)庫(kù)很有幫助,可以使我們更容易將庫(kù)的源代碼組織成接口和實(shí)現(xiàn)部分。如:在頭文件(.h文件)的名字空間部分定義庫(kù)接口;在實(shí)現(xiàn)文件(如.c或.cpp文件)的名字空間部分定義庫(kù)實(shí)現(xiàn)。名字空間定義可積累的特性是“向用戶隱藏實(shí)現(xiàn)細(xì)節(jié)”必需的,它允許把不同的實(shí)現(xiàn)文件(如.c或.cpp文件)編譯鏈接到一個(gè)程序中,而不會(huì)有編譯錯(cuò)誤和鏈接錯(cuò)誤。
              2、全局名字空間成員,可以用“::member_name”的方式引用。當(dāng)全局名字空間的成員被嵌套的局部域中聲明的名字隱藏時(shí),就可以采用這種方法引用全局名字空間成員。
              3、名字空間成員可以被定義在名字空間之外。但是,只有包圍該成員聲明的名字空間(也就是該成員聲明所在的名字空間及其外圍名字空間)才可以包含它的定義。
              尤其要注意的是#include語(yǔ)句的次序。假定名字空間成員mynamespace::member_i的聲明在文件dec.h中,且#include "dec.h"語(yǔ)句置于全局名字空間,那么在include語(yǔ)句之后定義的其他名字空間內(nèi),mynamespace::member_i的聲明均可見(jiàn)。即,mynamespace::member_i可以在#include "dec.h"之后的任何地方任何名字空間內(nèi)定義。
              4、未命名的名字空間。我們可以用未命名的名字空間聲明一個(gè)局部于某一文件的實(shí)體。未命名的名字空間可以namespace開(kāi)頭,其后不需名字,而用一對(duì)花括號(hào)包含名字空間聲明塊。如:
            代碼:
            // 其他代碼略 namespace { void mesg() { cout<<"**********\n"; } } int main() { mesg(); //正確     //... return 0; }
              由于未命名名字空間的成員是程序?qū)嶓w,所以mesg()可以在程序整個(gè)執(zhí)行期間被調(diào)用。但是,未命名名字空間成員只在特定的文件中可見(jiàn),在構(gòu)成程序的其他文件中是不可以見(jiàn)的。未命名名字空間的成員與被聲明為static的全局實(shí)體具有類似的特性。在C中,被聲明為static的全局實(shí)體在聲明它的文件之外是不可見(jiàn)的。

            using關(guān)鍵字
              1、using聲明與using指示符:前者是聲明某名字空間內(nèi)的一個(gè)成員,后者是使用整個(gè)名字空間。例如:
            代碼:
            using cpp_primer::matrix; // ok,using聲明 using namespace cpp_primer; //ok,using指示符
              2、 該using指示符語(yǔ)句可以加在程序文件的幾乎任何地方,包括文件開(kāi)頭(#include語(yǔ)句之前)、函數(shù)內(nèi)部。不過(guò)用using指定的名字空間作用域(生命周期)受using語(yǔ)句所在位置的生命周期約束。如,函數(shù)內(nèi)部使用“using namespace myspacename;”則 myspacename僅在該函數(shù)內(nèi)部可見(jiàn)。
              3、可以用using語(yǔ)句指定多個(gè)名字空間,使得多個(gè)名字空間同時(shí)可見(jiàn)。但這增加了名字污染的可能性,而且只有在使用各名字空間相同成員時(shí)由多個(gè)using指示符引起的二義性錯(cuò)誤才能被檢測(cè)到,這將給程序的檢測(cè)、擴(kuò)展、移植帶來(lái)很大的隱患。因此,因該盡量使用using聲明而不是濫用using指示符。

            重載函數(shù)
              1、如果兩個(gè)函數(shù)的參數(shù)表中參數(shù)的個(gè)數(shù)或者類型不同,則認(rèn)為這兩個(gè)函數(shù)是重載的。
              如果兩個(gè)函數(shù)的返回類型和參數(shù)表精確匹配,則第二個(gè)聲明被視為第一個(gè)的重復(fù)聲明,與參數(shù)名無(wú)關(guān)。如 void print(string& str)與void print(string&)是一樣的。
              如果兩個(gè)函數(shù)的參數(shù)表相同,但是返回類型不同,則第二個(gè)聲明被視為第一個(gè)的錯(cuò)誤重復(fù)聲明,會(huì)標(biāo)記為編譯錯(cuò)誤。
              如果在兩個(gè)函數(shù)的參數(shù)表中,只有缺省實(shí)參不同,則第二個(gè)聲明被視為第一個(gè)的重復(fù)聲明。如int max(int *ia,int sz)與int max(int *, int=10)。
              參數(shù)名類型如果是由typedef提供的,并不算作新類型,而應(yīng)該當(dāng)作typedef的原類型。
              當(dāng)參數(shù)類型是const或者volatile時(shí),分兩種情況:對(duì)于實(shí)參按值傳遞時(shí),const、volatile修飾符可以忽略;對(duì)于把const、volatile應(yīng)用在指針或者引用參數(shù)指向的類型時(shí),const、volatile修飾符對(duì)于重載函數(shù)的聲明是有作用的。例如:
            代碼:
            //OK,以下兩個(gè)聲明其實(shí)一樣 void func(int i); void func(const int i); //Error,無(wú)法通過(guò)編譯,因?yàn)閒unc函數(shù)被定義了兩次。 void func(int i){} void func(const int i){} //OK,聲明了不同的函數(shù) void func2(int *); void func2(const int *); //OK,聲明了不同的函數(shù) void func3(int&); void func3(const int&);
              2、鏈接指示符extern "C"只能指定重載函數(shù)集中的一個(gè)函數(shù)。原因與內(nèi)部名編碼有關(guān),在大多數(shù)編譯器內(nèi)部,每個(gè)函數(shù)明及其相關(guān)參數(shù)表都被作為一個(gè)惟一的內(nèi)部名編碼,一般的做法是把參數(shù)的個(gè)數(shù)和類型都進(jìn)行編碼,然后將其附在函數(shù)名后面。但是這種編碼不使用于用鏈接指示符extern "C"聲明的函數(shù),這就是為什么在重載函數(shù)集合中只有一個(gè)函數(shù)可以被聲明為extern "C"的原因,具有不同的參數(shù)表的兩個(gè)extern "C"的函數(shù)會(huì)被鏈接編輯器視為同一函數(shù)。例如,包含以下兩個(gè)聲明的程序是非法的。
            代碼:
            //error:一個(gè)重載函數(shù)集中有兩個(gè)extern "C"函數(shù) extern "C" void print(const char*); extern "C" void print(int);

            函數(shù)模板
              1、定義函數(shù)模板:
            代碼:
            template <typename/class identifier, ...> [inline/extern] ReturnType FunctionName(FuncParameters...) {   //definition of a funciton template... }

            posted @ 2006-06-29 15:11 井泉 閱讀(294) | 評(píng)論 (1)編輯 收藏

            劃分全局名字空間

            ?
            ?

            條款28: 劃分全局名字空間

            全局空間最大的問(wèn)題在于它本身僅有一個(gè)。在大的軟件項(xiàng)目中,經(jīng)常會(huì)有不少人把他們定義的名字都放在這個(gè)單一的空間中,從而不可避免地導(dǎo)致名字沖突。例如,假設(shè)library1.h定義了一些常量,其中包括:

            const double lib_version = 1.204;

            類似的,library2.h也定義了:

            const int lib_version = 3;

            很顯然,如果某個(gè)程序想同時(shí)包含library1.h和library2.h就會(huì)有問(wèn)題。對(duì)于這類問(wèn)題,你除了嘴里罵幾句,或給作者發(fā)報(bào)復(fù)性郵件,或自己編輯頭文件來(lái)消除名字沖突外,也沒(méi)其它什么辦法。

            但是,作為程序員,你可以盡力使自己寫的程序庫(kù)不給別人帶來(lái)這些問(wèn)題。例如,可以預(yù)先想一些不大可能造成沖突的某種前綴,加在每個(gè)全局符號(hào)前。當(dāng)然得承認(rèn),這樣組合起來(lái)的標(biāo)識(shí)符看起來(lái)不是那么令人舒服。

            另一個(gè)比較好的方法是使用c++ namespace。namespace本質(zhì)上和使用前綴的方法一樣,只不過(guò)避免了別人總是看到前綴而已。所以,不要這么做:

            const double sdmbook_version = 2.0;????? // 在這個(gè)程序庫(kù)中,
            ???????????????????????????????????????? // 每個(gè)符號(hào)以"sdm"開(kāi)頭
            class sdmhandle { ... };????????????????

            sdmhandle& sdmgethandle();???????????? // 為什么函數(shù)要這樣聲明?
            ?????????????????????????????????????? // 參見(jiàn)條款47

            而要這么做:

            namespace sdm {
            ? const double book_version = 2.0;
            ? class handle { ... };
            ? handle& gethandle();
            }

            用戶于是可以通過(guò)三種方法來(lái)訪問(wèn)這一名字空間里的符號(hào):將名字空間中的所有符號(hào)全部引入到某一用戶空間;將部分符號(hào)引入到某一用戶空間;或通過(guò)修飾符顯式地一次性使用某個(gè)符號(hào):

            void f1()
            {
            ? using namespace sdm;?????????? // 使得sdm中的所有符號(hào)不用加
            ???????????????????????????????? // 修飾符就可以使用

            ? cout << book_version;????????? // 解釋為sdm::book_version
            ? ...

            ? handle h = gethandle();??????? // handle解釋為sdm::handle,
            ???????????????????????????????? // gethandle解釋為sdm::gethandle
            ? ...???????????????????????????

            }

            void f2()
            {
            ? using sdm::book_version;??????? // 使得僅book_version不用加
            ???????????????????????????????? // 修飾符就可以使用

            ? cout << book_version;?????????? // 解釋為
            ????????????????????????????????? // sdm::book_version
            ? ...

            ? handle h = gethandle();???????? // 錯(cuò)誤! handle和gethandle
            ????????????????????????????????? // 都沒(méi)有引入到本空間
            ? ...????????????????????????????

            }

            void f3()
            {
            ? cout << sdm::book_version;????? // 使得book_version
            ????????????????????????????????? // 在本語(yǔ)句有效
            ? ...????????????????????????????

            ? double d = book_version;??????? // 錯(cuò)誤! book_version
            ????????????????????????????????? // 不在本空間

            ? handle h = gethandle();???????? // 錯(cuò)誤! handle和gethandle
            ????????????????????????????????? // 都沒(méi)有引入到本空間
            ? ...???????????????????????????

            }

            (有些名字空間沒(méi)有名字。這種沒(méi)命名的名字空間一般用于限制名字空間內(nèi)部元素的可見(jiàn)性。詳見(jiàn)條款m31。)

            名字空間帶來(lái)的最大的好處之一在于:潛在的二義不會(huì)造成錯(cuò)誤(參見(jiàn)條款26)。所以,從多個(gè)不同的名字空間引入同一個(gè)符號(hào)名不會(huì)造成沖突(假如確實(shí)真的從不使用這個(gè)符號(hào)的話)。例如,除了名字空間sdm外,假如還要用到下面這個(gè)名字空間:

            namespace acmewindowsystem {

            ? ...

            ? typedef int handle;

            ? ...

            }

            只要不引用符號(hào)handle,使用sdm和acmewindowsystem時(shí)就不會(huì)有沖突。假如真的要引用,可以明確地指明是哪個(gè)名字空間的handle:

            void f()
            {
            ? using namespace sdm;???????????????? // 引入sdm里的所有符號(hào)
            ? using namespace acmewindowsystem;??? // 引入acme里的所有符號(hào)

            ? ...????????????????????????????????? // 自由地引用sdm
            ?????????????????????????????????????? // 和acme里除handle之外
            ?????????????????????????????????????? // 的其它符號(hào)

            ? handle h;??????????????????????????? // 錯(cuò)誤! 哪個(gè)handle?

            ? sdm::handle h1;????????????????????? // 正確, 沒(méi)有二義

            ? acmewindowsystem::handle h2;???????? // 也沒(méi)有二義

            ? ...

            }

            假如用常規(guī)的基于頭文件的方法來(lái)做,只是簡(jiǎn)單地包含sdm.h和acme.h,這樣的話,由于handle有多個(gè)定義,編譯將不能通過(guò)。

            名字空間的概念加入到c++標(biāo)準(zhǔn)的時(shí)間相對(duì)較晚,所以有些人會(huì)認(rèn)為它不太重要,可有可無(wú)。但這種想法是錯(cuò)誤的,因?yàn)閏++標(biāo)準(zhǔn)庫(kù)(參見(jiàn)條款49)里幾乎所有的東西都存在于名字空間std之中。這可能令你不以為然,但它卻以一種直接的方式影響到你:這就是為什么c++提供了那些看起來(lái)很有趣的、沒(méi)有擴(kuò)展名的頭文件,如<iostream>, <string>等。詳細(xì)介紹參見(jiàn)條款49。

            由于名字空間的概念引入的時(shí)間相對(duì)較晚,有些編譯器可能不支持。就算是這樣,那也沒(méi)理由污染全局名字空間,因?yàn)榭梢杂胹truct來(lái)近似實(shí)現(xiàn)namespace。可以這樣做:先創(chuàng)建一個(gè)結(jié)構(gòu)用以保存全局符號(hào)名,然后將這些全局符號(hào)名作為靜態(tài)成員放入結(jié)構(gòu)中:

            // 用于模擬名字空間的一個(gè)結(jié)構(gòu)的定義
            struct sdm {
            ? static const double book_version;
            ? class handle { ... };
            ? static handle& gethandle();
            };

            const double sdm::book_version = 2.0;????? // 靜態(tài)成員的定義

            現(xiàn)在,如果有人想訪問(wèn)這些全局符號(hào)名,只用簡(jiǎn)單地在它們前面加上結(jié)構(gòu)名作為前綴:

            void f()
            {
            ? cout << sdm::book_version;

            ? ...

            ? sdm::handle h = sdm::gethandle();

            ? ...
            }

            但是,如果全局范圍內(nèi)實(shí)際上沒(méi)有名字沖突,用戶就會(huì)覺(jué)得加修飾符麻煩而多余。幸運(yùn)的是,還是有辦法來(lái)讓用戶選擇使用它們或忽略它們。

            對(duì)于類型名,可以用類型定義(typedef)來(lái)顯式地去掉空間引用。例如,假設(shè)結(jié)構(gòu)s(模擬的名字空間)內(nèi)有個(gè)類型名t,可以這樣用typedef來(lái)使得t成為s::t的同義詞:

            typedef sdm::handle handle;

            對(duì)于結(jié)構(gòu)中的每個(gè)(靜態(tài))對(duì)象x,可以提供一個(gè)(全局)引用x,并初始化為s::x:

            const double& book_version = sdm::book_version;

            老實(shí)說(shuō),如果讀了條款47,你就會(huì)不喜歡定義一個(gè)象book_version這樣的非局部靜態(tài)對(duì)象。(你就會(huì)用條款47中所介紹的函數(shù)來(lái)取代這樣的對(duì)象)

            處理函數(shù)的方法和處理對(duì)象一樣,但要注意,即使定義函數(shù)的引用是合法的,但代碼的維護(hù)者會(huì)更喜歡你使用函數(shù)指針:

            sdm::handle& (* const gethandle)() =????? // gethandle是指向sdm::gethandle
            ? sdm::gethandle;???????????????????????? // 的const 指針 (見(jiàn)條款21)

            注意gethandle是一個(gè)常指針。因?yàn)槟惝?dāng)然不想讓你的用戶將它指向別的什么東西,而不是sdm::gethandle,對(duì)不對(duì)?

            (如果真想知道怎么定義一個(gè)函數(shù)的引用,看看下面:

            sdm::handle& (&gethandle)() =????? // gethandle是指向
            ? sdm::gethandle;????????????????? // sdm::gethandle的引用

            我個(gè)人認(rèn)為這樣的做法也很好,但你可能以前從沒(méi)見(jiàn)到過(guò)。除了初始化的方式外,函數(shù)的引用和函數(shù)的常指針在行為上完全相同,只是函數(shù)指針更易于理解。)

            有了上面的類型定義和引用,那些不會(huì)遭遇全局名字沖突的用戶就會(huì)使用沒(méi)有修飾符的類型和對(duì)象名;相反,那些有全局名字沖突的用戶就會(huì)忽略類型和引用的定義,代之以帶修飾符的符號(hào)名。還要注意的是,不是所有用戶都想使用這種簡(jiǎn)寫名,所以要把類型定義和引用放在一個(gè)單獨(dú)的頭文件中,不要把它和(模擬namespace的)結(jié)構(gòu)的定義混在一起。

            struct是namespace的很好的近似,但實(shí)際上還是相差很遠(yuǎn)。它在很多方面很欠缺,其中很明顯的一點(diǎn)是對(duì)運(yùn)算符的處理。如果運(yùn)算符被定義為結(jié)構(gòu)的靜態(tài)成員,它就只能通過(guò)函數(shù)調(diào)用來(lái)使用,而不能象常規(guī)的運(yùn)算符所設(shè)計(jì)的那樣,可以通過(guò)自然的中綴語(yǔ)法來(lái)使用:

            // 定義一個(gè)模擬名字空間的結(jié)構(gòu),結(jié)構(gòu)內(nèi)部包含widgets的類型
            // 和函數(shù)。widgets對(duì)象支持operator+進(jìn)行加法運(yùn)算
            struct widgets {
            ? class widget { ... };


            ? // 參見(jiàn)條款21:為什么返回const
            ? static const widget operator+(const widget& lhs,
            ??????????????????????????????? const widget& rhs);

            ? ...

            };

            // 為上面所述的widge和operator+
            // 建立全局(無(wú)修飾符的)名稱

            typedef widgets::widget widget;


            const widget (* const operator+)(const widget&,??????? // 錯(cuò)誤!
            ???????????????????????????????? const widget&);?????? // operator+不能是指針名
            ?

            widget w1, w2, sum;

            sum = w1 + w2;?????????????????????????? // 錯(cuò)誤! 本空間沒(méi)有聲明
            ???????????????????????????????????????? // 參數(shù)為widgets 的operator+

            sum = widgets::operator+(w1, w2);??????? // 合法, 但不是
            ???????????????????????????????????????? // "自然"的語(yǔ)法

            正因?yàn)檫@些限制,所以一旦編譯器支持,就要盡早使用真正的名字空間。

            posted @ 2006-06-29 15:09 井泉 閱讀(246) | 評(píng)論 (0)編輯 收藏

            C語(yǔ)言高效編程的幾招

            編寫高效簡(jiǎn)潔的C語(yǔ)言代碼,是許多軟件工程師追求的目標(biāo)。本文就工作中的一些體會(huì)和經(jīng)驗(yàn)做相關(guān)的闡述,不對(duì)的地方請(qǐng)各位指教。

            第1招:以空間換時(shí)間

              計(jì)算機(jī)程序中最大的矛盾是空間和時(shí)間的矛盾,那么,從這個(gè)角度出發(fā)逆向思維來(lái)考慮程序的效率問(wèn)題,我們就有了解決問(wèn)題的第1招——以空間換時(shí)間。
            例如:字符串的賦值。
            方法A,通常的辦法:
            #define LEN 32
            char string1 [LEN];
            memset (string1,0,LEN);
            strcpy (string1,“This is a example!!”);
            方法B:
            const char string2[LEN] =“This is a example!”;
            char * cp;
            cp = string2 ;
            (使用的時(shí)候可以直接用指針來(lái)操作。)

              從上面的例子可以看出,A和B的效率是不能比的。在同樣的存儲(chǔ)空間下,B直接使用指針就可以操作了,而A需要調(diào)用兩個(gè)字符函數(shù)才能完成。B的缺點(diǎn)在于靈活性沒(méi)有A好。在需要頻繁更改一個(gè)字符串內(nèi)容的時(shí)候,A具有更好的靈活性;如果采用方法B,則需要預(yù)存許多字符串,雖然占用了大量的內(nèi)存,但是獲得了程序執(zhí)行的高效率。

              如果系統(tǒng)的實(shí)時(shí)性要求很高,內(nèi)存還有一些,那我推薦你使用該招數(shù)。

              該招數(shù)的變招——使用宏函數(shù)而不是函數(shù)。舉例如下:
            方法C:
            #define bwMCDR2_ADDRESS 4
            #define bsMCDR2_ADDRESS 17
            int BIT_MASK(int __bf)
            {
            return ((1U << (bw ## __bf)) - 1) << (bs ## __bf);
            }
            void SET_BITS(int __dst, int __bf, int __val)
            {
            __dst = ((__dst) & ~(BIT_MASK(__bf))) | \
            (((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))
            }

            SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);
            方法D:
            #define bwMCDR2_ADDRESS 4
            #define bsMCDR2_ADDRESS 17
            #define bmMCDR2_ADDRESS BIT_MASK(MCDR2_ADDRESS)
            #define BIT_MASK(__bf) (((1U << (bw ## __bf)) - 1) << (bs ## __bf))
            #define SET_BITS(__dst, __bf, __val) \
            ((__dst) = ((__dst) & ~(BIT_MASK(__bf))) | \
            (((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))

            SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);

              函數(shù)和宏函數(shù)的區(qū)別就在于,宏函數(shù)占用了大量的空間,而函數(shù)占用了時(shí)間。大家要知道的是,函數(shù)調(diào)用是要使用系統(tǒng)的棧來(lái)保存數(shù)據(jù)的,如果編譯器里有棧檢查選項(xiàng),一般在函數(shù)的頭會(huì)嵌入一些匯編語(yǔ)句對(duì)當(dāng)前棧進(jìn)行檢查;同時(shí),CPU也要在函數(shù)調(diào)用時(shí)保存和恢復(fù)當(dāng)前的現(xiàn)場(chǎng),進(jìn)行壓棧和彈棧操作,所以,函數(shù)調(diào)用需要一些CPU時(shí)間。而宏函數(shù)不存在這個(gè)問(wèn)題。宏函數(shù)僅僅作為預(yù)先寫好的代碼嵌入到當(dāng)前程序,不會(huì)產(chǎn)生函數(shù)調(diào)用,所以僅僅是占用了空間,在頻繁調(diào)用同一個(gè)宏函數(shù)的時(shí)候,該現(xiàn)象尤其突出。

              D方法是我看到的最好的置位操作函數(shù),是ARM公司源碼的一部分,在短短的三行內(nèi)實(shí)現(xiàn)了很多功能,幾乎涵蓋了所有的位操作功能。C方法是其變體,其中滋味還需大家仔細(xì)體會(huì)。

            第2招:數(shù)學(xué)方法解決問(wèn)題

              現(xiàn)在我們演繹高效C語(yǔ)言編寫的第二招——采用數(shù)學(xué)方法來(lái)解決問(wèn)題。

              數(shù)學(xué)是計(jì)算機(jī)之母,沒(méi)有數(shù)學(xué)的依據(jù)和基礎(chǔ),就沒(méi)有計(jì)算機(jī)的發(fā)展,所以在編寫程序的時(shí)候,采用一些數(shù)學(xué)方法會(huì)對(duì)程序的執(zhí)行效率有數(shù)量級(jí)的提高。
            舉例如下,求 1~100的和。
            方法E
            int I , j;
            for (I = 1 ;I<=100; I ++){
            j += I;
            }
            方法F
            int I;
            I = (100 * (1+100)) / 2

              這個(gè)例子是我印象最深的一個(gè)數(shù)學(xué)用例,是我的計(jì)算機(jī)啟蒙老師考我的。當(dāng)時(shí)我只有小學(xué)三年級(jí),可惜我當(dāng)時(shí)不知道用公式 N×(N+1)/ 2 來(lái)解決這個(gè)問(wèn)題。方法E循環(huán)了100次才解決問(wèn)題,也就是說(shuō)最少用了100個(gè)賦值,100個(gè)判斷,200個(gè)加法(I和j);而方法F僅僅用了1個(gè)加法,1次乘法,1次除法。效果自然不言而喻。所以,現(xiàn)在我在編程序的時(shí)候,更多的是動(dòng)腦筋找規(guī)律,最大限度地發(fā)揮數(shù)學(xué)的威力來(lái)提高程序運(yùn)行的效率。

            第3招:使用位操作

              實(shí)現(xiàn)高效的C語(yǔ)言編寫的第三招——使用位操作,減少除法和取模的運(yùn)算。

              在計(jì)算機(jī)程序中,數(shù)據(jù)的位是可以操作的最小數(shù)據(jù)單位,理論上可以用“位運(yùn)算”來(lái)完成所有的運(yùn)算和操作。一般的位操作是用來(lái)控制硬件的,或者做數(shù)據(jù)變換使用,但是,靈活的位操作可以有效地提高程序運(yùn)行的效率。舉例如下:
            方法G
            int I,J;
            I = 257 /8;
            J = 456 % 32;
            方法H
            int I,J;
            I = 257 >>3;
            J = 456 - (456 >> 4 << 4);

              在字面上好像H比G麻煩了好多,但是,仔細(xì)查看產(chǎn)生的匯編代碼就會(huì)明白,方法G調(diào)用了基本的取模函數(shù)和除法函數(shù),既有函數(shù)調(diào)用,還有很多匯編代碼和寄存器參與運(yùn)算;而方法H則僅僅是幾句相關(guān)的匯編,代碼更簡(jiǎn)潔,效率更高。當(dāng)然,由于編譯器的不同,可能效率的差距不大,但是,以我目前遇到的MS C ,ARM C 來(lái)看,效率的差距還是不小。相關(guān)匯編代碼就不在這里列舉了。
            運(yùn)用這招需要注意的是,因?yàn)镃PU的不同而產(chǎn)生的問(wèn)題。比如說(shuō),在PC上用這招編寫的程序,并在PC上調(diào)試通過(guò),在移植到一個(gè)16位機(jī)平臺(tái)上的時(shí)候,可能會(huì)產(chǎn)生代碼隱患。所以只有在一定技術(shù)進(jìn)階的基礎(chǔ)下才可以使用這招。

            第4招:匯編嵌入

              高效C語(yǔ)言編程的必殺技,第四招——嵌入?yún)R編。

              “在熟悉匯編語(yǔ)言的人眼里,C語(yǔ)言編寫的程序都是垃圾”。這種說(shuō)法雖然偏激了一些,但是卻有它的道理。匯編語(yǔ)言是效率最高的計(jì)算機(jī)語(yǔ)言,但是,不可能靠著它來(lái)寫一個(gè)操作系統(tǒng)吧?所以,為了獲得程序的高效率,我們只好采用變通的方法 ——嵌入?yún)R編,混合編程。

              舉例如下,將數(shù)組一賦值給數(shù)組二,要求每一字節(jié)都相符。
            char string1[1024],string2[1024];
            方法I
            int I;
            for (I =0 ;I<1024;I++)
            *(string2 + I) = *(string1 + I)
            方法J
            #ifdef _PC_
            int I;
            for (I =0 ;I<1024;I++)
            *(string2 + I) = *(string1 + I);
            #else
            #ifdef _ARM_
            __asm
            {
            MOV R0,string1
            MOV R1,string2
            MOV R2,#0
            loop:
            LDMIA R0!, [R3-R11]
            STMIA R1!, [R3-R11]
            ADD R2,R2,#8
            CMP R2, #400
            BNE loop
            }
            #endif

              方法I是最常見(jiàn)的方法,使用了1024次循環(huán);方法J則根據(jù)平臺(tái)不同做了區(qū)分,在ARM平臺(tái)下,用嵌入?yún)R編僅用128次循環(huán)就完成了同樣的操作。這里有朋友會(huì)說(shuō),為什么不用標(biāo)準(zhǔn)的內(nèi)存拷貝函數(shù)呢?這是因?yàn)樵谠磾?shù)據(jù)里可能含有數(shù)據(jù)為0的字節(jié),這樣的話,標(biāo)準(zhǔn)庫(kù)函數(shù)會(huì)提前結(jié)束而不會(huì)完成我們要求的操作。這個(gè)例程典型應(yīng)用于LCD數(shù)據(jù)的拷貝過(guò)程。根據(jù)不同的CPU,熟練使用相應(yīng)的嵌入?yún)R編,可以大大提高程序執(zhí)行的效率。

              雖然是必殺技,但是如果輕易使用會(huì)付出慘重的代價(jià)。這是因?yàn)椋褂昧饲度雲(yún)R編,便限制了程序的可移植性,使程序在不同平臺(tái)移植的過(guò)程中,臥虎藏龍,險(xiǎn)象環(huán)生!同時(shí)該招數(shù)也與現(xiàn)代軟件工程的思想相違背,只有在迫不得已的情況下才可以采用。切記,切記。

              使用C語(yǔ)言進(jìn)行高效率編程,我的體會(huì)僅此而已。在此以本文拋磚引玉,還請(qǐng)各位高手共同切磋。希望各位能給出更好的方法,大家一起提高我們的編程技巧。

            posted @ 2006-06-27 09:42 井泉 閱讀(262) | 評(píng)論 (1)編輯 收藏

            用C實(shí)現(xiàn)WebService

            一.系統(tǒng)環(huán)境 2
            二.gSOAP的簡(jiǎn)要使用例子 2
            三.圖示說(shuō)明 6
            四.要注意的問(wèn)題 6
            五.參考文檔 7
            六.備注 7

            一.系統(tǒng)環(huán)境
            linux操作系統(tǒng)kernel2.4.2,安裝gsoap2.6到目錄/usr/local/gsoap
            二.gSOAP的簡(jiǎn)要使用例子
            下面是一個(gè)簡(jiǎn)單的例子,實(shí)現(xiàn)一個(gè)加法運(yùn)算的WebService,具體功能是cli端輸入num1和num2,server端返回一個(gè)num1和num2相加的結(jié)果sum。

            1. 首先,我們需要做的是寫一個(gè)函數(shù)聲明文件,來(lái)定義接口函數(shù)ns__add,文件名字為add.h,內(nèi)容如下:

            //gsoap ns service name: add
            //gsoap ns service namespace: http://mail.263.net/add.wsdl
            //gsoap ns service location: http://mail.263.net
            //gsoap ns service executable: add.cgi
            //gsoap ns service encoding: encoded
            //gsoap ns schema namespace: urn:add

            int ns__add( int num1, int num2, int* sum );

            2. 然后我們需要?jiǎng)?chuàng)建文件Makefile,從而利用gsoapcpp2工具由add.h生成一些.xml文件、.c文件和.h文件,這些文件均為自動(dòng)生成,Makefile的內(nèi)容如下:

            GSOAP_ROOT=/usr/local/gsoap
            WSNAME=add
            CC=g++ -g -DWITH_NONAMESPACES
            INCLUDE=-I $(GSOAP_ROOT)
            SERVER_OBJS=$(WSNAME)C.o $(WSNAME)Server.o stdsoap2.o
            CLIENT_OBJS=$(GSOAP_ROOT)/env/envC.o $(WSNAME)ClientLib.o stdsoap2.o
            ALL_OBJS=${WSNAME}server.o $(WSNAME)C.o $(WSNAME)Server.o ${WSNAME}test.o ${WSNAME}client.o $(WSNAME)ClientLib.o

            #總的目標(biāo)
            all:server

            ${WSNAME}.wsdl:${WSNAME}.h
            $(GSOAP_ROOT)/soapcpp2 -p$(WSNAME) -i -n -c ${WSNAME}.h

            stdsoap2.o:$(GSOAP_ROOT)/stdsoap2.c
            $(CC) -c $?

            #編譯一樣生成規(guī)則的.o文件
            $(ALL_OBJS):%.o:%.c
            $(CC) -c $? $(INCLUDE)

            #編譯服務(wù)器端
            server:Makefile ${WSNAME}.wsdl ${WSNAME}server.o $(SERVER_OBJS)
            $(CC) ${WSNAME}server.o $(SERVER_OBJS) -o ${WSNAME}server

            #編譯客戶端
            client:Makefile ${WSNAME}.wsdl ${WSNAME}client.c ${WSNAME}test.c $(ALL_OBJS) stdsoap2.o
            $(CC) ${WSNAME}test.o ${WSNAME}client.o $(CLIENT_OBJS) -o ${WSNAME}test

            cl:
            rm -f *.o *.xml *.a *.wsdl *.nsmap $(WSNAME)H.h $(WSNAME)C.c $(WSNAME)Server.c $(WSNAME)Client.c $(WSNAME)Stub.* $(WSNAME)$(WSNAME)Proxy.* $(WSNAME)$(WSNAME)Object.* $(WSNAME)ServerLib.c $(WSNAME)ClientLib.c $(WSNAME)server ns.xsd $(WSNAME)test

            3.我們先來(lái)做一個(gè)server端,創(chuàng)建文件addserver.c文件,內(nèi)容如下:

            #include "addH.h"
            #include "add.nsmap"

            int main(int argc, char **argv)
            {
            int m, s; /* master and slave sockets */
            struct soap add_soap;
            soap_init(&add_soap);
            soap_set_namespaces(&add_soap, add_namespaces);
            if (argc < 2)
            {
            printf("usage: %s <server_port> \n", argv[0]);
            exit(1);
            }
            else
            {
            m = soap_bind(&add_soap, NULL, atoi(argv[1]), 100);
            if (m < 0)
            {
            soap_print_fault(&add_soap, stderr);
            exit(-1);
            }
            fprintf(stderr, "Socket connection successful: master socket = %d\n", m);
            for ( ; ; )
            {
            s = soap_accept(&add_soap);
            if (s < 0)
            {
            soap_print_fault(&add_soap, stderr);
            exit(-1);
            }
            fprintf(stderr, "Socket connection successful: slave socket = %d\n", s);
            add_serve(&add_soap);//該句說(shuō)明該server的服務(wù)
            soap_end(&add_soap);
            }
            }
            return 0;
            }
            //server端的實(shí)現(xiàn)函數(shù)與add.h中聲明的函數(shù)相同,但是多了一個(gè)當(dāng)前的soap連接的參數(shù)
            int ns__add(struct soap *add_soap, int num1, int num2, int *sum)
            {
            *sum = num1 + num2;
            return 0;
            }

            4.讓我們的server跑起來(lái)吧:
            shell>make
            shell>./addserver 8888
            如果終端打印出“Socket connection successful: master socket = 3”,那么你的server已經(jīng)在前臺(tái)run起來(lái)了,應(yīng)該是值得高興的&#61514;。
            打開(kāi)IE,鍵入http://本機(jī)IP:8888,顯示XML,服務(wù)已經(jīng)啟動(dòng),終端打印出“Socket connection successful: slave socket = 4”,表示服務(wù)接收到了一次soap的連接。

            5.讓我們?cè)賮?lái)寫個(gè)客戶端(這個(gè)只是將soap的客戶端函數(shù)封裝一下,具體的調(diào)用參見(jiàn)下面的addtest.c),創(chuàng)建文件addclient.c,內(nèi)容如下:

            #include "addStub.h"
            #include "add.nsmap"
            /**
            * 傳入?yún)?shù):server:server的地址
            * num1,num2:需要相加的數(shù)
            * 傳出參數(shù):sum:num1和num2相加的結(jié)果
            * 返回值:0為成功,其他為失敗
            */
            int add( const char* server, int num1, int num2, int *sum )
            {
            struct soap add_soap;
            int result = 0;
            soap_init(&add_soap);
            soap_set_namespaces(&add_soap, add_namespaces);

            //該函數(shù)是客戶端調(diào)用的主要函數(shù),后面幾個(gè)參數(shù)和add.h中聲明的一樣,前面多了3個(gè)參數(shù),函數(shù)名是接口函數(shù)名ns__add前面加上soap_call_
            soap_call_ns__add( &add_soap, server, "", num1, num2, sum );
            if(add_soap.error)
            {
            printf("soap error:%d,%s,%s\n", add_soap.error, *soap_faultcode(&add_soap), *soap_faultstring(&add_soap) );
            result = add_soap.error;
            }
            soap_end(&add_soap);
            soap_done(&add_soap);
            return result;
            }

            6.我們最終寫一個(gè)可以運(yùn)行的客戶端調(diào)用程序,創(chuàng)建文件addtest.c,內(nèi)容如下:

            #include <stdio.h>
            #include <stdlib.h>

            int add(const char* server, int num1, int num2, int *sum);

            int main(int argc, char **argv)
            {
            int result = -1;
            char* server="http://localhost:8888";
            int num1 = 0;
            int num2 = 0;
            int sum = 0;
            if( argc < 3 )
            {
            printf("usage: %s num1 num2 \n", argv[0]);
            exit(0);
            }

            num1 = atoi(argv[1]);
            num2 = atoi(argv[2]);

            result = add(server, num1, num2, &sum);
            if (result != 0)
            {
            printf("soap err,errcode = %d\n", result);
            }
            else
            {
            printf("%d+%d=%d\n", num1, num2, sum );
            }
            return 0;
            }

            7.讓我們的client端和server端通訊
            shell>make client
            shell>./addtest 7 8
            當(dāng)然,你的server應(yīng)該還在run,這樣得到輸出結(jié)果7+8=15,好了,你成功完成了你的第一個(gè)C寫的WebService,恭喜。
            三.圖示說(shuō)明

            四.要注意的問(wèn)題
            1. add.h文件前面的幾句注釋不能刪除,為soapcpp2需要識(shí)別的標(biāo)志
            2. 接口函數(shù)的返回值只能是int,是soap調(diào)用的結(jié)果,一般通過(guò)soap.error來(lái)判斷soap的連接情況,這個(gè)返回值沒(méi)有用到。
            3. 接口函數(shù)的最后一個(gè)參數(shù)為傳出參數(shù),如果需要傳出多個(gè)參數(shù),需要自己定義一個(gè)結(jié)構(gòu)將返回項(xiàng)封裝。
            4. 在.h文件中不能include別的.h文件,可能不能生效,需要用到某些結(jié)構(gòu)的時(shí)候需要在該文件中直接聲明。
            5. 如果客戶端的調(diào)用不需要返回值,那么最后一個(gè)參數(shù)
            五.參考文檔
            1.gsoap主頁(yè)
            http://gsoap2.sourceforge.net

            2.跟我一起寫Makefile
            http://dev.csdn.net/develop/article/20/20025.shtm

            3.Web Services: A Technical Introduction(機(jī)械工業(yè)出版社)
            六.備注
            192.168.18.233和192.168.18.234的/usr/local/gsoap目錄下的3個(gè)需要的文件及一個(gè)env目錄,不是編譯安裝的,是在別的地方編譯好了直接copy過(guò)來(lái)的(實(shí)際編譯結(jié)果中還有wsdl2h工具及其他一些文件,但是我們的實(shí)際開(kāi)發(fā)中只是用到了這3個(gè)文件及env目錄)。因?yàn)闀r(shí)間倉(cāng)促,本人還沒(méi)有時(shí)間研究編譯的問(wèn)題,相關(guān)細(xì)節(jié)可以查看參考文檔1。
            在192.168.18.233的/home/weiqiong/soap/sample目錄下及192.168.18.234的/tmp/soap/sample目錄下有本文講到的加法運(yùn)算的例子。


            全文結(jié)束

            posted @ 2006-06-27 09:28 井泉 閱讀(642) | 評(píng)論 (0)編輯 收藏

            system V消息機(jī)制

            #ifndef MSG_H
            #define MSG_H
            //msgid
            #define LISTEN_THREAD??7
            #define CENTER_THREAD??0
            #define SEND_THREAD???2
            #define REV_THREAD???3
            #define TIME_THREAD???4
            //lp
            #define EXIT????0
            #define SEND_SGIP_SUBMIT?1
            #define SEND_SGIP_BIND
            #define SEND_SGIP_R
            #define SEND_SGIP_UNBIND
            #define SEND_SGIP_UNBIND_R
            #define REV_SGIP_SOCKET
            //wp
            #define SEND_SUCCESS
            #define PACK_FAIL
            #define SEND_FAIL
            enum mgnt_cmd_type
            {
            ?event_login???????? = 0,
            ??event_logout,
            ??event_sip_init_para,
            ??event_log_init_para,
            ??event_sip_clean,
            ??event_set_dtmf_mode,
            ??event_set_dhcp,
            ??event_set_pppoe,
            ??
            ??event_pstn_call_out,
            ??event_sip_call_out,
            ??event_answer_sipcall,
            ??event_release_sipcall,
            ??event_loadBMP_init,
            ??
            ??
            ??event_pstn_call_in=20,
            ??event_sip_call_in,
            ??event_remote_release_call,
            ??event_remote_establish_call,
            ??event_remote_cancelcall,
            ??event_login_return,
            ??event_remote_ignore,

            ??event_set_pstn_ring,
            ??event_set_sip_ring,
            ??event_set_alarm_ring,
            ??event_set_ring_volume
            ??
            };

            typedef struct msgbuf
            {
            ?long???msgtype;
            ?unsigned long?msgid;
            ?unsigned long?lp;
            ?unsigned long?wp;
            }MSGBuf, *pMSGBuf;


            int vvMSGSend(long thread_id, unsigned long msgid, unsigned long lp, unsigned long wp);
            int vvMSGRecv(long thread_id, struct msgbuf *msg, int is_wait);


            #ifndef _WINDOWS

            #include <sys/types.h>
            #include <sys/ipc.h>
            #include <sys/msg.h>
            #include <unistd.h>

            #define?MSG_FILE_NAME??? "/rw/"????? //"/mnt/"
            #define MSG_FLAG???(IPC_CREAT? | 00666)
            //| IPC_EXCL
            ?


            typedef struct sendMsg
            {
            ?int sd;
            ?void *content;
            }SendMsg, *pSendMsg;


            #endif

            #endif //MSG_H





            #include "vvmsg.h"
            #include <ps_log.h>

            #ifndef _WINDOWS
            #include <phone_Interface.h>
            #include <pthread.h>
            #include <basegdi.h>
            #include <keyboard.h>
            //#include "hash.h"
            extern? pthread_t g_incomingthread;
            //extern? hash_table table;
            #endif

            ?

            int vvMSGSend(long thread_id, unsigned long msgid, unsigned long lp, unsigned long wp)
            {?
            ?struct msgbuf bmsg;
            #ifndef _WINDOWS
            ?key_t key;

            ?int msg_id;
            ?bmsg.msgtype = thread_id;
            ?bmsg.msgid = msgid;
            ?bmsg.lp = lp;
            ?bmsg.wp = wp;

            ?if((key = ftok(MSG_FILE_NAME,'a')) == -1)
            ?{
            ??return -1;
            ?}

            ?if((msg_id = msgget(key,MSG_FLAG)) == -1)
            ?{
            ??return -1;
            ?}

            ?if (msgsnd(msg_id, &bmsg, sizeof(struct msgbuf), IPC_NOWAIT) == -1)
            ?{
            ??return -1;
            ?}
            #endif
            ?return 1;

            }

            int vvMSGRecv(long thread_id, struct msgbuf *msg, int is_wait)
            {
            ?#ifndef _WINDOWS
            ?key_t key;??
            ?int msg_id;
            ?if((key = ftok(MSG_FILE_NAME,'a')) == -1)
            ?{
            ??printf("Recv msg error 1!\n");
            ??return -1;
            ?}
            ?if((msg_id = msgget(key,MSG_FLAG)) == -1)
            ?{
            ??printf("Recv msg error 2!\n");
            ??return -1;
            ?}
            ?if (is_wait != 1)
            ?{
            ??if (msgrcv(msg_id, msg, sizeof(struct msgbuf), thread_id, IPC_NOWAIT) == -1)
            ??{
            ???printf("Recv msg error 3!\n");
            ???return -1;
            ??}?
            ?}
            ?else
            ?{
            ??if (msgrcv(msg_id, msg, sizeof(struct msgbuf), thread_id, 0) == -1)
            ??{
            ???//printf("Recv msg error 4!\n");
            ???return -1;
            ??}
            ?}
            ??#endif
            ?return 1;

            }

            void *skype_thread_start(void *arg)
            {
            ?#ifndef _WINDOWS
            ?MSGBuf msg;
            ?pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);//設(shè)置線程屬性
            ?pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,0);
            ?for (;;)
            ?{
            ??pthread_testcancel();//設(shè)置取消點(diǎn)
            ??if (vvMSGRecv((long)g_incomingthread, &msg, 1) == -1)
            ???continue;
            ??
            ??// analysis the message
            ??switch (msg.msgid)
            ??{
            ???ps_show_str(log_DEBUG, "vvmsg event!!!!!!!!!!!!!!!%d\r\n", msg.msgid);
            ??case event_login:
            ???{
            ????userLogin();
            ????
            ???}
            ???break;
            ??case event_logout:
            ???{
            ????userLogout();
            ???}
            ??case event_sip_clean:
            ???{
            ????SipClean();
            ???}
            ???break;
            ??case event_sip_init_para:
            ???{
            ????ps_show_str(log_DEBUG, "event before################UpdateSipInitPara\r\n");
            ????UpdateSipInitPara();
            ????ps_show_str(log_DEBUG, "event after##################UpdateSipInitPara\r\n");
            ???}
            ???break;
            ??case event_log_init_para:
            ???{
            ????UpdateLogInitPara();
            ???}
            ???break;
            ??case event_set_dtmf_mode:
            ???{
            ????int i = (int)msg.lp;
            ????ps_show_str(log_DEBUG, "event_set_dtmf_mode########################%d\r\n", i);
            ????SetDTMFMode(i);
            ???}
            ???break;
            ??case event_set_dhcp:
            ???{
            ????SetDHCP();
            ???}
            ???break;
            ??case event_set_pppoe:
            ???{
            ????SetPPPOE();
            ???}
            ???break;

            ??case event_pstn_call_out:
            ???{
            ????pstncall((char*)msg.lp);
            ???}
            ???break;
            ??case event_sip_call_out:
            ???{
            ????
            ????sipcall((char*)msg.lp);
            ???}
            ???break;

            ??case event_answer_sipcall:
            ???{
            ????callmgr_answercall((LINE_ID_T *)msg.lp);
            ???}
            ???break;
            ??????? case event_release_sipcall:
            ???{
            ????callmgr_releasecall((LINE_ID_T *)msg.lp);
            ???}
            ???break;
            ??case event_loadBMP_init:
            ???{
            ????CreateSysBmp();
            ????
            ???}
            ???break;
            ???

            ?

            ?

            ?

            ?

            ?

            ?

            ?

            ??case event_pstn_call_in:
            ???{
            ????LINE_ID_T *line = (LINE_ID_T *)msg.wp;
            ????sipcome_create(line);
            ???}
            ???break;
            ??case event_sip_call_in:
            ???{
            ????LINE_ID_T *line = (LINE_ID_T *)msg.wp;
            ????sipcome_create(line);
            ???}
            ???break;
            ?
            ??case event_remote_establish_call:
            ???{
            ????
            ????LINE_ID_T *line = (LINE_ID_T *)msg.wp;
            ????pstnchat_create(line);
            ????if(g_Hwnd[HWND_CALLOUT]!=0)
            ????calling_destroy(g_Hwnd[HWND_CALLOUT]);
            ????
            ???}
            ???break;
            ??case event_remote_cancelcall:
            ???{
            ????if(g_Hwnd[HWND_CALLIN]!=0)
            ???SendMessage(g_Hwnd[HWND_CALLIN],MSG_KEYDOWN,KEY_SW_RSK,0);
            ???}
            ???break;
            ??case event_remote_release_call:
            ???{
            ????if(g_Hwnd[HWND_CHAT]!=0)
            ????SendMessage(g_Hwnd[HWND_CHAT],MSG_KEYDOWN,KEY_SW_RSK,0);
            ???}
            ???break;
            ??case event_login_return:
            ???{
            ????printf("sfds0000000000000000000000000000000dssssssss^^^^^^^^^^^^^^^^^\r\n");
            ????if(g_Hwnd[HWND_MAINSCREEN]!=0)
            ????{
            ?????UpdateWindow(g_Hwnd[HWND_MAINSCREEN],1);
            ????//?SetFocusChild(g_Hwnd[HWND_MAINSCREEN]);
            ????//?ShowWindow(g_Hwnd[HWND_MAINSCREEN], SW_SHOW);
            ????}
            ???}
            ???break;
            ??case event_remote_ignore:
            ???{
            ????if(g_Hwnd[HWND_CALLOUT]!=0)
            ????SendMessage(g_Hwnd[HWND_CALLOUT],MSG_KEYDOWN,KEY_SW_RSK,0);?
            ???}
            ???break;
            ??case event_set_pstn_ring:
            ???{
            ????SetPstnRing((int)msg.lp);
            ???}
            ???break;
            ??case event_set_sip_ring:
            ???{
            ????SetSipRing((int)msg.lp);
            ???}
            ???break;
            ??case event_set_ring_volume:
            ???{
            ????SetRingVolume((int)msg.lp);
            ???}
            ???break;
            ??}

            ?}
            ?#endif
            }

            附(創(chuàng)建線程):if (pthread_create(&g_incomingthread, NULL, skype_thread_start, NULL))
            ??return -1;

            posted @ 2006-06-27 09:23 井泉 閱讀(304) | 評(píng)論 (0)編輯 收藏

            在 C/C++ 中如何構(gòu)造通用的對(duì)象鏈表

            一個(gè)簡(jiǎn)化的問(wèn)題示例

            鏈表的難點(diǎn)在于必須復(fù)制鏈表處理函數(shù)來(lái)處理不同的對(duì)象,即便邏輯是完全相同的。例如兩個(gè)結(jié)構(gòu)類似的鏈表:


            struct Struct_Object_A
              {
                int a;
                int b;
                Struct_Object_A *next;
              
              } OBJECT_A;
              
              typedef struct Struct_Object_B
              {
                int a;
                int b;
                int c;
                Struct_Object_B *next;
              
              } OBJECT_B; 

            上面定義的兩個(gè)結(jié)構(gòu)只有很小的一點(diǎn)差別。OBJECT_B 和 OBJECT_A 之間只差一個(gè)整型變量。但是,在編譯器看來(lái),它們?nèi)匀皇欠浅2煌摹1仨殲榇鎯?chǔ)在鏈表中的每個(gè)對(duì)象復(fù)制用來(lái)添加、刪除和搜索鏈表的函數(shù)。為了解決這個(gè)問(wèn)題,可以使用具有全部三個(gè)變量的一個(gè)聯(lián)合或結(jié)構(gòu),其中整數(shù) c 并不是在所有的情況下都要使用。這可能變得非常復(fù)雜,并會(huì)形成不良的編程風(fēng)格。

            C 代碼解決方案:虛擬鏈表

            此問(wèn)題更好的解決方案之一是虛擬鏈表。虛擬鏈表是只包含鏈表指針的鏈表。對(duì)象存儲(chǔ)在鏈表結(jié)構(gòu)背后。這一點(diǎn)是這樣實(shí)現(xiàn)的,首先為鏈表節(jié)點(diǎn)分配內(nèi)存,接著為對(duì)象分配內(nèi)存,然后將這塊內(nèi)存分配給鏈表節(jié)點(diǎn)指針,如下所示:

            虛擬鏈表結(jié)構(gòu)的一種實(shí)現(xiàn)

            typedef struct liststruct
              {
                liststruct *next;
              
              } LIST, *pLIST;
              
              pLIST Head = NULL;
              
              pLIST AddToList( pLIST Head,
            void * data, size_t datasize )
              {
              pLIST newlist=NULL;
              void *p;
              
                // 分配節(jié)點(diǎn)內(nèi)存和數(shù)據(jù)內(nèi)存
                newlist = (pLIST) malloc
            ( datasize + sizeof( LIST ) );
              
                // 為這塊數(shù)據(jù)緩沖區(qū)指定一個(gè)指針
                p = (void *)( newlist + 1 );
              
                // 復(fù)制數(shù)據(jù)
                memcpy( p, data, datasize );
              
                // 將這個(gè)節(jié)點(diǎn)指定給鏈表的表頭
                if( Head )
                {
                newlist->next = Head;
                }
                else
                newlist->next = NULL;
              
                Head = newlist;
              
                return Head;
              }  

            鏈表節(jié)點(diǎn)現(xiàn)在建立在數(shù)據(jù)值副本的基本之上。這個(gè)版本能很好地處理標(biāo)量值,但不能處理帶有用 malloc 或 new 分配的元素的對(duì)象。要處理這些對(duì)象,LIST 結(jié)構(gòu)需要包含一個(gè)一般的解除函數(shù)指針,這個(gè)指針可用來(lái)在將節(jié)點(diǎn)從鏈表中刪除并解除它之前釋放內(nèi)存(或者關(guān)閉文件,或者調(diào)用關(guān)閉方法)。

            一個(gè)帶有解除函數(shù)的鏈表

            typedef void (*ListNodeDestructor)( void * );
              
              typedef struct liststruct
              {
                ListNodeDestructor DestructFunc;
                liststruct *next;
              
              } LIST, *pLIST;
              
              pLIST AddToList( pLIST Head, void * data, 
            size_t datasize,
              ListNodeDestructor Destructor )
              {
              pLIST newlist=NULL;
              void *p;
              
              
                // 分配節(jié)點(diǎn)內(nèi)存和數(shù)據(jù)內(nèi)存
                newlist = (pLIST) malloc
            ( datasize + sizeof( LIST ) );
              
                // 為這塊數(shù)據(jù)緩沖區(qū)指定一個(gè)指針
                p = (void *)( newlist + 1 );
              
                // 復(fù)制數(shù)據(jù)
                memcpy( p, data, datasize );
              
                newlist->DestructFunc = Destructor;
                
                // 將這個(gè)節(jié)點(diǎn)指定給鏈表的表頭
                if( Head )
                {
                  newlist->next = Head;
                }
                else
                  newlist->next = NULL;
              
                Head = newlist;
              
                return Head;
              }
              
              void DeleteList( pLIST Head )
              {
                pLIST Next;
                while( Head )
                {
                  Next = Head->next;
                  Head->DestructFunc( 
            (void *) Head );
                  free( Head );
                  Head = Next;
                }
              }
              
              typedef struct ListDataStruct
              {
                LPSTR p;
              
              } LIST_DATA, *pLIST_DATA;
              
              void ListDataDestructor( void *p )
              {
                // 對(duì)節(jié)點(diǎn)指針進(jìn)行類型轉(zhuǎn)換
                pLIST pl = (pLIST)p;
              
                // 對(duì)數(shù)據(jù)指針進(jìn)行類型轉(zhuǎn)換
                pLIST_DATA pLD = (pLIST_DATA)
            ( pl + 1 );
              
                delete pLD->p;
              }
              pLIST Head = NULL;
              
              void TestList()
              {
                pLIST_DATA d = new LIST_DATA;
                d->p = new char[24];
                strcpy( d->p, "Hello" ); 
                Head = AddToList( Head, (void *) d,
            sizeof( pLIST_DATA ),
                ListDataDestructor );
                // 該對(duì)象已被復(fù)制,現(xiàn)在刪除原來(lái)的對(duì)象
                delete d;
              
                d = new LIST_DATA;
                d->p = new char[24];
                strcpy( d->p, "World" ); 
                Head = AddToList( Head, (void *) d,
            sizeof( pLIST_DATA ),
                ListDataDestructor );
                delete d;
              
                // 釋放鏈表
                DeleteList( Head );
              }   

            在每個(gè)鏈表節(jié)點(diǎn)中包含同一個(gè)解除函數(shù)的同一個(gè)指針?biāo)坪跏抢速M(fèi)內(nèi)存空間。確實(shí)如此,但只有鏈表始終包含相同的對(duì)象才屬于這種情況。按這種方式編寫鏈表允許您將任何對(duì)象放在鏈表中的任何位置。大多數(shù)鏈表函數(shù)要求對(duì)象總是相同的類型或類。

            虛擬鏈表則無(wú)此要求。它所需要的只是將對(duì)象彼此區(qū)分開(kāi)的一種方法。要實(shí)現(xiàn)這一點(diǎn),您既可以檢測(cè)解除函數(shù)指針的值,也可以在鏈表中所用的全部結(jié)構(gòu)前添加一個(gè)類型值并對(duì)它進(jìn)行檢測(cè)。

            當(dāng)然,如果要將鏈表編寫為一個(gè) C++ 類,則對(duì)指向解除函數(shù)的指針的設(shè)置和存儲(chǔ)只能進(jìn)行一次。

            C++ 解決方案:類鏈表

            本解決方案將 CList 類定義為從 LIST 結(jié)構(gòu)導(dǎo)出的一個(gè)類,它通過(guò)存儲(chǔ)解除函數(shù)的單個(gè)值來(lái)處理單個(gè)存儲(chǔ)類型。請(qǐng)注意添加的 GetCurrentData() 函數(shù),該函數(shù)完成從鏈表節(jié)點(diǎn)指針到數(shù)據(jù)偏移指針的數(shù)學(xué)轉(zhuǎn)換。一個(gè)虛擬鏈表對(duì)象

            // 定義解除函數(shù)指針
              
              typedef void (*ListNodeDestructor)
            ( void * );
              
              // 未添加解除函數(shù)指針的鏈表
              
              typedef struct ndliststruct
              {
                ndliststruct *next;
              
              } ND_LIST, *pND_LIST;
              
              // 定義處理一種數(shù)據(jù)類型的鏈表類
              
              class CList : public ND_LIST
              {
              public:
                CList(ListNodeDestructor);
                ~CList();
                pND_LIST AddToList
            ( void * data, size_t datasize );
                void *GetCurrentData();
                void DeleteList( pND_LIST Head );
              
              
              private:
                pND_LIST m_HeadOfList;
                pND_LIST m_CurrentNode;
                ListNodeDestructor
            m_DestructFunc;
              };
              
              // 用正確的起始值構(gòu)造這個(gè)鏈表對(duì)象
              
              CList::CList(ListNodeDestructor Destructor)
                : m_HeadOfList(NULL), 
            m_CurrentNode(NULL)
              {
                m_DestructFunc = Destructor;
              }
              
              // 在解除對(duì)象以后刪除鏈表
              
              CList::~CList()
              {
                DeleteList(m_HeadOfList);
              }
              
              // 向鏈表中添加一個(gè)新節(jié)點(diǎn)
              
              pND_LIST CList::AddToList
            ( void * data, size_t datasize )
              {
              pND_LIST newlist=NULL;
              void *p;
              
              
                // 分配節(jié)點(diǎn)內(nèi)存和數(shù)據(jù)內(nèi)存
                newlist = (pND_LIST) malloc
            ( datasize + sizeof( ND_LIST ) );
              
                // 為這塊數(shù)據(jù)緩沖區(qū)指定一個(gè)指針
                p = (void *)( newlist + 1 );
              
                // 復(fù)制數(shù)據(jù)
                memcpy( p, data, datasize );
              
                // 將這個(gè)節(jié)點(diǎn)指定給鏈表的表頭
                if( m_HeadOfList )
                {
                  newlist->next = m_HeadOfList;
                }
                else
                  newlist->next = NULL;
              
                m_HeadOfList = newlist;
              
                return m_HeadOfList;
              }
              
              // 將當(dāng)前的節(jié)點(diǎn)數(shù)據(jù)作為 void 類型返回,
            以便調(diào)用函數(shù)能夠?qū)⑺D(zhuǎn)換為任何類型
              
              void * CList::GetCurrentData()
              {
                return (void *)(m_CurrentNode+1);
              }
              
              // 刪除已分配的鏈表
              
              void CList::DeleteList( pND_LIST Head )
              {
                pND_LIST Next;
                while( Head )
                {
                  Next = Head->next;
                  m_DestructFunc( (void *) Head );
                  free( Head );
                  Head = Next;
                }
              }
              
              // 創(chuàng)建一個(gè)要在鏈表中創(chuàng)建和存儲(chǔ)的結(jié)構(gòu)
              
              typedef struct ListDataStruct
              {
                LPSTR p;
              
              } LIST_DATA, *pND_LIST_DATA;
              
              // 定義標(biāo)準(zhǔn)解除函數(shù)
              
              void ClassListDataDestructor( void *p )
              {
                // 對(duì)節(jié)點(diǎn)指針進(jìn)行類型轉(zhuǎn)換
                pND_LIST pl = (pND_LIST)p;
              
                // 對(duì)數(shù)據(jù)指針進(jìn)行類型轉(zhuǎn)換
                pND_LIST_DATA pLD = (pND_LIST_DATA)
            ( pl + 1 );
              
                delete pLD->p;
              }
              
              // 測(cè)試上面的代碼
              
              void MyCListClassTest()
              {
                // 創(chuàng)建鏈表類
              
                CList* pA_List_of_Data =
            new CList(ClassListDataDestructor);
              
                // 創(chuàng)建數(shù)據(jù)對(duì)象
                
                pND_LIST_DATA d = new LIST_DATA;
                d->p = new char[24];
                strcpy( d->p, "Hello" ); 
              
                // 創(chuàng)建指向鏈表頂部的局部指針
              
                pND_LIST Head = NULL;
              
                //向鏈表中添加一些數(shù)據(jù)
              
                Head = pA_List_of_Data->AddToList
            ( (void *) d, 
                sizeof( pND_LIST_DATA ) );
                // 該對(duì)象已被復(fù)制,現(xiàn)在刪除原來(lái)的對(duì)象
                delete d;
              
                // 確認(rèn)它已被存儲(chǔ)
                char * p = ((pND_LIST_DATA) pA_List_of_Data->GetCurrentData())->p;
              
                d = new LIST_DATA;
                d->p = new char[24];
                strcpy( d->p, "World" ); 
                Head = pA_List_of_Data->AddToList
            ( (void *) d, sizeof( pND_LIST_DATA ) );
                // 該對(duì)象已被復(fù)制,現(xiàn)在刪除原來(lái)的對(duì)象
                delete d;
              
                // 確認(rèn)它已被存儲(chǔ)
                p = ((pND_LIST_DATA) 
            pA_List_of_Data->GetCurrentData())->p;
              
                // 刪除鏈表類,析構(gòu)函數(shù)將刪除鏈表
                delete pA_List_of_Data;
              }
              

            小結(jié)

            從前面的討論來(lái)看,似乎僅編寫一個(gè)簡(jiǎn)單的鏈表就要做大量的工作,但這只須進(jìn)行一次。很容易將這段代碼擴(kuò)充為一個(gè)處理排序、搜索以及各種其他任務(wù)的 C++ 類,并且這個(gè)類可以處理任何數(shù)據(jù)對(duì)象或類(在一個(gè)項(xiàng)目中,它處理大約二十個(gè)不同的對(duì)象)。您永遠(yuǎn)不必重新編寫這段代碼。

            posted @ 2006-06-26 19:37 井泉 閱讀(271) | 評(píng)論 (0)編輯 收藏

            僅列出標(biāo)題
            共8頁(yè): 1 2 3 4 5 6 7 8 
            精品久久久久久无码专区| 免费一级欧美大片久久网| 亚洲国产成人久久笫一页| 久久受www免费人成_看片中文 | 99久久香蕉国产线看观香| 99久久国产综合精品女同图片| 色综合久久中文字幕无码| 久久精品免费观看| 亚洲美日韩Av中文字幕无码久久久妻妇 | 中文字幕久久精品| 久久国产色AV免费观看| 久久久久亚洲精品天堂久久久久久| 久久久久久精品无码人妻| 久久97久久97精品免视看秋霞 | 久久久亚洲裙底偷窥综合| 久久er国产精品免费观看2| 久久亚洲熟女cc98cm| 国产激情久久久久影院老熟女免费| 国内精品伊人久久久久777| 国产激情久久久久影院小草 | 国产精品99久久久精品无码| 国产美女久久久| 亚洲午夜无码久久久久| 久久久久国产成人精品亚洲午夜| 国产精品99精品久久免费| 中文精品99久久国产| 午夜精品久久影院蜜桃| 久久精品一区二区影院 | 亚洲国产成人久久综合一| 久久无码高潮喷水| 欧美日韩精品久久久久| 久久天天躁狠狠躁夜夜2020老熟妇 | 久久亚洲欧美国产精品| 2021国内久久精品| 久久久国产亚洲精品| 四虎影视久久久免费观看| 四虎久久影院| 久久久久久精品无码人妻| 久久精品国产2020| 久久精品中文字幕无码绿巨人| 久久成人小视频|