• <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>

            HUUYUU

            2008年7月10日 #

            Freetype學(xué)習(xí)筆記

                 摘要: 轉(zhuǎn)載時(shí)請(qǐng)注明出處和作者聯(lián)系方式:http://blog.csdn.net/absurd 作者聯(lián)系方式:Li XianJing <xianjimli at hotmail dot com> 更新時(shí)間:2006-12-19   GTK+(基于DirectFB)的字體繪制是通過pango+freetype+fontconfig三者協(xié)作來完成的,其中,fontconfig負(fù)責(zé)...  閱讀全文

            posted @ 2008-07-10 22:22 HUYU 閱讀(1347) | 評(píng)論 (1)編輯 收藏

            2008年1月22日 #

            寫operator new和operator delete時(shí)要遵循常規(guī)

            自己重寫operator new時(shí),很重要的一點(diǎn)是函數(shù)提供的行為要和系統(tǒng)缺省的operator new一致。實(shí)際做起來也就是:要有正確的返回值;可用內(nèi)存不夠時(shí)要調(diào)用出錯(cuò)處理函數(shù);處理好0字節(jié)內(nèi)存請(qǐng)求的情況。此外,還要避免不小心隱藏了標(biāo)準(zhǔn)形式的new。

            非類成員形式的operator new的偽代碼:

            void * operator new(size_t size)        // operator new還可能有其它參數(shù)
            {                                      

              if (size == 0)                      // 處理0字節(jié)請(qǐng)求時(shí),
              {

                    size = 1;                            // 把它當(dāng)作1個(gè)字節(jié)請(qǐng)求來處理

              }                                    
              while (1)

            {
                分配size字節(jié)內(nèi)存;

                  if (分配成功)
                       return (指向內(nèi)存的指針);

                // 分配不成功,找出當(dāng)前出錯(cuò)處理函數(shù)
                  new_handler globalhandler = set_new_handler(0);
                 set_new_handler(globalhandler);

                  if (globalhandler) (*globalhandler)();
                  else throw std::bad_alloc();
              }
            }

             

            為特定類寫的new往往沒有考慮該類被繼承的情況,使用sizeof(父類)獲得大小,但是如果發(fā)生子類調(diào)用父類的new時(shí),往往

            會(huì)出錯(cuò),子類的size往往大于父類的size。最好父類的new應(yīng)該這么寫:

            void * base::operator new(size_t size)
            {
              if (size != sizeof(base))                  // 如果數(shù)量“錯(cuò)誤”,讓標(biāo)準(zhǔn)operator new,精華部分。
                return ::operator new(size);        // 去處理這個(gè)請(qǐng)求
                                                                     //

              ...                                                    // 否則處理這個(gè)請(qǐng)求
            }

             

            對(duì)于operator delete(以及它的伙伴operator delete[]),情況更簡(jiǎn)單。所要記住的只是,c++保證刪除空指針永遠(yuǎn)是安全的,所以你要充分地應(yīng)用這一保證。

            下面是非類成員形式的operator delete的偽代碼:
            void operator delete(void *rawmemory)
            {
              if (rawmemory == 0) return;   //如果指針為空,返回
                                             //

              釋放rawmemory指向的內(nèi)存;

              return;
            }

             

            這個(gè)函數(shù)的類成員版本也簡(jiǎn)單,只是還必須檢查被刪除的對(duì)象的大小。假設(shè)類的operator new將“錯(cuò)誤”大小的分配請(qǐng)求轉(zhuǎn)給::operator new,那么也必須將“錯(cuò)誤”大小的刪除請(qǐng)求轉(zhuǎn)給::operator delete:

            void base::operator delete(void *rawmemory, size_t size)
            {
              if (rawmemory == 0) return;          // 檢查空指針

              if (size != sizeof(base))                 // 如果size"錯(cuò)誤",

            {    
                ::operator delete(rawmemory);  // 讓標(biāo)準(zhǔn)operator來處理請(qǐng)求
                return;                       
              }

              釋放指向rawmemory的內(nèi)存;

              return;
            }

            有關(guān)operator new和operator delete(以及他們的數(shù)組形式)的規(guī)定不是那么麻煩,重要的是必須遵守它。只要內(nèi)存分配程序支持new-handler函數(shù)并正確地處理了零內(nèi)存請(qǐng)求,就差不多了;如果內(nèi)存釋放程序又處理了空指針,那就沒其他什么要做的了。至于在類成員版本的函數(shù)里增加繼承支持,那將很快就可以完成。

            posted @ 2008-01-22 09:42 HUYU 閱讀(408) | 評(píng)論 (0)編輯 收藏

            2008年1月10日 #

            堆和棧的區(qū)別

            一、預(yù)備知識(shí)—程序的內(nèi)存分配
                一個(gè)由c/C++編譯的程序占用的內(nèi)存分為以下幾個(gè)部分
                1、棧區(qū)(stack)— 由編譯器自動(dòng)分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
                2、堆區(qū)(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時(shí)可能由OS回收 。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式倒是類似于鏈表,呵呵。
                3、全局區(qū)(靜態(tài)區(qū))(static)—,全局變量和靜態(tài)變量的存儲(chǔ)是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。 - 程序結(jié)束后有系統(tǒng)釋放
                4、文字常量區(qū)—常量字符串就是放在這里的。 程序結(jié)束后由系統(tǒng)釋放
                5、程序代碼區(qū)—存放函數(shù)體的二進(jìn)制代碼。


            例子程序
            這是一個(gè)前輩寫的,非常詳細(xì)
            //main.cpp
            int a = 0; 全局初始化區(qū)
            char *p1; 全局未初始化區(qū)
            main()
            {
            int b; 棧
            char s[] = "abc"; 棧
            char *p2; 棧
            char *p3 = "123456"; 123456\0在常量區(qū),p3在棧上。
            static int c =0; 全局(靜態(tài))初始化區(qū)
            p1 = (char *)malloc(10);
            p2 = (char *)malloc(20);
            分配得來得10和20字節(jié)的區(qū)域就在堆區(qū)。
            strcpy(p1, "123456"); 123456\0放在常量區(qū),編譯器可能會(huì)將它與p3所指向的"123456"優(yōu)化成一個(gè)地方。
            }


            二、堆和棧的理論知識(shí)

            2.1申請(qǐng)方式
            stack:
            由系統(tǒng)自動(dòng)分配。 例如,聲明在函數(shù)中一個(gè)局部變量 int b; 系統(tǒng)自動(dòng)在棧中為b開辟空間
            heap:
            需要程序員自己申請(qǐng),并指明大小,在c中malloc函數(shù)
            如p1 = (char *)malloc(10);
            在C++中用new運(yùn)算符
            如p2 = (char *)malloc(10);
            但是注意p1、p2本身是在棧中的。

            2.2申請(qǐng)后系統(tǒng)的響應(yīng)
            棧:只要棧的剩余空間大于所申請(qǐng)空間,系統(tǒng)將為程序提供內(nèi)存,否則將報(bào)異常提示棧溢出。
            堆:首先應(yīng)該知道操作系統(tǒng)有一個(gè)記錄空閑內(nèi)存地址的鏈表,當(dāng)系統(tǒng)收到程序的申請(qǐng)時(shí),
            會(huì)遍歷該鏈表,尋找第一個(gè)空間大于所申請(qǐng)空間的堆結(jié)點(diǎn),然后將該結(jié)點(diǎn)從空閑結(jié)點(diǎn)鏈表中刪除,并將該結(jié)點(diǎn)的空間分配給程序,另外,對(duì)于大多數(shù)系統(tǒng),會(huì)在這塊內(nèi)存空間中的首地址處記錄本次分配的大小,這樣,代碼中的delete語句才能正確的釋放本內(nèi)存空間。另外,由于找到的堆結(jié)點(diǎn)的大小不一定正好等于申請(qǐng)的大小,系統(tǒng)會(huì)自動(dòng)的將多余的那部分重新放入空閑鏈表中。

            2.3申請(qǐng)大小的限制
            棧:在Windows下,棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存的區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的,在WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個(gè)編譯時(shí)就確定的常數(shù)),如果申請(qǐng)的空間超過棧的剩余空間時(shí),將提示overflow。因此,能從棧獲得的空間較小。
            堆:堆是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來存儲(chǔ)的空閑內(nèi)存地址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。


            2.4申請(qǐng)效率的比較
            棧由系統(tǒng)自動(dòng)分配,速度較快。但程序員是無法控制的。
            堆是由new分配的內(nèi)存,一般速度比較慢,而且容易產(chǎn)生內(nèi)存碎片,不過用起來最方便.
            另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內(nèi)存,他不是在堆,也不是在棧是直接在進(jìn)程的地址空間中保留一快內(nèi)存,雖然用起來最不方便。但是速度快,也最靈活。

            2.5堆和棧中的存儲(chǔ)內(nèi)容
            棧: 在函數(shù)調(diào)用時(shí),第一個(gè)進(jìn)棧的是主函數(shù)中后的下一條指令(函數(shù)調(diào)用語句的下一條可執(zhí)行語句)的地址,然后是函數(shù)的各個(gè)參數(shù),在大多數(shù)的C編譯器中,參數(shù)是由右往左入棧的,然后是函數(shù)中的局部變量。注意靜態(tài)變量是不入棧的。
            當(dāng)本次函數(shù)調(diào)用結(jié)束后,局部變量先出棧,然后是參數(shù),最后棧頂指針指向最開始存的地址,也就是主函數(shù)中的下一條指令,程序由該點(diǎn)繼續(xù)運(yùn)行。
            堆:一般是在堆的頭部用一個(gè)字節(jié)存放堆的大小。堆中的具體內(nèi)容有程序員安排。

            2.6存取效率的比較
            char s1[] = "aaaaaaaaaaaaaaa";
            char *s2 = "bbbbbbbbbbbbbbbbb";
            aaaaaaaaaaa是在運(yùn)行時(shí)刻賦值的;
            而bbbbbbbbbbb是在編譯時(shí)就確定的;
            但是,在以后的存取中,在棧上的數(shù)組比指針?biāo)赶虻淖址?例如堆)快。
            比如:
            #i nclude
            void main()
            {
            char a = 1;
            char c[] = "1234567890";
            char *p ="1234567890";
            a = c[1];
            a = p[1];
            return;
            }
            對(duì)應(yīng)的匯編代碼
            10: a = c[1];
            00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
            0040106A 88 4D FC mov byte ptr [ebp-4],cl
            11: a = p[1];
            0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
            00401070 8A 42 01 mov al,byte ptr [edx+1]
            00401073 88 45 FC mov byte ptr [ebp-4],al
            第一種在讀取時(shí)直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據(jù)edx讀取字符,顯然慢了。

            2.7小結(jié)
            堆和棧的區(qū)別可以用如下的比喻來看出:
            使用棧就象我們?nèi)ワ堭^里吃飯,只管點(diǎn)菜(發(fā)出申請(qǐng))、付錢、和吃(使用),吃飽了就走,不必理會(huì)切菜、洗菜等準(zhǔn)備工作和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。
            使用堆就象是自己動(dòng)手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。



            windows進(jìn)程中的內(nèi)存結(jié)構(gòu)


            在閱讀本文之前,如果你連
            堆棧是什么多不知道的話,請(qǐng)先閱讀文章后面的基礎(chǔ)知識(shí)

            接觸過
            編程的人都知道,高級(jí)語言都能通過變量名來訪問內(nèi)存中的數(shù)據(jù)。那么這些變量在內(nèi)存中是如何存放的呢?程序又是如何使用這些變量的呢?下面就會(huì)對(duì)此進(jìn)行深入的討論。下文中的C語言代碼如沒有特別聲明,默認(rèn)都使用VC編譯的release版。

            首先,來了解一下 C 語言的變量是如何在內(nèi)存分部的。C 語言有全局變量(Global)、本地變量(Local),靜態(tài)變量(Static)、寄存器變量(Regeister)。每種變量都有不同的分配方式。先來看下面這段代碼:

            #i nclude <stdio.h>

            int g1=0, g2=0, g3=0;

            int main()
            {
            static int s1=0, s2=0, s3=0;
            int v1=0, v2=0, v3=0;

            //打印出各個(gè)變量的內(nèi)存地址

            printf("0x%08x\n",&v1); //打印各本地變量的內(nèi)存地址
            printf("0x%08x\n",&v2);
            printf("0x%08x\n\n",&v3);
            printf("0x%08x\n",&g1); //打印各全局變量的內(nèi)存地址
            printf("0x%08x\n",&g2);
            printf("0x%08x\n\n",&g3);
            printf("0x%08x\n",&s1); //打印各靜態(tài)變量的內(nèi)存地址
            printf("0x%08x\n",&s2);
            printf("0x%08x\n\n",&s3);
            return 0;
            }

            編譯后的執(zhí)行結(jié)果是:

            0x0012ff78
            0x0012ff7c
            0x0012ff80

            0x004068d0
            0x004068d4
            0x004068d8

            0x004068dc
            0x004068e0
            0x004068e4

            輸出的結(jié)果就是變量的內(nèi)存地址。其中v1,v2,v3是本地變量,g1,g2,g3是全局變量,s1,s2,s3是靜態(tài)變量。你可以看到這些變量在內(nèi)存是連續(xù)分布的,但是本地變量和全局變量分配的內(nèi)存地址差了十萬八千里,而全局變量和靜態(tài)變量分配的內(nèi)存是連續(xù)的。這是因?yàn)楸镜刈兞亢腿?靜態(tài)變量是分配在不同類型的內(nèi)存區(qū)域中的結(jié)果。對(duì)于一個(gè)進(jìn)程的內(nèi)存空間而言,可以在邏輯上分成3個(gè)部份:代碼區(qū),靜態(tài)數(shù)據(jù)區(qū)和動(dòng)態(tài)數(shù)據(jù)區(qū)。動(dòng)態(tài)數(shù)據(jù)區(qū)一般就是“堆棧”。“棧(stack)”和“堆(heap)”是兩種不同的動(dòng)態(tài)數(shù)據(jù)區(qū),棧是一種線性結(jié)構(gòu),堆是一種鏈?zhǔn)浇Y(jié)構(gòu)。進(jìn)程的每個(gè)線程都有私有的“棧”,所以每個(gè)線程雖然代碼一樣,但本地變量的數(shù)據(jù)都是互不干擾。一個(gè)堆棧可以通過“基地址”和“棧頂”地址來描述。全局變量和靜態(tài)變量分配在靜態(tài)數(shù)據(jù)區(qū),本地變量分配在動(dòng)態(tài)數(shù)據(jù)區(qū),即堆棧中。程序通過堆棧的基地址和偏移量來訪問本地變量。


            ├———————┤低端內(nèi)存區(qū)域
            │ …… │
            ├———————┤
            │ 動(dòng)態(tài)數(shù)據(jù)區(qū) │
            ├———————┤
            │ …… │
            ├———————┤
            │ 代碼區(qū) │
            ├———————┤
            │ 靜態(tài)數(shù)據(jù)區(qū) │
            ├———————┤
            │ …… │
            ├———————┤高端內(nèi)存區(qū)域


            堆棧是一個(gè)先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu),棧頂?shù)刂房偸切∮诘扔跅5幕刂贰N覀兛梢韵攘私庖幌潞瘮?shù)調(diào)用的過程,以便對(duì)堆棧在程序中的作用有更深入的了解。不同的語言有不同的函數(shù)調(diào)用規(guī)定,這些因素有參數(shù)的壓入規(guī)則和堆棧的平衡。windows API的調(diào)用規(guī)則和ANSI C的函數(shù)調(diào)用規(guī)則是不一樣的,前者由被調(diào)函數(shù)調(diào)整堆棧,后者由調(diào)用者調(diào)整堆棧。兩者通過“__stdcall”和“__cdecl”前綴區(qū)分。先看下面這段代碼:

            #i nclude <stdio.h>

            void __stdcall func(int param1,int param2,int param3)
            {
            int var1=param1;
            int var2=param2;
            int var3=param3;
            printf("0x%08x\n",?m1); //打印出各個(gè)變量的內(nèi)存地址
            printf("0x%08x\n",?m2);
            printf("0x%08x\n\n",?m3);
            printf("0x%08x\n",&var1);
            printf("0x%08x\n",&var2);
            printf("0x%08x\n\n",&var3);
            return;
            }

            int main()
            {
            func(1,2,3);
            return 0;
            }

            編譯后的執(zhí)行結(jié)果是:

            0x0012ff78
            0x0012ff7c
            0x0012ff80

            0x0012ff68
            0x0012ff6c
            0x0012ff70


            ├———————┤<—函數(shù)執(zhí)行時(shí)的棧頂(ESP)、低端內(nèi)存區(qū)域
            │ …… │
            ├———————┤
            │ var 1 │
            ├———————┤
            │ var 2 │
            ├———————┤
            │ var 3 │
            ├———————┤
            │ RET │
            ├———————┤<—“__cdecl”函數(shù)返回后的棧頂(ESP)
            │ parameter 1 │
            ├———————┤
            │ parameter 2 │
            ├———————┤
            │ parameter 3 │
            ├———————┤<—“__stdcall”函數(shù)返回后的棧頂(ESP)
            │ …… │
            ├———————┤<—棧底(基地址 EBP)、高端內(nèi)存區(qū)域


            上圖就是函數(shù)調(diào)用過程中堆棧的樣子了。首先,三個(gè)參數(shù)以從又到左的次序壓入堆棧,先壓“param3”,再壓“param2”,最后壓入“param1”;然后壓入函數(shù)的返回地址(RET),接著跳轉(zhuǎn)到函數(shù)地址接著執(zhí)行(這里要補(bǔ)充一點(diǎn),介紹UNIX下的緩沖溢出原理的文章中都提到在壓入RET后,繼續(xù)壓入當(dāng)前EBP,然后用當(dāng)前ESP代替EBP。然而,有一篇介紹windows下函數(shù)調(diào)用的文章中說,在windows下的函數(shù)調(diào)用也有這一步驟,但根據(jù)我的實(shí)際調(diào)試,并未發(fā)現(xiàn)這一步,這還可以從param3和var1之間只有4字節(jié)的間隙這點(diǎn)看出來);第三步,將棧頂(ESP)減去一個(gè)數(shù),為本地變量分配內(nèi)存空間,上例中是減去12字節(jié)(ESP=ESP-3*4,每個(gè)int變量占用4個(gè)字節(jié));接著就初始化本地變量的內(nèi)存空間。由于“__stdcall”調(diào)用由被調(diào)函數(shù)調(diào)整堆棧,所以在函數(shù)返回前要恢復(fù)堆棧,先回收本地變量占用的內(nèi)存(ESP=ESP+3*4),然后取出返回地址,填入EIP寄存器,回收先前壓入?yún)?shù)占用的內(nèi)存(ESP=ESP+3*4),繼續(xù)執(zhí)行調(diào)用者的代碼。參見下列匯編代碼:

            ;--------------func 函數(shù)的匯編代碼-------------------

            :00401000 83EC0C sub esp, 0000000C //創(chuàng)建本地變量的內(nèi)存空間
            :00401003 8B442410 mov eax, dword ptr [esp+10]
            :00401007 8B4C2414 mov ecx, dword ptr [esp+14]
            :0040100B 8B542418 mov edx, dword ptr [esp+18]
            :0040100F 89442400 mov dword ptr [esp], eax
            :00401013 8D442410 lea eax, dword ptr [esp+10]
            :00401017 894C2404 mov dword ptr [esp+04], ecx

            ……………………(省略若干代碼)

            :00401075 83C43C add esp, 0000003C ;恢復(fù)堆棧,回收本地變量的內(nèi)存空間
            :00401078 C3 ret 000C ;函數(shù)返回,恢復(fù)參數(shù)占用的內(nèi)存空間
            ;如果是“__cdecl”的話,這里是“ret”,堆棧將由調(diào)用者恢復(fù)

            ;-------------------函數(shù)結(jié)束-------------------------


            ;--------------主程序調(diào)用func函數(shù)的代碼--------------

            :00401080 6A03 push 00000003 //壓入?yún)?shù)param3
            :00401082 6A02 push 00000002 //壓入?yún)?shù)param2
            :00401084 6A01 push 00000001 //壓入?yún)?shù)param1
            :00401086 E875FFFFFF call 00401000 //調(diào)用func函數(shù)
            ;如果是“__cdecl”的話,將在這里恢復(fù)堆棧,“add esp, 0000000C”

            聰明的讀者看到這里,差不多就明白緩沖溢出的原理了。先來看下面的代碼:

            #i nclude <stdio.h>
            #i nclude <string.h>

            void __stdcall func()
            {
            char lpBuff[8]="\0";
            strcat(lpBuff,"AAAAAAAAAAA");
            return;
            }

            int main()
            {
            func();
            return 0;
            }

            編譯后執(zhí)行一下回怎么樣?哈,“"0x00414141"指令引用的"0x00000000"內(nèi)存。該內(nèi)存不能為"read"。”,“非法操作”嘍!"41"就是"A"的16進(jìn)制的ASCII碼了,那明顯就是strcat這句出的問題了。"lpBuff"的大小只有8字節(jié),算進(jìn)結(jié)尾的\0,那strcat最多只能寫入7個(gè)"A",但程序?qū)嶋H寫入了11個(gè)"A"外加1個(gè)\0。再來看看上面那幅圖,多出來的4個(gè)字節(jié)正好覆蓋了RET的所在的內(nèi)存空間,導(dǎo)致函數(shù)返回到一個(gè)錯(cuò)誤的內(nèi)存地址,執(zhí)行了錯(cuò)誤的指令。如果能精心構(gòu)造這個(gè)字符串,使它分成三部分,前一部份僅僅是填充的無意義數(shù)據(jù)以達(dá)到溢出的目的,接著是一個(gè)覆蓋RET的數(shù)據(jù),緊接著是一段shellcode,那只要著個(gè)RET地址能指向這段shellcode的第一個(gè)指令,那函數(shù)返回時(shí)就能執(zhí)行shellcode了。但是軟件的不同版本和不同的運(yùn)行環(huán)境都可能影響這段shellcode在內(nèi)存中的位置,那么要構(gòu)造這個(gè)RET是十分困難的。一般都在RET和shellcode之間填充大量的NOP指令,使得exploit有更強(qiáng)的通用性。


            ├———————┤<—低端內(nèi)存區(qū)域
            │ …… │
            ├———————┤<—由exploit填入數(shù)據(jù)的開始
            │ │
            │ buffer │<—填入無用的數(shù)據(jù)
            │ │
            ├———————┤
            │ RET │<—指向shellcode,或NOP指令的范圍
            ├———————┤
            │ NOP │
            │ …… │<—填入的NOP指令,是RET可指向的范圍
            │ NOP │
            ├———————┤
            │ │
            │ shellcode │
            │ │
            ├———————┤<—由exploit填入數(shù)據(jù)的結(jié)束
            │ …… │
            ├———————┤<—高端內(nèi)存區(qū)域


            windows下的動(dòng)態(tài)數(shù)據(jù)除了可存放在棧中,還可以存放在堆中。了解C++的朋友都知道,C++可以使用new關(guān)鍵字來動(dòng)態(tài)分配內(nèi)存。來看下面的C++代碼:

            #i nclude <stdio.h>
            #i nclude <iostream.h>
            #i nclude <windows.h>

            void func()
            {
            char *buffer=new char[128];
            char bufflocal[128];
            static char buffstatic[128];
            printf("0x%08x\n",buffer); //打印堆中變量的內(nèi)存地址
            printf("0x%08x\n",bufflocal); //打印本地變量的內(nèi)存地址
            printf("0x%08x\n",buffstatic); //打印靜態(tài)變量的內(nèi)存地址
            }

            void main()
            {
            func();
            return;
            }

            程序執(zhí)行結(jié)果為:

            0x004107d0
            0x0012ff04
            0x004068c0

            可以發(fā)現(xiàn)用new關(guān)鍵字分配的內(nèi)存即不在棧中,也不在靜態(tài)數(shù)據(jù)區(qū)。VC編譯器是通過windows下的“堆(heap)”來實(shí)現(xiàn)new關(guān)鍵字的內(nèi)存動(dòng)態(tài)分配。在講“堆”之前,先來了解一下和“堆”有關(guān)的幾個(gè)API函數(shù):

            HeapAlloc 在堆中申請(qǐng)內(nèi)存空間
            HeapCreate 創(chuàng)建一個(gè)新的堆對(duì)象
            HeapDestroy 銷毀一個(gè)堆對(duì)象
            HeapFree 釋放申請(qǐng)的內(nèi)存
            HeapWalk 枚舉堆對(duì)象的所有內(nèi)存塊
            GetProcessHeap 取得進(jìn)程的默認(rèn)堆對(duì)象
            GetProcessHeaps 取得進(jìn)程所有的堆對(duì)象
            LocalAlloc
            GlobalAlloc

            當(dāng)進(jìn)程初始化時(shí),系統(tǒng)會(huì)自動(dòng)為進(jìn)程創(chuàng)建一個(gè)默認(rèn)堆,這個(gè)堆默認(rèn)所占內(nèi)存的大小為1M。堆對(duì)象由系統(tǒng)進(jìn)行管理,它在內(nèi)存中以鏈?zhǔn)浇Y(jié)構(gòu)存在。通過下面的代碼可以通過堆動(dòng)態(tài)申請(qǐng)內(nèi)存空間:

            HANDLE hHeap=GetProcessHeap();
            char *buff=HeapAlloc(hHeap,0,8);

            其中hHeap是堆對(duì)象的句柄,buff是指向申請(qǐng)的內(nèi)存空間的地址。那這個(gè)hHeap究竟是什么呢?它的值有什么意義嗎?看看下面這段代碼吧:

            #pragma comment(linker,"/entry:main") //定義程序的入口
            #i nclude <windows.h>

            _CRTIMP int (__cdecl *printf)(const char *, ...); //定義STL函數(shù)printf
            /*---------------------------------------------------------------------------
            寫到這里,我們順便來復(fù)習(xí)一下前面所講的知識(shí):
            (*注)printf函數(shù)是C語言的標(biāo)準(zhǔn)函數(shù)庫中函數(shù),VC的標(biāo)準(zhǔn)函數(shù)庫由msvcrt.dll模塊實(shí)現(xiàn)。
            由函數(shù)定義可見,printf的參數(shù)個(gè)數(shù)是可變的,函數(shù)內(nèi)部無法預(yù)先知道調(diào)用者壓入的參數(shù)個(gè)數(shù),函數(shù)只能通過分析第一個(gè)參數(shù)字符串的格式來獲得壓入?yún)?shù)的信息,由于這里參數(shù)的個(gè)數(shù)是動(dòng)態(tài)的,所以必須由調(diào)用者來平衡堆棧,這里便使用了__cdecl調(diào)用規(guī)則。BTW,Windows系統(tǒng)的API函數(shù)基本上是__stdcall調(diào)用形式,只有一個(gè)API例外,那就是wsprintf,它使用__cdecl調(diào)用規(guī)則,同printf函數(shù)一樣,這是由于它的參數(shù)個(gè)數(shù)是可變的緣故。
            ---------------------------------------------------------------------------*/
            void main()
            {
            HANDLE hHeap=GetProcessHeap();
            char *buff=HeapAlloc(hHeap,0,0x10);
            char *buff2=HeapAlloc(hHeap,0,0x10);
            HMODULE hMsvcrt=LoadLibrary("msvcrt.dll");
            printf=(void *)GetProcAddress(hMsvcrt,"printf");
            printf("0x%08x\n",hHeap);
            printf("0x%08x\n",buff);
            printf("0x%08x\n\n",buff2);
            }

            執(zhí)行結(jié)果為:

            0x00130000
            0x00133100
            0x00133118

            hHeap的值怎么和那個(gè)buff的值那么接近呢?其實(shí)hHeap這個(gè)句柄就是指向HEAP首部的地址。在進(jìn)程的用戶區(qū)存著一個(gè)叫PEB(進(jìn)程環(huán)境塊)的結(jié)構(gòu),這個(gè)結(jié)構(gòu)中存放著一些有關(guān)進(jìn)程的重要信息,其中在PEB首地址偏移0x18處存放的ProcessHeap就是進(jìn)程默認(rèn)堆的地址,而偏移0x90處存放了指向進(jìn)程所有堆的地址列表的指針。windows有很多API都使用進(jìn)程的默認(rèn)堆來存放動(dòng)態(tài)數(shù)據(jù),如windows 2000下的所有ANSI版本的函數(shù)都是在默認(rèn)堆中申請(qǐng)內(nèi)存來轉(zhuǎn)換ANSI字符串到Unicode字符串的。對(duì)一個(gè)堆的訪問是順序進(jìn)行的,同一時(shí)刻只能有一個(gè)線程訪問堆中的數(shù)據(jù),當(dāng)多個(gè)線程同時(shí)有訪問要求時(shí),只能排隊(duì)等待,這樣便造成程序執(zhí)行效率下降。

            最后來說說內(nèi)存中的數(shù)據(jù)對(duì)齊。所位數(shù)據(jù)對(duì)齊,是指數(shù)據(jù)所在的內(nèi)存地址必須是該數(shù)據(jù)長(zhǎng)度的整數(shù)倍,DWORD數(shù)據(jù)的內(nèi)存起始地址能被4除盡,WORD數(shù)據(jù)的內(nèi)存起始地址能被2除盡,x86 CPU能直接訪問對(duì)齊的數(shù)據(jù),當(dāng)他試圖訪問一個(gè)未對(duì)齊的數(shù)據(jù)時(shí),會(huì)在內(nèi)部進(jìn)行一系列的調(diào)整,這些調(diào)整對(duì)于程序來說是透明的,但是會(huì)降低運(yùn)行速度,所以編譯器在編譯程序時(shí)會(huì)盡量保證數(shù)據(jù)對(duì)齊。同樣一段代碼,我們來看看用VC、Dev-C++和lcc三個(gè)不同編譯器編譯出來的程序的執(zhí)行結(jié)果:

            #i nclude <stdio.h>

            int main()
            {
            int a;
            char b;
            int c;
            printf("0x%08x\n",&a);
            printf("0x%08x\n",&b);
            printf("0x%08x\n",&c);
            return 0;
            }

            這是用VC編譯后的執(zhí)行結(jié)果:
            0x0012ff7c
            0x0012ff7b
            0x0012ff80
            變量在內(nèi)存中的順序:b(1字節(jié))-a(4字節(jié))-c(4字節(jié))。

            這是用Dev-C++編譯后的執(zhí)行結(jié)果:
            0x0022ff7c
            0x0022ff7b
            0x0022ff74
            變量在內(nèi)存中的順序:c(4字節(jié))-中間相隔3字節(jié)-b(占1字節(jié))-a(4字節(jié))。

            這是用lcc編譯后的執(zhí)行結(jié)果:
            0x0012ff6c
            0x0012ff6b
            0x0012ff64
            變量在內(nèi)存中的順序:同上。

            三個(gè)編譯器都做到了數(shù)據(jù)對(duì)齊,但是后兩個(gè)編譯器顯然沒VC“聰明”,讓一個(gè)char占了4字節(jié),浪費(fèi)內(nèi)存哦。


            基礎(chǔ)知識(shí):
            堆棧是一種簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu),是一種只允許在其一端進(jìn)行插入或刪除的線性表。允許插入或刪除操作的一端稱為棧頂,另一端稱為棧底,對(duì)堆棧的插入和刪除操作被稱為入棧和出棧。有一組CPU指令可以實(shí)現(xiàn)對(duì)進(jìn)程的內(nèi)存實(shí)現(xiàn)堆棧訪問。其中,POP指令實(shí)現(xiàn)出棧操作,PUSH指令實(shí)現(xiàn)入棧操作。CPU的ESP寄存器存放當(dāng)前線程的棧頂指針,EBP寄存器中保存當(dāng)前線程的棧底指針。CPU的EIP寄存器存放下一個(gè)CPU指令存放的內(nèi)存地址,當(dāng)CPU執(zhí)行完當(dāng)前的指令后,從EIP寄存器中讀取下一條指令的內(nèi)存地址,然后繼續(xù)執(zhí)行。

             <br>

            堆(Heap)棧(Stack)

            1、內(nèi)存分配方面:

                堆:一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時(shí)可能由OS回收 。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式是類似于鏈表。可能用到的關(guān)鍵字如下:newmallocdeletefree等等。

                棧:由編譯器(Compiler)自動(dòng)分配釋放,存放函數(shù)的參數(shù)值局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。

            2、申請(qǐng)方式方面:

                堆:需要程序員自己申請(qǐng),并指明大小。在c中malloc函數(shù)如p1 = (char *)malloc(10);在C++中用new運(yùn)算符,但是注意p1、p2本身是在棧中的。因?yàn)樗麄冞€是可以認(rèn)為是局部變量。

                棧:由系統(tǒng)自動(dòng)分配。 例如,聲明在函數(shù)中一個(gè)局部變量 int b;系統(tǒng)自動(dòng)在棧中為b開辟空間。

            3、系統(tǒng)響應(yīng)方面:

                堆:操作系統(tǒng)有一個(gè)記錄空閑內(nèi)存地址的鏈表,當(dāng)系統(tǒng)收到程序的申請(qǐng)時(shí),會(huì)遍歷該鏈表,尋找第一個(gè)空間大于所申請(qǐng)空間的堆結(jié)點(diǎn),然后將該結(jié)點(diǎn)從空閑結(jié)點(diǎn)鏈表中刪除,并將該結(jié)點(diǎn)的空間分配給程序,另外,對(duì)于大多數(shù)系統(tǒng),會(huì)在這塊內(nèi)存空間中的首地址處記錄本次分配的大小,這樣代碼中的delete語句才能正確的釋放本內(nèi)存空間。另外由于找到的堆結(jié)點(diǎn)的大小不一定正好等于申請(qǐng)的大小,系統(tǒng)會(huì)自動(dòng)的將多余的那部分重新放入空閑鏈表中。

                棧:只要棧的剩余空間大于所申請(qǐng)空間,系統(tǒng)將為程序提供內(nèi)存,否則將報(bào)異常提示棧溢出。

            4、大小限制方面:

                堆:是向高地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是不連續(xù)的內(nèi)存區(qū)域。這是由于系統(tǒng)是用鏈表來存儲(chǔ)的空閑內(nèi)存地址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見,堆獲得的空間比較靈活,也比較大。

                棧:在Windows下, 棧是向低地址擴(kuò)展的數(shù)據(jù)結(jié)構(gòu),是一塊連續(xù)的內(nèi)存的區(qū)域。這句話的意思是棧頂?shù)牡刂泛蜅5淖畲笕萘渴?strong>系統(tǒng)預(yù)先規(guī)定好的,在WINDOWS下,棧的大小是固定的(是一個(gè)編譯時(shí)就確定的常數(shù)),如果申請(qǐng)的空間超過棧的剩余空間時(shí),將提示overflow。因此,能從棧獲得的空間較小。

            5、效率方面:

                堆:是由new分配的內(nèi)存,一般速度比較慢,而且容易產(chǎn)生內(nèi)存碎片,不過用起來最方便,另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內(nèi)存,他不是在堆,也不是在棧是直接在進(jìn)程的地址空間中保留一快內(nèi)存,雖然用起來最不方便。但是速度快,也最靈活。

                棧:由系統(tǒng)自動(dòng)分配,速度較快。但程序員是無法控制的。

            6、存放內(nèi)容方面:

                堆:一般是在堆的頭部用一個(gè)字節(jié)存放堆的大小。堆中的具體內(nèi)容有程序員安排。

                棧:在函數(shù)調(diào)用時(shí)第一個(gè)進(jìn)棧的是主函數(shù)中后的下一條指令(函數(shù)調(diào)用語句的下一條可執(zhí)行語句)的地址然后是函數(shù)的各個(gè)參數(shù),在大多數(shù)的C編譯器中,參數(shù)是由右往左入棧,然后是函數(shù)中的局部變量。 注意: 靜態(tài)變量是不入棧的。當(dāng)本次函數(shù)調(diào)用結(jié)束后,局部變量先出棧,然后是參數(shù),最后棧頂指針指向最開始存的地址,也就是主函數(shù)中的下一條指令,程序由該點(diǎn)繼續(xù)運(yùn)行

            7、存取效率方面:

                堆:char *s1 = "Hellow Word";是在編譯時(shí)就確定的;

                棧:char s1[] = "Hellow Word"; 是在運(yùn)行時(shí)賦值的;用數(shù)組比用指針?biāo)俣纫煲恍驗(yàn)橹羔樤诘讓訁R編中需要用edx寄存器中轉(zhuǎn)一下,而數(shù)組在棧上直接讀取。


             

            Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1925576

            posted @ 2008-01-10 13:22 HUYU 閱讀(444) | 評(píng)論 (0)編輯 收藏

            2007年1月19日 #

            Variable Parameters Print Example

            #include <stdio.h>
            #include <string.h>
            #include <stdarg.h>

            #include "drm_korea_def.h"

            #ifdef __KDRM_FSTRACE

            #define LOG_BUFFER_SIZE 512
            #define LOG_LINE_NUM  16

            #ifdef WIN32
            #define LOG_FILE     FILE *
            #define LOG_OPEN(s, m)   fopen(s, m)
            #define LOG_CLOSE(hLog)   fclose(hLog)
            #define LOG_READ (hLog, p, x)  fread (p, x, 1, hLog)
            #define LOG_WRITE(hLog, p, x) fwrite(p, x, 1, hLog)
            #define LOG_PUTC(hLog, c)  fputc(c, hLog)
            #define LOG_PUTS(hLog, s)  fputs(s, hLog)
            #else
            #define LOG_FILE     int32_t
            #define LOG_OPEN(s, m)   Fopen((uint8_t *)s, (uint8_t *)m)
            #define LOG_CLOSE(hLog)   Fclose(hLog)
            #define LOG_READ (hLog, p, x)  Fread (hLog,  p, x)
            #define LOG_WRITE(hLog, p, x) Fwrite(hLog, p, x)
            #define LOG_PUTC(hLog, c)  Fputc(hLog, c)
            #define LOG_PUTS(hLog, s)  Fputs(hLog, (uint8_t *)s)
            #endif

            static LOG_FILE fpLogfile;
            static char logBuffer[LOG_BUFFER_SIZE];

            void log_Init(char *logName)
            {
             fpLogfile = LOG_OPEN(logName, "w+");

             if(fpLogfile > 0)
             {
              log_Print("\r\n====> Starting ... :%s %s\r\n", __TIME__, __FILE__);
             }
            }

            void log_Print(char *logFormat, ...)
            {
             if(fpLogfile > 0)
             {
              va_list va;
              int len;

              va_start(va, logFormat);
              len = vsprintf(logBuffer, logFormat, va);
              va_end(va);

              logBuffer[len+1] = 0;

              LOG_PUTS(fpLogfile, logBuffer);
             }
            }

            char Hex2Dec(unsigned char bHex)
            {
             bHex &= 0x0F;
             bHex += bHex<10? '0': 'A' - 10;

             return bHex;
            }

            void log_Dump(const unsigned char  *pCache, int u32Size, bool bBinary)
            {
             int i, stride;
             unsigned char *p = (unsigned char  *)pCache;
             char cTmp;

             if(fpLogfile < 0)
              return;

             if(bBinary)
             {
              for(; p<pCache+u32Size; p+=LOG_BUFFER_SIZE)
              {
               stride = (p+LOG_BUFFER_SIZE <= pCache+u32Size)? LOG_BUFFER_SIZE: u32Size%LOG_BUFFER_SIZE;
               LOG_WRITE(fpLogfile, p, stride);
              }
             }
             else
             {
              for(; p<pCache+u32Size; p+=LOG_LINE_NUM)
              {
               stride = (p+LOG_LINE_NUM <= pCache+u32Size)? LOG_LINE_NUM:u32Size%LOG_LINE_NUM;

               for(i=0; i<stride; i++)
               {
                cTmp = Hex2Dec((p[i]&0xF0) >> 4);
                LOG_PUTC(fpLogfile, cTmp);
                cTmp = Hex2Dec(p[i]&0x0F);
                LOG_PUTC(fpLogfile, cTmp);
                LOG_PUTC(fpLogfile, ' ');
                if(i== (LOG_LINE_NUM/2 - 1))
                {
                 LOG_PUTC(fpLogfile, '-');
                 LOG_PUTC(fpLogfile, ' ');
                }
               }

               LOG_PUTC(fpLogfile, '\n');
              }
             }
            }

            void log_Terminate(void)
            {
             if(fpLogfile > 0)
             {
              log_Print("<==== Stopping ... :%s\r\n", __TIME__);
              LOG_CLOSE(fpLogfile);
             }
            }

            #endif //__KDRM_FSTRACE

            posted @ 2007-01-19 17:55 HUYU 閱讀(339) | 評(píng)論 (0)編輯 收藏

            2006年11月10日 #

            FreeType2研究

            FreeType2研究

            最近學(xué)習(xí)狀態(tài)不佳,感覺什么都想做卻什么也做不下去,浮躁之極。大的庫一下子研究不下來,索性找一下小庫來看看。
            游戲里面一般都涉及到文本、壓縮、圖像、腳本的概念,為了將來有機(jī)會(huì)研究游戲所以先下手這些小庫,不求甚解只求用好。

            先從字體著手,F(xiàn)reeType字體作為一種字體文件編程開發(fā)包,廣泛易用在游戲里面。網(wǎng)上漢語資料比較少,只能看它的faq。翻譯了部分如下:

            FreeType 2 Library

            FAQ

            (當(dāng)前下載地址: http://sourceforge.net/project/showfiles.php?group_id=3157 版本 2.2.1

            1、? FreeType2 是什么?

            它是一個(gè)為各種應(yīng)用程序提供通用的字體文件訪問的軟件包。尤其值得注意的以下特性:

            l???????? 提供統(tǒng)一的字體文件訪問接口。支持位圖和向量格式,包括 TrueType OpenType Typel CID CFF Windows FON/FNT X11 PCF

            l???????? 提供高效反走樣的基于 256 灰度級(jí)的位圖字形的生產(chǎn)。

            l???????? 模塊清晰,每種字體格式對(duì)于一個(gè)模塊。類庫的構(gòu)建可以按照你需要支持的格式進(jìn)行裁減以減小代碼尺寸。(最小的反走樣 FreeType <30Kb

            2、? FreeType2 能做什么?

            FT2 已經(jīng)易用于許多領(lǐng)域。例如:

            l???????? 圖形子系統(tǒng)和文本顯示庫

            l???????? 文本排版(布局、分頁、渲染)

            l???????? 字體識(shí)別和轉(zhuǎn)換工具

            一般來說,該庫使得你能輕松的操縱字體文件。

            3、? FreeType2 不能做什么?

            FT2 并不包含大量豐富的高級(jí)特性,它只定位于出色的字體服務(wù)。也就是說下面的一些特性 FT2 類庫并不直接提供支持,然而你可以以它為基礎(chǔ)在上層進(jìn)行實(shí)現(xiàn):

            l???????? 任意表面的文字渲染

            FT2 不是圖形庫所以它僅支持兩種象素格式的文本渲染: 1-bit 的單色位圖和 8-bit 的灰度象素。

            如果你需要繪制其它格式的表面(例如 24-bit RGB 象素),你就得選擇其它你喜愛的圖形庫來做。

            注意:為了渲染向量輪廓文本而不是放走樣的象素,應(yīng)用程序可以提供自己的渲染回調(diào)以繪制或者直接組合反走樣文本到任意目標(biāo)表面。

            l???????? 文本緩存

            每次從字體中請(qǐng)求文本圖象, FT2 都要解析字體文件 / 流相關(guān)部分,通過它的字體格式進(jìn)行解釋。對(duì)于某些特殊格式可能會(huì)很慢包括像 TrueType (或者 Type1 )這樣的向量字體。

            注意:自從 2.0.1 版本開始 FT2 提供了一個(gè) beta 版本的緩存子系統(tǒng)。當(dāng)然你還是可以寫自己的緩存來滿足某種特殊需求。

            l???????? 文本布局

            不支持文本布局操作。高級(jí)操作例如文本替換、字距調(diào)整、兩端調(diào)整等都不屬于字體服務(wù)本身職責(zé)。

            4、? FreeType2 可移植性?

            FT2 源碼可移植性很好由于以下原因:

            l???????? 代碼書寫遵循 ANSI C 標(biāo)準(zhǔn)

            l???????? 對(duì)于各種編譯警告我們都謹(jǐn)慎的避免。當(dāng)前代碼在很多編譯器上編譯通過且沒有產(chǎn)生一條警告。

            l???????? 庫沒有使用任何硬編碼,是嵌入式系統(tǒng)開發(fā)的一個(gè)好的選擇。(例如它能夠直接在 ROM 中運(yùn)行)

            同時(shí),我們盡最大努力確保庫的高效、緊湊和友好性。

            5、? FreeType2 FreeType1.x 的區(qū)別?

            最大的區(qū)別就是:

            l???????? FT1 僅支持 TrueType 格式,而 FT2 支持很多格式。

            l???????? FT2 APIs FT1 APIs 簡(jiǎn)單且強(qiáng)大。

            l???????? FT1 包括 OpenType 文本布局處理擴(kuò)展,而 FT2 中則不包括而是移到獨(dú)立的工程里面―― FreeType Layout 。( FT 布局目前無法獲取)

            6、? FreeType2 是否兼容 FreeType 1.x

            FreeType2 不直接兼容 FreeType 1.x ,但是我們可以提供一個(gè)二進(jìn)制兼容層使得應(yīng)用程序重鏈接到新版本。我們最終放棄了這種想法因?yàn)閮蓚€(gè)版本可以共存在一個(gè)系統(tǒng)中。(沒有命名沖突)

            FT2 API 1.x 簡(jiǎn)單且強(qiáng)大,所以我們鼓勵(lì)你采用新版本,這樣可以使你減少很多不必要的工作。

            7、? 是否可以使用 FreeType2 編輯字體或者創(chuàng)建新字體?

            答案是明確的:不可以。因?yàn)樵搸煸O(shè)計(jì)明確,用較少代碼和內(nèi)存讀取字體文件。所以我們不打算以任何方式在字體引擎里面支持編輯或者創(chuàng)建功能,因?yàn)檫@樣將導(dǎo)致整個(gè)代碼重寫。這并不意味我們將來不會(huì)引入字體編輯 / 創(chuàng)建功能庫,這取決于需求(或者說有多少人愿意為此買單)。

            在我們正式發(fā)布前不要在這方面進(jìn)行揣測(cè),對(duì)我們而言這個(gè)項(xiàng)目存在其他一些更重要的部分需要解決(像文字布局、文本緩存)。

            編譯 & 配置

            1、? 如何編譯 FreeType2 庫?

            可以采取多種編譯方式,在 freetype2/docs/build 下有詳細(xì)說明文檔。

            這里介紹最簡(jiǎn)單的基于 VS IDE 的編譯方式。 freetype\builds\win32\visualc 下有 VC6 VC7.1 的工作區(qū)文件。 VC6 打開后直接編譯,有幾個(gè)警告。



            光看或許無法到感性認(rèn)識(shí),于是來兩個(gè)demo。網(wǎng)上比較少,我是參考nehe教程寫的。總體來說會(huì)簡(jiǎn)單使用了,如果想深入了解怕是非看他的document不可。
            簡(jiǎn)單使用示例

            FT_Library????pFTLib???????? = ?NULL;
            ????FT_Face????????pFTFace????????
            = ?NULL;
            ????FT_Error????error????????
            = ? 0 ;
            ????
            // ?Init?FreeType?Lib?to?manage?memory
            ????error? = ?FT_Init_FreeType( & pFTLib);
            ????
            if (error)
            ????
            {
            ????????pFTLib?
            = ? 0 ;
            ????????printf(
            " There?is?some?error?when?Init?Library " );
            ????????
            return ? - 1 ;
            ????}


            ????
            // ?create?font?face?from?font?file
            ????error? = ?FT_New_Face(pFTLib,? " C:\\WINDOWS\\Fonts\\arial.ttf " ,? 0 ,? & pFTFace);
            ????
            if ( ! error)
            ????
            {
            ????????FT_Set_Char_Size(pFTFace,?
            16 << 6 ,? 16 << 6 ,? 300 ,? 300 );
            ????????FT_Glyph????glyph;
            ????????
            // ?load?glyph?'C'
            ????????FT_Load_Glyph(pFTFace,?FT_Get_Char_Index(pFTFace,? 67 ),?FT_LOAD_DEFAULT);
            ????????error?
            = ?FT_Get_Glyph(pFTFace -> glyph,? & glyph);
            ????????
            if ( ! error)
            ????????
            {
            ????????????
            // ?convert?glyph?to?bitmap?with?256?gray
            ????????????FT_Glyph_To_Bitmap( & glyph,?ft_render_mode_normal,? 0 ,? 1 );
            ????????????FT_BitmapGlyph????bitmap_glyph?
            = ?(FT_BitmapGlyph)glyph;
            ????????????FT_Bitmap
            & ????bitmap? = ?bitmap_glyph -> bitmap;
            ????????????
            for ( int ?i = 0 ;?i < bitmap.rows;? ++ i)
            ????????????
            {
            ????????????????
            for ( int ?j = 0 ;?j < bitmap.width;? ++ j)
            ????????????????
            {
            ????????????????????
            // ?if?it?has?gray>0?we?set?show?it?as?1,?o?otherwise
            ????????????????????printf( " %d " ,?bitmap.buffer[i * bitmap.width + j] ? 1 : 0 );
            ????????????????}

            ????????????????printf(
            " \n " );
            ????????????}

            ????????????
            // ?free?glyph
            ????????????FT_Done_Glyph(glyph);
            ????????????glyph?
            = ?NULL;
            ????????}

            ????????
            // ?free?face
            ????????FT_Done_Face(pFTFace);
            ????????pFTFace?
            = ?NULL;
            ????}


            ????
            // ?free?FreeType?Lib
            ????FT_Done_FreeType(pFTLib);
            ????pFTLib?
            = ?NULL;

            posted @ 2006-11-10 23:00 HUYU 閱讀(2174) | 評(píng)論 (5)編輯 收藏

            2006年10月30日 #

            從笑話中悟出C++開發(fā)管理之"道"

            1. 程序員寫出自認(rèn)為沒有Bug的代碼。

            2. 軟件測(cè)試,發(fā)現(xiàn)了20個(gè)Bug。

            3. 程序員修改了10個(gè)Bug,并告訴測(cè)試組另外10個(gè)不是Bug。

            4. 測(cè)試組發(fā)現(xiàn)其中5個(gè)改動(dòng)根本無法工作,同時(shí)又發(fā)現(xiàn)了15個(gè)新Bug。

            5. 重復(fù)3次步驟3和步驟4。

            6. 鑒于市場(chǎng)方面的壓力,為了配合當(dāng)初制定的過分樂觀的發(fā)布時(shí)間表,產(chǎn)品終于上市了。

            7. 用戶發(fā)現(xiàn)了137個(gè)新Bug。

            8. 已經(jīng)領(lǐng)了項(xiàng)目獎(jiǎng)金的程序員不知跑到哪里去了。

            9. 新組建的項(xiàng)目組修正了差不多全部137個(gè)Bug,但又發(fā)現(xiàn)了456個(gè)新Bug。

            10. 最初那個(gè)程序員從斐濟(jì)給飽受拖欠工資之苦的測(cè)試組寄來了一張明信片。整個(gè)測(cè)試組集體辭職.

            11. 公司被競(jìng)爭(zhēng)對(duì)手惡意收購(gòu)。收購(gòu)時(shí),軟件的最終版本包含783個(gè)Bug。

            12. 新CEO走馬上任。公司雇了一名新程序員重寫該軟件。

            13. 程序員寫出自認(rèn)為沒有Bug的代碼。

              要我說,如果真有這樣的公司,不倒閉對(duì)不起人民。

             這個(gè)笑話從程序員開始,到程序員結(jié)束,從頭到尾都在說程序員的不是。但是我要說的是,這完全是管理者的失敗,從整個(gè)過程中,看不到任何管理工作。這種管理者不但無知無能,還很無恥——將自己的失敗責(zé)任推給程序員。

             1、程序員憑什么證明他的代碼沒有BUG?有Test case嗎?有Code review嗎?這個(gè)環(huán)節(jié)管理缺失。

             2、測(cè)試發(fā)現(xiàn)BUG有進(jìn)行BUG管理嗎?有跟蹤嗎?這個(gè)環(huán)節(jié)管理缺失。
             3、憑什么證明程序員已經(jīng)把那10個(gè)BUG修改好了?另10個(gè)又為什么不是BUG?BUG的評(píng)價(jià)標(biāo)準(zhǔn)難道是程序員說了算?這個(gè)環(huán)節(jié)管理缺失。

             4、5個(gè)不能工作的BUG修改問題有沒有追究責(zé)任?增加新BUG是修改過程中不可避免的事情,但是如果有有效的單元測(cè)試機(jī)制,可以大大減少這種情況。這個(gè)環(huán)節(jié)管理缺失。

             5、迭代是正常的,但是問題處理于發(fā)散而不是收斂發(fā)展,可見沒有有效的管理調(diào)控。這個(gè)環(huán)節(jié)管理缺失。

             6、過于樂觀的時(shí)間表和不可能達(dá)到的最后期限,都表現(xiàn)出管理者的無知和無能。而在這樣的情況下強(qiáng)行推出產(chǎn)品,那就是無知者無畏了。

             7、這是對(duì)用戶的不負(fù)責(zé)任,管理者要負(fù)最大的責(zé)任。

             8、這樣的情況還能發(fā)項(xiàng)目獎(jiǎng)金,只能說管理者不是一般的愚蠢。

             9、管理工作沒有任何的改進(jìn),問題仍然處于發(fā)散迭代狀態(tài)。管理工作依然沒有到位。

             10、拖欠測(cè)試部門工資體現(xiàn)出管理者對(duì)質(zhì)量管理工作的忽視以及對(duì)人力資源管理方面一無所知。

             11、送被收購(gòu)者兩個(gè)字:活該。送收購(gòu)者兩個(gè)字:瞎眼。

             12、可見新管理者與原管理者半斤八兩,都沒有認(rèn)識(shí)到問題的根本所在。不過也只有這樣的管理者才會(huì)作出收購(gòu)這種公司的決策。

             13、歷史的重演是必然的。

             一個(gè)正常的企業(yè)或是項(xiàng)目,其運(yùn)作必須應(yīng)該是循環(huán)向上進(jìn)行的。而保障這種運(yùn)行的工作就是管理。而管理工作的主要內(nèi)容就是控制,包括控制循環(huán)的節(jié)奏——不能太快也不能太慢,控制發(fā)展的方向——只能向上不能向下,控制運(yùn)作的穩(wěn)定——不能大起大落或時(shí)聚時(shí)散等。
             而這一切,在這個(gè)例子中都看不到。

             在這個(gè)笑話的例子中,一切都是以開發(fā)工作在驅(qū)動(dòng),這首先就是一個(gè)方向性錯(cuò)誤,產(chǎn)品是為用戶服務(wù)的,當(dāng)然應(yīng)該是以用戶和市場(chǎng)作為驅(qū)動(dòng),并且結(jié)合自身的能力最終 確定工作的重點(diǎn)。這一錯(cuò)誤折射出管理者對(duì)被管理的內(nèi)容很不了解,只好任由比較了解的程序員擺布——事實(shí)上他們除了技術(shù),并不會(huì)了解更多。

             一個(gè)管理者如果對(duì)自己所管理的內(nèi)容不了解,他就不可能管理得好。

             這是一件毫無疑問的事,可是國(guó)內(nèi)的軟件業(yè)似乎總是不相信這一點(diǎn)。中國(guó)軟件業(yè)中流毒最深的謊言之一就是:

             管理者只要懂管理就可以,不需要懂技術(shù)。

            其實(shí)這不過是那些無知無能無恥的管理者為了騙錢而編出來的,相信這句話的人必將付出金錢的代價(jià)。

             其次是質(zhì)量管理。基本的質(zhì)量管理常識(shí)告訴我們,每次循環(huán)結(jié)束前,最重的工作就是總結(jié)改進(jìn)。只有這樣才能保證循環(huán)運(yùn)作是向上發(fā)展,而不是失去控制地向下發(fā)展。 也只有有效的質(zhì)量管理,才能保證迭代過程是收斂發(fā)展,并最終達(dá)到目標(biāo)。但在這個(gè)例子中,這個(gè)部分顯然是缺失的——其中雖然有測(cè)試部門,但是他們的作用僅僅 是質(zhì)量管理中的質(zhì)量檢測(cè)環(huán)節(jié),管理部分還是缺失的。

             然后是人力資源管理。軟件開發(fā)是一項(xiàng)勞動(dòng)密集型的工作,雖然這是腦力勞動(dòng),但同樣意味著人在因素在其中占有決定性的地位。而例子中未改完BUG的程 序員拿到項(xiàng)目獎(jiǎng)金,而同樣辛苦工作的測(cè)試人員卻被拖欠薪資,除了表現(xiàn)出管理者對(duì)他們的工作內(nèi)容的不了解,以及對(duì)質(zhì)量管理工作的不重視以外,還表現(xiàn)出管理者 完全不會(huì)管人,這是一種謀殺團(tuán)隊(duì)的行為——謀殺一個(gè)團(tuán)隊(duì)遠(yuǎn)比建設(shè)要容易得多。

             最后,這個(gè)失敗的管理者把他的經(jīng)歷編成這個(gè)笑話,讓大家看到他被程序員們害得多慘,把程序員妖魔化為一群騙子。但只要稍懂管理的人簡(jiǎn)單分析一下就可以看出來,只不過是這個(gè)人的無知和無能造成了他現(xiàn)在的結(jié)果,而把責(zé)任推給別人的行為更是表現(xiàn)出他的無恥。

             作為身居高位的管理者,如果連應(yīng)該承擔(dān)的責(zé)任都要推卸,他們還能勝任什么事情呢?

            posted @ 2006-10-30 08:49 HUYU 閱讀(281) | 評(píng)論 (0)編輯 收藏

            2006年10月13日 #

            關(guān)注對(duì)TinyXML的應(yīng)用

            關(guān)注對(duì)TinyXML的應(yīng)用
            http://sourceforge.net/projects/tinyxml/

            posted @ 2006-10-13 13:56 HUYU 閱讀(406) | 評(píng)論 (0)編輯 收藏

            2006年10月4日 #

            strlen & strcmp

            unsigned int strlenW(const wchar_t *wcs)
            {
            ?const wchar_t *eos = wcs;

            ?while (*eos)
            ???? ++eos;

            ?return eos-wcs;
            }


            int strcmpW(const wchar_t *pwc1, const wchar_t *pwc2)
            {
            ?int ret = 0;

            ?while ( !(ret = *pwc1 - *pwc2) && *pwc2)
            ??++pwc1, ++pwc2;
            ?return ret;
            }

            posted @ 2006-10-04 00:02 HUYU 閱讀(376) | 評(píng)論 (0)編輯 收藏

            如何識(shí)別字符串的編碼?

            如果哪一天你的程序收到一段不明編碼的字符串,或者別人給了一個(gè)你看不懂的文本文件,你應(yīng)該如何去識(shí)別字符串的編碼呢?

            一種是程序中用的方法,可以使用ICU之類的庫來幫你識(shí)別,如果你的字符串越長(zhǎng),它所能猜到的概率就越大。

            另外一種方法是使用IE來幫助你查看。使用IE打開不明編碼的文件,然后選擇Encoding,不停的切換編碼,基本上看起來像文字的時(shí)候,就是那個(gè)編碼了:).這個(gè)方法很簡(jiǎn)單,比較實(shí)用。

            另外對(duì)于unicode的編碼,觀察其BOM,也有助于你去猜測(cè)編碼。
            UTF-8: EF BB BF E6 B5 8B E8 AF 95 31 32 33 34
            UTF-16: FF FE 4B 6D D5 8B 31 00 32 00 33 00 34 00
            UTF-16 Big endian : FE FF 6D 4B 8B D5 00 31 00 32 00 33 00 34


            最后附上兩個(gè)小工具,能幫你生成各種文字的字符和識(shí)別字符在不同code page下的編碼。just have fun

            http://www.shnenglu.com/Files/sandy/encoding_tools.rar

            posted @ 2006-10-04 00:01 HUYU 閱讀(1102) | 評(píng)論 (0)編輯 收藏

            2006年9月4日 #

            c++中的string用法(三)

                 摘要: basic_string::max_size 返回string 能放的最大元素個(gè)數(shù)。(不同于capacity) size _ type max _ ...  閱讀全文

            posted @ 2006-09-04 18:49 HUYU 閱讀(8406) | 評(píng)論 (0)編輯 收藏

            僅列出標(biāo)題  下一頁
            久久亚洲精品国产精品婷婷| 国产精品久久久久乳精品爆| a级成人毛片久久| 99久久精品国产一区二区三区| 亚洲国产精品久久久久网站 | 久久频这里精品99香蕉久| 亚洲精品乱码久久久久久久久久久久 | 亚洲国产精品18久久久久久| 精品久久久久久中文字幕大豆网 | 一本色道久久综合狠狠躁| 久久国产亚洲精品无码| 久久精品亚洲欧美日韩久久| 亚洲日韩中文无码久久| 久久福利青草精品资源站免费 | 久久人人爽人人爽人人片AV不| 狠狠色丁香久久婷婷综合五月| 久久精品国产亚洲5555| 国产人久久人人人人爽| 日本加勒比久久精品| 国产精品久久久久久久久免费| 久久久久久亚洲精品影院| 久久er国产精品免费观看2| 免费无码国产欧美久久18| 99久久精品免费观看国产| 久久这里只有精品18| 久久强奷乱码老熟女| 久久久青草青青亚洲国产免观| 久久人人爽人人爽人人片AV东京热| 青青草原综合久久| 久久99精品久久只有精品| 久久久黄片| 91精品国产91久久久久久青草| 伊人久久综合成人网| 亚洲国产一成久久精品国产成人综合| 国产精品久久久久久福利69堂| 人人妻久久人人澡人人爽人人精品| 久久国产成人| 国产精品久久久久久久午夜片| 91精品国产高清91久久久久久| 色欲av伊人久久大香线蕉影院| 一本久道久久综合狠狠躁AV|