• <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>
            posts - 319, comments - 22, trackbacks - 0, articles - 11
              C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

                 摘要: 時(shí)常在cpp的代碼之中看到這樣的代碼:   view plaincopy to clipboardprint? #ifdef __cplusplus    extern "C" {    #endif    /...  閱讀全文

            posted @ 2011-04-19 23:00 RTY 閱讀(182) | 評(píng)論 (0)編輯 收藏

              靜態(tài)鏈接庫(kù)(Lib)和動(dòng)態(tài)鏈接庫(kù)(DLL)的問(wèn)題困擾了我很長(zhǎng)時(shí)間,而當(dāng)中關(guān)鍵的問(wèn)題是兩者有何聯(lián)系?又有何區(qū)別呢?怎么創(chuàng)建?怎么使用?使用的過(guò)程中要注意什么?一直想把這個(gè)問(wèn)題總結(jié)一下。

              在windows下一般可以看到后綴為dll和后綴為lib的文件,但這兩種文件可以分為三種庫(kù),分別是動(dòng)態(tài)鏈接庫(kù)(Dynamic-Link Libraries),目標(biāo)庫(kù)(Object Libraries)和導(dǎo)入庫(kù)(Import Libraries),下面一一解釋這三種庫(kù)。

            目標(biāo)庫(kù)(Object Libraries)

            目標(biāo)庫(kù)又叫靜態(tài)鏈接庫(kù),是擴(kuò)展名為.LIB的文件,包括了用戶(hù)程序要用到的各種函數(shù)。它在用戶(hù)程序進(jìn)行鏈接時(shí),“靜態(tài)鏈接”到可執(zhí)行程序文件當(dāng)中。例如,在VC++中最常使用到的C運(yùn)行時(shí)目標(biāo)庫(kù)文件就是LIBC.LIB。在鏈接應(yīng)用程序時(shí)常使用所謂“靜態(tài)鏈接”的方法,即將各個(gè)目標(biāo)文件(.obj)、運(yùn)行時(shí)函數(shù)庫(kù)(.lib)以及已編譯的資源文件(.res)鏈接到一起,形成一個(gè)可執(zhí)行文件(.exe)。使用靜態(tài)鏈接時(shí),可執(zhí)行文件需要使用的各種函數(shù)和資源都已包含到文件中。這樣做的缺點(diǎn)是對(duì)于多個(gè)程序都使用的相同函數(shù)和資源要重復(fù)鏈接到exe文件中,使程序變大、占用內(nèi)存增加。  

            導(dǎo)入庫(kù)(Import Libraries)

            導(dǎo)入庫(kù)是一種特殊形式的目標(biāo)庫(kù)文件形式。和目標(biāo)庫(kù)文件一樣,導(dǎo)入庫(kù)文件的擴(kuò)展名也是.LIB,也是在用戶(hù)程序被鏈接時(shí),被“靜態(tài)鏈接”到可執(zhí)行文件當(dāng)中。但是不同的是,導(dǎo)入庫(kù)文件中并不包含有程序代碼。相應(yīng)的,它包含了相關(guān)的鏈接信息,幫助應(yīng)用程序在可執(zhí)行文件中建立起正確的對(duì)應(yīng)于動(dòng)態(tài)鏈接庫(kù)的重定向表。比如KERNEL32.LIB、USER32.LIB和GDI32.LIB就是我們常用到的導(dǎo)入庫(kù),通過(guò)它們,我們就可以調(diào)用Windows提供的函數(shù)了。如果我們?cè)诔绦蛑惺褂玫搅薘ectangle這個(gè)函數(shù),GDI32.LIB就可以告訴鏈接器,這個(gè)函數(shù)在GDI32.DLL動(dòng)態(tài)鏈接庫(kù)文件中。這樣,當(dāng)用戶(hù)程序運(yùn)行時(shí),它就知道“動(dòng)態(tài)鏈接”到GDI32.DLL模塊中以使用這個(gè)函數(shù)。其實(shí)說(shuō)白了導(dǎo)入庫(kù)就是一個(gè)索引,一個(gè)dll動(dòng)態(tài)鏈接庫(kù)的索引表,這是我的理解。

            動(dòng)態(tài)鏈接庫(kù)(Dynamic-Link Libraries)

            “動(dòng)態(tài)鏈接”是將一些公用的函數(shù)或資源組織成動(dòng)態(tài)鏈接庫(kù)文件(.dll),當(dāng)某個(gè)需要使用dll中的函數(shù)或資源的程序啟動(dòng)時(shí)(準(zhǔn)確的說(shuō)是初始化時(shí)),系統(tǒng)將該dll映射到調(diào)用進(jìn)程的虛擬地址空間、增加該dll的引用計(jì)數(shù)值,然后當(dāng)實(shí)際使用到該dll時(shí)操作系統(tǒng)就將該dll載入內(nèi)存;如果使用該dll的所有程序都已結(jié)束,則系統(tǒng)將該庫(kù)從內(nèi)存中移除。使用同一dll的各個(gè)進(jìn)程在運(yùn)行時(shí)共享dll的代碼,但是對(duì)于dll中的數(shù)據(jù)則各有一份拷貝(當(dāng)然也有在dll中共享數(shù)據(jù)的方法)。 動(dòng)態(tài)鏈接庫(kù)中可以定義兩種函數(shù):輸出函數(shù)和內(nèi)部函數(shù)。輸出函數(shù)可以被其他模塊調(diào)用,內(nèi)部函數(shù)只能被動(dòng)態(tài)鏈接庫(kù)本身調(diào)用。動(dòng)態(tài)鏈接庫(kù)也可以輸出數(shù)據(jù),但這些數(shù)據(jù)通常只被它自己的函數(shù)所使用。

              如我們所知,Windows程序都是一些可執(zhí)行文件,它們可以創(chuàng)建并顯示一個(gè)或多個(gè)窗體,使用消息循環(huán)來(lái)接收用戶(hù)的輸入。但是動(dòng)態(tài)鏈接庫(kù)并不能直接被執(zhí)行,它們一般也不會(huì)接收消息。它們只是一些包含著函數(shù)的獨(dú)立文件,這些函數(shù)可以被Windows程序或者其它DLL調(diào)用以完成某項(xiàng)任務(wù)。 
              “動(dòng)態(tài)鏈接”是指Windows程序在運(yùn)行時(shí)才把自己需要存在于某個(gè)庫(kù)中的函數(shù)鏈接進(jìn)來(lái)。“靜態(tài)鏈接”是指Windows程序在編譯階段就把各種對(duì)象模塊(.OBJ)、運(yùn)行時(shí)庫(kù)(.LIB)和資源文件(.RES)鏈接到一起以創(chuàng)建一個(gè)可執(zhí)行文件(.EXE)。 
              DERNAL32.DLL,USER32.DLL,GDI32.DLL,各種驅(qū)動(dòng)程序如KEYBOARD.DRV,SYSTEM.DRV和MOUSE.DRV,顯卡和打印機(jī)驅(qū)動(dòng)程序等都是動(dòng)態(tài)鏈接庫(kù)。這些庫(kù)可以被所有的Windows程序共同使用。 
            有某些動(dòng)態(tài)鏈接庫(kù)(如字體文件)稱(chēng)為“resource-only”。它們只包括數(shù)據(jù),而不包括代碼。因此,動(dòng)態(tài)鏈接庫(kù)的目的之一就是為許多不同的程序提供函數(shù)和資源。在傳統(tǒng)的操作系統(tǒng)里,用戶(hù)程序在運(yùn)行時(shí)只能調(diào)用操作系統(tǒng)自身的某些函數(shù)。而在Windows操作系統(tǒng)下,模塊或程序調(diào)用另一個(gè)模塊中的函數(shù)來(lái)執(zhí)行是一種非常普遍的操作。因此,從某種角度看,對(duì)DLL進(jìn)行編程,其實(shí)是在對(duì)Windows操作系統(tǒng)作擴(kuò)展,也可以看作是在對(duì)用戶(hù)程序作擴(kuò)展。 
              動(dòng)態(tài)鏈接庫(kù)模塊可以有其它的擴(kuò)展名,但是標(biāo)準(zhǔn)的擴(kuò)展名是.DLL。只有具有標(biāo)準(zhǔn)擴(kuò)展句的動(dòng)態(tài)鏈接庫(kù)模塊才可以被Windows自動(dòng)加載。而如果是其它擴(kuò)展名的動(dòng)態(tài)鏈接庫(kù)模塊,程序必須使用LoadLibrary或者LoadLibraryEx函數(shù)來(lái)顯示加載。 
              我們可以發(fā)現(xiàn),在大型的應(yīng)用軟件中,會(huì)常常使用到動(dòng)態(tài)鏈接庫(kù)技術(shù)。舉個(gè)例子,假如我們要寫(xiě)一個(gè)大型的應(yīng)用軟件,其中包括了多個(gè)程序。我們可以發(fā)現(xiàn)很多程序可能都會(huì)使用到一些同樣的通用的函數(shù)。我們可以把這些通用的函數(shù)放到某個(gè)目標(biāo)庫(kù)文件中(.LIB),然后在鏈接是把它加到每個(gè)程序中進(jìn)行靜態(tài)鏈接。但是這是一種非常浪費(fèi)的方法,因?yàn)槊總€(gè)程序模塊中都會(huì)包括這些通用函數(shù)的獨(dú)立拷貝。另外,如果我們要改變庫(kù)文件中的某個(gè)函數(shù),就必須把所有使用到這個(gè)函數(shù)的程序都重新編譯一遍。但是,如果我們使用動(dòng)態(tài)鏈接庫(kù)的技術(shù),把所有這些通用函數(shù)都放到一個(gè)動(dòng)態(tài)鏈接庫(kù)文件當(dāng)中,我們就可以解決以上提到的各種問(wèn)題。首先,動(dòng)態(tài)鏈接庫(kù)在硬盤(pán)上只保留一個(gè)拷貝,程序只是在運(yùn)行時(shí)才會(huì)調(diào)用其中使用到的函數(shù),這樣我們就可以節(jié)省大量的程序存儲(chǔ)和運(yùn)行空間。其次,如果要修改某個(gè)通用函數(shù)時(shí),只要調(diào)用接口沒(méi)有改變,只是改變它的實(shí)現(xiàn)方法,那么我們就不必對(duì)每個(gè)用到它的程序都進(jìn)行重新編譯,而只要把動(dòng)態(tài)鏈接庫(kù)模塊重新編譯一遍就可以了。 
              動(dòng)態(tài)鏈接庫(kù)模塊也可以作為一個(gè)單獨(dú)的產(chǎn)品來(lái)發(fā)布。這樣程序開(kāi)發(fā)人員就可以使用第三方的模塊來(lái)開(kāi)發(fā)自己的應(yīng)用程序,提高了程序的復(fù)用程序,也節(jié)省了大量的時(shí)間和精力。

               目標(biāo)庫(kù)和導(dǎo)入庫(kù)都是在程序開(kāi)發(fā)過(guò)程中才使用到的,而動(dòng)態(tài)鏈接庫(kù)是在程序運(yùn)行時(shí)才使用的。在程序運(yùn)行時(shí),相應(yīng)的動(dòng)態(tài)鏈接庫(kù)文件必須已經(jīng)保存在硬盤(pán)上了。另外,如果要使用動(dòng)態(tài)鏈接庫(kù)文件,該文件必須要保存在同.EXE文件同一個(gè)目錄下,或者保存在當(dāng)前目錄、Windows系統(tǒng)目錄、Windows目錄或環(huán)境變量中PATH參數(shù)指定的目錄下。程序也是按照這個(gè)順序來(lái)搜尋它需要的動(dòng)態(tài)鏈接庫(kù)文件的。

            創(chuàng)建靜態(tài)鏈接庫(kù)(Lib)

              創(chuàng)建靜態(tài)鏈接庫(kù)比較簡(jiǎn)單,創(chuàng)建win32控制臺(tái)程序,選擇靜態(tài)庫(kù),這里我沒(méi)有選擇上預(yù)編譯頭。生成工程以后就像定義一般的函數(shù)般,定義放在頭文件,然后實(shí)現(xiàn)放在cpp文件里頭,直接build就出來(lái)一個(gè)靜態(tài)的lib了,發(fā)布時(shí)附上頭文件給使用者就可以。

             

            創(chuàng)建動(dòng)態(tài)鏈接庫(kù)(DLL)

             

            用SDK創(chuàng)建一個(gè)簡(jiǎn)單的dll文件

            在VC++中選擇新建一個(gè)Win32 Dynamic-Link Library。需要建立一個(gè)c/c++ head file和一個(gè)c/c++ source file并加入工程。頭文件中內(nèi)容為輸出函數(shù)的聲明,源文件中內(nèi)容為DllMain函數(shù)和輸出函數(shù)的定義。下面是一個(gè)最簡(jiǎn)單的例子。

             

             

            頭文件代碼如下: 

             

            代碼
            #ifdef TEST_CREATE_DLL_EXPORTS
            #define TEST_CREATE_DLL_API __declspec(dllexport)
            #else
            #define TEST_CREATE_DLL_API __declspec(dllimport)
            #endif

            // This class is exported from the test_create_dll.dll
            class TEST_CREATE_DLL_API Ctest_create_dll {
            public:
            Ctest_create_dll(void);
            // TODO: add your methods here.
            };

            extern TEST_CREATE_DLL_API int ntest_create_dll;

            TEST_CREATE_DLL_API int fntest_create_dll(void);

             

            在創(chuàng)建工程的時(shí)候????TEST_CREATE_DLL_EXPORTS就已經(jīng)在預(yù)定義處定義過(guò),生成導(dǎo)出dll。

            頭文件預(yù)處理中的__declspec是微軟增加的“C擴(kuò)展類(lèi)存儲(chǔ)屬性”(C Extended Storage-Class Attributes),它指明一個(gè)給出的實(shí)例被存儲(chǔ)為一種微軟特定的類(lèi)存儲(chǔ)屬性,可以為thread,naked,dllimport或dllexport. [MSDN原文:The extended attribute syntax for specifying storage-class information uses the __declspec keyword, which specifies that an instance of a given type is to be stored with a Microsoft-specific storage-class attribute (thread, naked, dllimport, or dllexport).] 輸出函數(shù)必須指明為CALLBACK。 DllMain是dll的入口點(diǎn)函數(shù)。也可以不寫(xiě)它。DllMain必須返回TRUE,否則系統(tǒng)將終止程序并彈出一個(gè)“啟動(dòng)程序時(shí)出錯(cuò)”對(duì)話(huà)框。 編譯鏈接后,得到動(dòng)態(tài)鏈接庫(kù)文件dlldemo.dll和輸入庫(kù)文件dlldemo.lib。

            _declspec(dllexport) 

            聲明一個(gè)導(dǎo)出函數(shù),是說(shuō)這個(gè)函數(shù)要從本DLL導(dǎo)出。我要給別人用。一般用于dll中 。
            省掉在DEF文件中手工定義導(dǎo)出哪些函數(shù)的一個(gè)方法。當(dāng)然,如果你的DLL里全是C++的類(lèi)的話(huà),你無(wú)法在DEF里指定導(dǎo)出的函數(shù),只能用__declspec(dllexport)導(dǎo)出類(lèi)。

             

            __declspec(dllimport)

            聲明一個(gè)導(dǎo)入函數(shù),是說(shuō)這個(gè)函數(shù)是從別的DLL導(dǎo)入。我要用。一般用于使用某個(gè)dll的exe中 。
            不使用 __declspec(dllimport) 也能正確編譯代碼,但使用 __declspec(dllimport) 使編譯器可以生成更好的代碼。編譯器之所以能夠生成更好的代碼,是因?yàn)樗梢源_定函數(shù)是否存在于 DLL 中,這使得編譯器可以生成跳過(guò)間接尋址級(jí)別的代碼,而這些代碼通常會(huì)出現(xiàn)在跨 DLL 邊界的函數(shù)調(diào)用中。但是,必須使用 __declspec(dllimport) 才能導(dǎo)入 DLL 中使用的變量。

            相信寫(xiě)WIN32程序的人,做過(guò)DLL,都會(huì)很清楚__declspec(dllexport)的作用,它就是為了省掉在DEF文件中手工定義導(dǎo)出哪些函數(shù)的一個(gè)方法。當(dāng)然,如果你的DLL里全是C++的類(lèi)的話(huà),你無(wú)法在DEF里指定導(dǎo)出的函數(shù),只能用__declspec(dllexport)導(dǎo)出類(lèi)。但是,MSDN文檔里面,對(duì)于__declspec(dllimport)的說(shuō)明讓人感覺(jué)有點(diǎn)奇怪,先來(lái)看看MSDN里面是怎么說(shuō)的:

            不使用 __declspec(dllimport) 也能正確編譯代碼,但使用 __declspec(dllimport) 使編譯器可以生成更好的代碼。編譯器之所以能夠生成更好的代碼,是因?yàn)樗梢源_定函數(shù)是否存在于 DLL 中,這使得編譯器可以生成跳過(guò)間接尋址級(jí)別的代碼,而這些代碼通常會(huì)出現(xiàn)在跨 DLL 邊界的函數(shù)調(diào)用中。但是,必須使用 __declspec(dllimport) 才能導(dǎo)入 DLL 中使用的變量。

            extern "C"   

            指示編譯器用C語(yǔ)言方法給函數(shù)命名。

            在制作DLL導(dǎo)出函數(shù)時(shí)由于C++存在函數(shù)重載,因此__declspec(dllexport)    function(int,int)    在DLL會(huì)被decorate,例如被decorate成為function_int_int,而且不同的編譯器decorate的方法不同,造成了在用GetProcAddress取得function地址時(shí)的不便,使用extern "C"時(shí),上述的decorate不會(huì)發(fā)生,因?yàn)镃沒(méi)有函數(shù)重載,但如此一來(lái)被extern"C"修飾的函數(shù),就不具備重載能力,可以說(shuō)extern 和extern "C"不是一回事。

            C++編譯器在生成DLL時(shí),會(huì)對(duì)導(dǎo)出的函數(shù)進(jìn)行名字改編,并且不同的編譯器使用的改變規(guī)則不一樣,因此改編后的名字會(huì)不一樣。這樣,如果利用不同的編譯器分別生成DLL和訪問(wèn)該DLL的客戶(hù)端代碼程序的話(huà),后者在訪問(wèn)該DLL的導(dǎo)出函數(shù)時(shí)會(huì)出現(xiàn)問(wèn)題。為了實(shí)現(xiàn)通用性,需要加上限定符:extern “C”。

            但是利用限定符extern “C”可以解決C++和C之間相互調(diào)用時(shí)函數(shù)命名的問(wèn)題,但是這種方法有一個(gè)缺陷,就是不能用于導(dǎo)出一個(gè)類(lèi)的成員函數(shù),只能用于導(dǎo)出全局函數(shù)。LoadLibrary導(dǎo)入的函數(shù)名,對(duì)于非改編的函數(shù),可以寫(xiě)函數(shù)名;對(duì)于改編的函數(shù),就必須吧@和號(hào)碼都寫(xiě)上,一樣可以加載成功,可以試試看。

            解決警告  inconsistent dll linkage

             inconsistent dll linkage警告是寫(xiě)dll時(shí)常遇到的一個(gè)問(wèn)題,解決此警告的方法如下:

              一般PREDLL_API工程依賴(lài)于是否定義了MYDLL_EXPORTS來(lái)決定宏展開(kāi)為_(kāi)_declspec(dllexport)還是__declspec(dllimport)。展開(kāi)為_(kāi)_declspec(dllexport)是DLL編譯時(shí)的需要,通知編譯器該函數(shù)是需要導(dǎo)出供外部調(diào)用的。展開(kāi)為_(kāi)_declspec(dllimport)是給調(diào)用者用的,通知編譯器,該函數(shù)是個(gè)外部導(dǎo)入函數(shù)。

            對(duì)于工程設(shè)置里面的預(yù)定義宏,是最早被編譯器看到的。所以當(dāng)編譯器編譯DLL工程中的MYDLL.cpp時(shí),因?yàn)榭吹角懊嬗泄こ淘O(shè)置有定義MYDLL_EXPORTS,所以就把PREDLL_API展開(kāi)為_(kāi)_declspec(dllexport)了。

            這樣做的目的是為了讓DLL和調(diào)用者共用同一個(gè)h文件,在DLL項(xiàng)目中,定義MYDLL_EXPORTS,PREDLL_API就是導(dǎo)出;在調(diào)用該DLL的項(xiàng)目中,不定義MYDLL_EXPORTS,PREDLL_API就是導(dǎo)入。

            使用靜態(tài)鏈接庫(kù)(Lib)

             

              使用靜態(tài)鏈接庫(kù)需要庫(kù)的開(kāi)發(fā)者提供庫(kù)的頭文件以及l(fā)ib文件,一般來(lái)說(shuō)lib文件都比較大(相對(duì)導(dǎo)入庫(kù)來(lái)說(shuō)),靜態(tài)鏈接庫(kù)是將全部指令都包含入調(diào)用程序生成的EXE文件中,并不存在“導(dǎo)出某個(gè)函數(shù)提供給用戶(hù)使用”的情況,就是要么全要,要么都不要。

            使用動(dòng)態(tài)鏈接庫(kù)(DLL)

             

            方法一: load-time dynamic linking (隱式調(diào)用)
              在要調(diào)用dll的應(yīng)用程序鏈接時(shí),將dll的輸入庫(kù)文件(import library,.lib文件)包含進(jìn)去。具體的做法是在源文件開(kāi)頭加一句#include ,然后就可以在源文件中調(diào)用dlldemo.dll中的輸出文件了。

            #pragma comment(lib, "***.lib") //通知編譯器DLL的.lib文件所在路徑及文件名,也可以不采用該語(yǔ)句,在屬性欄——輸入——附加依賴(lài)項(xiàng)處添加對(duì)應(yīng)的lib就可以編譯鏈接應(yīng)用程序了。

            extern "C" __declspec(dllimport) foo(); //聲明導(dǎo)入函數(shù)

            方法二: run-time dynamic linking (顯式調(diào)用)
              不必在鏈接時(shí)包含輸入庫(kù)文件,而是在源程序中使用LoadLibrary或LoadLibraryEx動(dòng)態(tài)的載入dll。
              主要步驟為(以demodll.dll為例): 

            1) typedef函數(shù)原型和定義函數(shù)指針。
              typedef void (CALLBACK* DllFooType)(void) ;
              DllFooType pfnDllFoo = NULL ;
            2) 使用LoadLibrary載入dll,并保存dll實(shí)例句柄
              HINSTANCE dllHandle = NULL ;
              ... 
              dllHandle = LoadLibrary(TEXT("dlldemo.dll"));
            3) 使用GetProcAddress得到dll中函數(shù)的指針
              pfnDllFoo = (DllFooType)GetProcAddress(dllHandle,TEXT("DllFoo")) ;
              注意從GetProcAddress返回的指針必須轉(zhuǎn)型為特定類(lèi)型的函數(shù)指針。
            4)檢驗(yàn)函數(shù)指針,如果不為空則可調(diào)用該函數(shù) 
              if(pfnDllFoo!=NULL)
              DllFoo() ;
            5)使用FreeLibrary卸載dll
              FreeLibrary(dllHandle) ;

            動(dòng)態(tài)鏈接庫(kù)(DLL)的優(yōu)點(diǎn)

             

              →節(jié)約內(nèi)存;
              →使應(yīng)用程序“變瘦”;
              →可單獨(dú)修改動(dòng)態(tài)鏈接庫(kù)而不必與應(yīng)用程序重新鏈接;
              →可方便實(shí)現(xiàn)多語(yǔ)言聯(lián)合編程(比如用VC++寫(xiě)個(gè)dll,然后在VB中調(diào)用);
              →可將資源打包;
              →可在應(yīng)用程序間共享內(nèi)存
              →......

             

            杭州京都醫(yī)院http://www.fjzzled.com/京都醫(yī)院

            posted @ 2011-04-19 22:58 RTY 閱讀(1302) | 評(píng)論 (0)編輯 收藏

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

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

                 棧:由編譯器(Compiler)自動(dòng)分配釋放,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類(lèi)似于數(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開(kāi)辟空間

             

            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語(yǔ)句才能正確的釋放本內(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)是用鏈表來(lái)存儲(chǔ)的空閑內(nèi)存地址的,自然是不連續(xù)的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限于計(jì)算機(jī)系統(tǒng)中有效的虛擬內(nèi)存。由此可見(jiàn),堆獲得的空間比較靈活,也比較大。

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

             

            5、效率方面:

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

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

             

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

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

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

             

            7、存取效率方面:

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

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

            posted @ 2011-04-19 22:55 RTY 閱讀(232) | 評(píng)論 (0)編輯 收藏

            1.初識(shí)Visual Leak Detector
                  靈活自由是C/C++語(yǔ)言的一大特色,而這也為C/C++程序員出了一個(gè)難題。當(dāng)程序越來(lái)越復(fù)雜時(shí),內(nèi)存的管理也會(huì)變得越加復(fù)雜,稍有不慎就會(huì)出現(xiàn)內(nèi)存問(wèn)題。內(nèi)存泄漏是最常見(jiàn)的內(nèi)存問(wèn)題之一。內(nèi)存泄漏如果不是很?chē)?yán)重,在短時(shí)間內(nèi)對(duì)程序不會(huì)有太大的影響,這也使得內(nèi)存泄漏問(wèn)題有很強(qiáng)的隱蔽性,不容易被發(fā)現(xiàn)。然而不管內(nèi)存泄漏多么輕微,當(dāng)程序長(zhǎng)時(shí)間運(yùn)行時(shí),其破壞力是驚人的,從性能下降到內(nèi)存耗盡,甚至?xí)绊懙狡渌绦虻恼_\(yùn)行。另外內(nèi)存問(wèn)題的一個(gè)共同特點(diǎn)是,內(nèi)存問(wèn)題本身并不會(huì)有很明顯的現(xiàn)象,當(dāng)有異常現(xiàn)象出現(xiàn)時(shí)已時(shí)過(guò)境遷,其現(xiàn)場(chǎng)已非出現(xiàn)問(wèn)題時(shí)的現(xiàn)場(chǎng)了,這給調(diào)試內(nèi)存問(wèn)題帶來(lái)了很大的難度。            

            Visual Leak Detector是一款用于Visual C++的免費(fèi)的內(nèi)存泄露檢測(cè)工具。可以在http://www.codeproject.com/tools/visualleakdetector.asp下載到。相比較其它的內(nèi)存泄露檢測(cè)工具,它在檢測(cè)到內(nèi)存泄漏的同時(shí),還具有如下特點(diǎn):

            1、 可以得到內(nèi)存泄漏點(diǎn)的調(diào)用堆棧,如果可以的話(huà),還可以得到其所在文件及行號(hào);

            2、 可以得到泄露內(nèi)存的完整數(shù)據(jù);

            3、 可以設(shè)置內(nèi)存泄露報(bào)告的級(jí)別;

            4、 它是一個(gè)已經(jīng)打包的lib,使用時(shí)無(wú)須編譯它的源代碼。而對(duì)于使用者自己的代碼,也只需要做很小的改動(dòng);

            5、 他的源代碼使用GNU許可發(fā)布,并有詳盡的文檔及注釋。對(duì)于想深入了解堆內(nèi)存管理的讀者,是一個(gè)不錯(cuò)的選擇。            

            可見(jiàn),從使用角度來(lái)講,Visual Leak Detector簡(jiǎn)單易用,對(duì)于使用者自己的代碼,唯一的修改是#include Visual Leak Detector的頭文件后正常運(yùn)行自己的程序,就可以發(fā)現(xiàn)內(nèi)存問(wèn)題。從研究的角度來(lái)講,如果深入Visual Leak Detector源代碼,可以學(xué)習(xí)到堆內(nèi)存分配與釋放的原理、內(nèi)存泄漏檢測(cè)的原理及內(nèi)存操作的常用技巧等。      本文首先將介紹Visual Leak Detector的使用方法與步驟,然后再和讀者一起初步的研究Visual Leak Detector的源代碼,去了解Visual Leak Detector的工作原理。

             

            2.使用Visual Leak Detector(1.0)
                  下面讓我們來(lái)介紹如何使用這個(gè)小巧的工具。      

            首先從網(wǎng)站上下載zip包,解壓之后得到vld.h, vldapi.h, vld.lib, vldmt.lib, vldmtdll.lib, dbghelp.dll等文件。將.h文件拷貝到Visual C++的默認(rèn)include目錄下,將.lib文件拷貝到Visual C++的默認(rèn)lib目錄下,便安裝完成了。因?yàn)榘姹締?wèn)題,如果使用windows 2000或者以前的版本,需要將dbghelp.dll拷貝到你的程序的運(yùn)行目錄下,或其他可以引用到的目錄。      

            接下來(lái)需要將其加入到自己的代碼中。方法很簡(jiǎn)單,只要在包含入口函數(shù)的.cpp文件中包含vld.h就可以。如果這個(gè)cpp文件包含了stdafx.h,則將包含vld.h的語(yǔ)句放在stdafx.h的包含語(yǔ)句之后,否則放在最前面。如下是一個(gè)示例程序:

            #include<vld.h>

            void main()

            {

              …

            }      

            接下來(lái)讓我們來(lái)演示如何使用Visual Leak Detector檢測(cè)內(nèi)存泄漏。下面是一個(gè)簡(jiǎn)單的程序,用new分配了一個(gè)int大小的堆內(nèi)存,并沒(méi)有釋放。其申請(qǐng)的內(nèi)存地址用printf輸出到屏幕上。

            #include<vld.h>

            #include<stdlib.h>

            #include<stdio.h> 

            void f()

            {   

              int*p =newint(0x12345678);   

              printf("p=%08x, ", p);

            void main()

            {   

              f();

            }

            編譯運(yùn)行后,在標(biāo)準(zhǔn)輸出窗口得到:

            p=003a89c0 

            在Visual C++的Output窗口得到: 

            WARNING: Visual Leak Detector detected memory leaks!

            ---------- Block 57 at 0x003A89C0: 4 bytes ---------- --57號(hào)塊0x003A89C0地址泄漏了4個(gè)字節(jié) 

            Call Stack:                                             --下面是調(diào)用堆棧   

            d:\test\testvldconsole\testvldconsole\main.cpp (7): f --表示在main.cpp第7行的f()函數(shù)   

            d:\test\testvldconsole\testvldconsole\main.cpp (14): main–雙擊以引導(dǎo)至對(duì)應(yīng)代碼處   

            f:\rtm\vctools\crt_bld\self_x86\crt\src\crtexe.c (586): __tmainCRTStartup   

            f:\rtm\vctools\crt_bld\self_x86\crt\src\crtexe.c (403): mainCRTStartup   

            0x7C816D4F (File and line number not available): RegisterWaitForInputIdle 

            Data:                                 --這是泄漏內(nèi)存的內(nèi)容,0x12345678   78 56 34 12                                                 

            xV4..... ........ 

            Visual Leak Detector detected 1 memory leak.   

            第二行表示57號(hào)塊有4字節(jié)的內(nèi)存泄漏,地址為0x003A89C0,根據(jù)程序控制臺(tái)的輸出,可以知道,該地址為指針p。程序的第7行,f()函數(shù)里,在該地址處分配了4字節(jié)的堆內(nèi)存空間,并賦值為0x12345678,這樣在報(bào)告中,我們看到了這4字節(jié)同樣的內(nèi)容。

            可以看出,對(duì)于每一個(gè)內(nèi)存泄漏,這個(gè)報(bào)告列出了它的泄漏點(diǎn)、長(zhǎng)度、分配該內(nèi)存時(shí)的調(diào)用堆棧、和泄露內(nèi)存的內(nèi)容(分別以16進(jìn)制和文本格式列出)。雙擊該堆棧報(bào)告的某一行,會(huì)自動(dòng)在代碼編輯器中跳到其所指文件的對(duì)應(yīng)行。這些信息對(duì)于我們查找內(nèi)存泄露將有很大的幫助。

            這是一個(gè)很方便易用的工具,安裝后每次使用時(shí),僅僅需要將它頭文件包含進(jìn)來(lái)重新build就可以。而且,該工具僅在build Debug版的時(shí)候會(huì)連接到你的程序中,如果build Release版,該工具不會(huì)對(duì)你的程序產(chǎn)生任何性能等方面影響。所以盡可以將其頭文件一直包含在你的源代碼中。

             

            3.Visual Leak Detector工作原理
              下面讓我們來(lái)看一下該工具的工作原理。      

              在這之前,我們先來(lái)看一下Visual C++內(nèi)置的內(nèi)存泄漏檢測(cè)工具是如何工作的。Visual C++內(nèi)置的工具CRT Debug Heap工作原來(lái)很簡(jiǎn)單。在使用Debug版的malloc分配內(nèi)存時(shí),malloc會(huì)在內(nèi)存塊的頭中記錄分配該內(nèi)存的文件名及行號(hào)。當(dāng)程序退出時(shí)CRT會(huì)在main()函數(shù)返回之后做一些清理工作,這個(gè)時(shí)候來(lái)檢查調(diào)試堆內(nèi)存,如果仍然有內(nèi)存沒(méi)有被釋放,則一定是存在內(nèi)存泄漏。從這些沒(méi)有被釋放的內(nèi)存塊的頭中,就可以獲得文件名及行號(hào)。        這種靜態(tài)的方法可以檢測(cè)出內(nèi)存泄漏及其泄漏點(diǎn)的文件名和行號(hào),但是并不知道泄漏究竟是如何發(fā)生的,并不知道該內(nèi)存分配語(yǔ)句是如何被執(zhí)行到的。要想了解這些,就必須要對(duì)程序的內(nèi)存分配過(guò)程進(jìn)行動(dòng)態(tài)跟蹤。Visual Leak Detector就是這樣做的。它在每次內(nèi)存分配時(shí)將其上下文記錄下來(lái),當(dāng)程序退出時(shí),對(duì)于檢測(cè)到的內(nèi)存泄漏,查找其記錄下來(lái)的上下文信息,并將其轉(zhuǎn)換成報(bào)告輸出。      

            3.1初始化
              Visual Leak Detector要記錄每一次的內(nèi)存分配,而它是如何監(jiān)視內(nèi)存分配的呢?Windows提供了分配鉤子(allocation hooks)來(lái)監(jiān)視調(diào)試堆內(nèi)存的分配。它是一個(gè)用戶(hù)定義的回調(diào)函數(shù),在每次從調(diào)試堆分配內(nèi)存之前被調(diào)用。在初始化時(shí),Visual Leak Detector使用_CrtSetAllocHook注冊(cè)這個(gè)鉤子函數(shù),這樣就可以監(jiān)視從此之后所有的堆內(nèi)存分配了。      

              如何保證在Visual Leak Detector初始化之前沒(méi)有堆內(nèi)存分配呢?全局變量是在程序啟動(dòng)時(shí)就初始化的,如果將Visual Leak Detector作為一個(gè)全局變量,就可以隨程序一起啟動(dòng)。但是C/C++并沒(méi)有約定全局變量之間的初始化順序,如果其它全局變量的構(gòu)造函數(shù)中有堆內(nèi)存分配,則可能無(wú)法檢測(cè)到。Visual Leak Detector使用了C/C++提供的#pragma init_seg來(lái)在某種程度上減少其它全局變量在其之前初始化的概率。根據(jù)#pragma init_seg的定義,全局變量的初始化分三個(gè)階段:首先是compiler段,一般c語(yǔ)言的運(yùn)行時(shí)庫(kù)在這個(gè)時(shí)候初始化;然后是lib段,一般用于第三方的類(lèi)庫(kù)的初始化等;最后是user段,大部分的初始化都在這個(gè)階段進(jìn)行。Visual Leak Detector將其初始化設(shè)置在compiler段,從而使得它在絕大多數(shù)全局變量和幾乎所有的用戶(hù)定義的全局變量之前初始化。 

             

            4.記錄內(nèi)存分配
              一個(gè)分配鉤子函數(shù)需要具有如下的形式:intYourAllocHook(intallocType,void*userData, size_t size,intblockType,longrequestNumber,constunsignedchar*filename,intlineNumber);      就像前面說(shuō)的,它在Visual Leak Detector初始化時(shí)被注冊(cè),每次從調(diào)試堆分配內(nèi)存之前被調(diào)用。這個(gè)函數(shù)需要處理的事情是記錄下此時(shí)的調(diào)用堆棧和此次堆內(nèi)存分配的唯一標(biāo)識(shí)——requestNumber。      

              得到當(dāng)前的堆棧的二進(jìn)制表示并不是一件很復(fù)雜的事情,但是因?yàn)椴煌w系結(jié)構(gòu)、不同編譯器、不同的函數(shù)調(diào)用約定所產(chǎn)生的堆棧內(nèi)容略有不同,要解釋堆棧并得到整個(gè)函數(shù)調(diào)用過(guò)程略顯復(fù)雜。不過(guò)windows提供一個(gè)StackWalk64函數(shù),可以獲得堆棧的內(nèi)容。StackWalk64的聲明如下:BOOLStackWalk64( DWORDMachineType, HANDLEhProcess, HANDLEhThread, LPSTACKFRAME64StackFrame, PVOIDContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64TranslateAddress);STACKFRAME64結(jié)構(gòu)表示了堆棧中的一個(gè)frame。給出初始的STACKFRAME64,反復(fù)調(diào)用該函數(shù),便可以得到內(nèi)存分配點(diǎn)的調(diào)用堆棧了。   // Walk the stack.   while(count < _VLD_maxtraceframes) {       count++;       if(!pStackWalk64(architecture, m_process, m_thread, &frame, &context,                         NULL, pSymFunctionTableAccess64, pSymGetModuleBase64, NULL)) {           // Couldn't trace back through any more frames.           break;       }

            posted @ 2011-04-19 22:53 RTY 閱讀(381) | 評(píng)論 (0)編輯 收藏

            QList<T> 的釋放分兩種情況:

            1.T的類(lèi)型為非指針,這時(shí)候直接調(diào)用clear()方法就可以釋放了,看如下測(cè)試代碼

            #include <QtCore/QCoreApplication>#include <QList>#include <QString>
            int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    typedef struct _test    {        int id;        QString name;        QString sex;    }Por_test;    QList<Por_test>  slist;    for (int i=0;i<100000;i++)    {        Por_test s;        s.id = 1;        s.name = QString("hello World!");        s.sex = QString("男");        slist.append(s);    }    slist.clear();    return a.exec();}

            將上面代碼中的slist.clear(); 注釋掉,內(nèi)存顯示為如下(任務(wù)管理器里的截圖)

            111

            如不去掉的話(huà),內(nèi)存顯示如下圖

            22222

            2.T的類(lèi)型為指針的情況,這時(shí)候直接調(diào)用clear()方法將不能釋放,先看代碼

            #include <QtCore/QCoreApplication>#include <QList>#include <QString>int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    typedef struct _test    {        int id;        QString name;        QString sex;    }Por_test;    QList<Por_test *>  slist;    for (int i=0;i<100000;i++)    {        Por_test *s = new Por_test();        s->id = 1;        s->name = QString("hello World!");        s->sex = QString("男?");        slist.append(s);    }//    qDeleteAll(slist);    slist.clear();    return a.exec();}

             

            上面代碼運(yùn)行后的內(nèi)存情況如下圖

            3333

            說(shuō)明當(dāng)T的類(lèi)型為指針時(shí),調(diào)用clear()方法并不能釋放其內(nèi)存

            此時(shí)void qDeleteAll ( const Container & c )方法將派上用場(chǎng)了,將上面代碼中的注釋去掉以后,

            再次運(yùn)行程序,此時(shí)的內(nèi)存情況如下圖

            4444

            通過(guò)對(duì)比靚圖,可以看出,內(nèi)存已經(jīng)釋放,我們?cè)賮?lái)看下qt助手中qDeleteAll 方法的說(shuō)明

            void qDeleteAll ( ForwardIterator begin, ForwardIterator end )

            Deletes all the items in the range [beginend) using the C++ delete operator. The item type must be a pointer type (for example, QWidget *).

            Example:

             QList<Employee *> list; list.append(new Employee("Blackpool", "Stephen")); list.append(new Employee("Twist", "Oliver")); qDeleteAll(list.begin(), list.end()); list.clear();

            Notice that qDeleteAll() doesn't remove the items from the container; it merely calls delete on them. In the example above, we call clear() on the container to remove the items.

            This function can also be used to delete items stored in associative containers, such as QMap and QHash. Only the objects stored in each container will be deleted by this function; objects used as keys will not be deleted.

            See also forward iterators.

            void qDeleteAll ( const Container & c )

            This is an overloaded member function, provided for convenience.

            This is the same as qDeleteAll(c.begin(), c.end()).

             

            上面qDeleteAll 方法的說(shuō)明,已經(jīng)很清楚了,如果T為指針類(lèi)型時(shí),釋放內(nèi)存須在clear方法前加上qDeleteAll 方法。

            posted @ 2011-04-19 22:49 RTY 閱讀(3859) | 評(píng)論 (0)編輯 收藏

            app - 建立一個(gè)應(yīng)用程序的makefile。這是默認(rèn)值,所以如果模板沒(méi)有被指定,這個(gè)將被使用。

            lib - 建立一個(gè)庫(kù)的makefile。

            vcapp - 建立一個(gè)應(yīng)用程序的Visual Studio項(xiàng)目文件。

            vclib - 建立一個(gè)庫(kù)的Visual Studio項(xiàng)目文件。

            subdirs - 這是一個(gè)特殊的模板,它可以創(chuàng)建一個(gè)能夠進(jìn)入特定目錄并且為一個(gè)項(xiàng)目文件生成makefile并且為它調(diào)用make的makefile。

            “app”模板
            “app”模板告訴qmake為建立一個(gè)應(yīng)用程序生成一個(gè)makefile。當(dāng)使用這個(gè)模板時(shí),下面這些qmake系統(tǒng)變量是被承認(rèn)的。你應(yīng)該在你的.pro文件中使用它們來(lái)為你的應(yīng)用程序指定特定信息。

            HEADERS - 應(yīng)用程序中的所有頭文件的列表。

            SOURCES - 應(yīng)用程序中的所有源文件的列表。

            FORMS - 應(yīng)用程序中的所有.ui文件(由Qt設(shè)計(jì)器生成)的列表。

            LEXSOURCES - 應(yīng)用程序中的所有l(wèi)ex源文件的列表。

            YACCSOURCES - 應(yīng)用程序中的所有yacc源文件的列表。

            TARGET - 可執(zhí)行應(yīng)用程序的名稱(chēng)。默認(rèn)值為項(xiàng)目文件的名稱(chēng)。(如果需要擴(kuò)展名,會(huì)被自動(dòng)加上。)

            DESTDIR - 放置可執(zhí)行程序目標(biāo)的目錄。

            DEFINES - 應(yīng)用程序所需的額外的預(yù)處理程序定義的列表。

            INCLUDEPATH - 應(yīng)用程序所需的額外的包含路徑的列表。

            DEPENDPATH - 應(yīng)用程序所依賴(lài)的搜索路徑。

            VPATH - 尋找補(bǔ)充文件的搜索路徑。

            DEF_FILE - 只有Windows需要:應(yīng)用程序所要連接的.def文件。

            RC_FILE - 只有Windows需要:應(yīng)用程序的資源文件。

            RES_FILE - 只有Windows需要:應(yīng)用程序所要連接的資源文件。

            CONFIG變量
            配置變量指定了編譯器所要使用的選項(xiàng)和所需要被連接的庫(kù)。配置變量中可以添加任何東西,但只有下面這些選項(xiàng)可以被qmake識(shí)別。

            下面這些選項(xiàng)控制著使用哪些編譯器標(biāo)志:

            release - 應(yīng)用程序?qū)⒁詒elease模式連編。如果“debug”被指定,它將被忽略。

            debug - 應(yīng)用程序?qū)⒁詃ebug模式連編。

            warn_on - 編譯器會(huì)輸出盡可能多的警告信息。如果“warn_off”被指定,它將被忽略。

            warn_off - 編譯器會(huì)輸出盡可能少的警告信息。

            下面這些選項(xiàng)定義了所要連編的庫(kù)/應(yīng)用程序的類(lèi)型:

            qt - 應(yīng)用程序是一個(gè)Qt應(yīng)用程序,并且Qt庫(kù)將會(huì)被連接。

            thread - 應(yīng)用程序是一個(gè)多線(xiàn)程應(yīng)用程序。

            x11 - 應(yīng)用程序是一個(gè)X11應(yīng)用程序或庫(kù)。

            windows - 只用于“app”模板:應(yīng)用程序是一個(gè)Windows下的窗口應(yīng)用程序。

            console - 只用于“app”模板:應(yīng)用程序是一個(gè)Windows下的控制臺(tái)應(yīng)用程序。

            dll - 只用于“lib”模板:庫(kù)是一個(gè)共享庫(kù)(dll)。

            staticlib - 只用于“lib”模板:庫(kù)是一個(gè)靜態(tài)庫(kù)。

            plugin - 只用于“lib”模板:庫(kù)是一個(gè)插件,這將會(huì)使dll選項(xiàng)生效。

            例如,如果你的應(yīng)用程序使用Qt庫(kù),并且你想把它連編為一個(gè)可調(diào)試的多線(xiàn)程的應(yīng)用程序,你的項(xiàng)目文件應(yīng)該會(huì)有下面這行:

                CONFIG += qt thread debug注意,你必須使用“+=”,不要使用“=”,否則qmake就不能正確使用連編Qt的設(shè)置了,比如沒(méi)法獲得所編譯的Qt庫(kù)的類(lèi)型了。

             

            qmake高級(jí)概念
            操作符

            “=”操作符      分配一個(gè)值給一個(gè)變量
            “+=”操作符     向一個(gè)變量的值的列表中添加一個(gè)值
            “-=”操作符      從一個(gè)變量的值的列表中移去一個(gè)值
            “*=”操作符      僅僅在一個(gè)值不存在于一個(gè)變量的值的列表中的時(shí)候,把它添加進(jìn)去
            “~=”操作符      替換任何與指定的值的正則表達(dá)式匹配的任何值 DEFINES ~= s/QT_[DT].+/QT
            作用域
            win32:thread {
                    DEFINES += QT_THREAD_SUPPORT    } else:debug {        DEFINES += QT_NOTHREAD_DEBUG    } else {        warning("Unknown configuration")    }    }變量
            到目前為止我們遇到的變量都是系統(tǒng)變量,比如DEFINES、SOURCES和HEADERS。你也可以為你自己創(chuàng)建自己的變量,這樣你就可以在作用域中使用它們了。創(chuàng)建自己的變量很容易,只要命名它并且分配一些東西給它。比如:

                MY_VARIABLE = value你也可以通過(guò)在其它任何一個(gè)變量的變量名前加$$來(lái)把這個(gè)變量的值分配給當(dāng)前的變量。例如:

               MY_DEFINES = $$DEFINESMY_DEFINES = $${DEFINES}
            第二種方法允許你把一個(gè)變量和其它變量連接起來(lái),而不用使用空格。qmake將允許一個(gè)變量包含任何東西(包括$(VALUE),可以直接在makefile中直接放置,并且允許它適當(dāng)?shù)財(cái)U(kuò)張,通常是一個(gè)環(huán)境變量)。無(wú)論如何,如果你需要立即設(shè)置一個(gè)環(huán)境變量,然后你就可以使用$$()方法。比如:

                MY_DEFINES = $$(ENV_DEFINES)這將會(huì)設(shè)置MY_DEFINES為環(huán)境變量ENV_DEFINES傳遞給.pro文件地值。另外你可以在替換的變量里調(diào)用內(nèi)置函數(shù)。這些函數(shù)(不會(huì)和下一節(jié)中列舉的測(cè)試函數(shù)混淆)列出如下:

            join( variablename, glue, before, after )
            這將會(huì)在variablename的各個(gè)值中間加入glue。如果這個(gè)變量的值為非空,那么就會(huì)在值的前面加一個(gè)前綴before和一個(gè)后綴after。只有variablename是必須的字段,其它默認(rèn)情況下為空串。如果你需要在glue、before或者after中使用空格的話(huà),你必須提供它們。

            member( variablename, position )
            這將會(huì)放置variablename的列表中的position位置的值。如果variablename不夠長(zhǎng),這將會(huì)返回一個(gè)空串。variablename是唯一必須的字段,如果沒(méi)有指定位置,則默認(rèn)為列表中的第一個(gè)值。

            find( variablename, substr )
            這將會(huì)放置variablename中所有匹配substr的值。substr也可以是正則表達(dá)式,而因此將被匹配。

                MY_VAR = one two three four    MY_VAR2 = $$join(MY_VAR, " -L", -L) -Lfive    MY_VAR3 = $$member(MY_VAR, 2) $$find(MY_VAR, t.*)MY_VAR2將會(huì)包含“-Lone -Ltwo -Lthree -Lfour -Lfive”,并且MYVAR3將會(huì)包含“three two three”。

            system( program_and_args )
            這將會(huì)返回程序執(zhí)行在標(biāo)準(zhǔn)輸出/標(biāo)準(zhǔn)錯(cuò)誤輸出的內(nèi)容,并且正像平時(shí)所期待地分析它。比如你可以使用這個(gè)來(lái)詢(xún)問(wèn)有關(guān)平臺(tái)的信息。

                UNAME = $$system(uname -s)    contains( UNAME, [lL]inux ):message( This looks like Linux ($$UNAME) to me )測(cè)試函數(shù)
            qmake提供了可以簡(jiǎn)單執(zhí)行,但強(qiáng)大測(cè)試的內(nèi)置函數(shù)。這些測(cè)試也可以用在作用域中(就像上面一樣),在一些情況下,忽略它的測(cè)試值,它自己使用測(cè)試函數(shù)是很有用的。

            contains( variablename, value )
            如果value存在于一個(gè)被叫做variablename的變量的值的列表中,那么這個(gè)作用域中的設(shè)置將會(huì)被處理。例如:

                contains( CONFIG, thread ) {        DEFINES += QT_THREAD_SUPPORT    }如果thread存在于CONFIG變量的值的列表中時(shí),那么QT_THREAD_SUPPORT將會(huì)被加入到DEFINES變量的值的列表中。

            count( variablename, number )
            如果number與一個(gè)被叫做variablename的變量的值的數(shù)量一致,那么這個(gè)作用域中的設(shè)置將會(huì)被處理。例如:

                count( DEFINES, 5 ) {        CONFIG += debug    }error( string )
            這個(gè)函數(shù)輸出所給定的字符串,然后會(huì)使qmake退出。例如:

                error( "An error has occured" )文本“An error has occured”將會(huì)被顯示在控制臺(tái)上并且qmake將會(huì)退出。

            exists( filename )
            如果指定文件存在,那么這個(gè)作用域中的設(shè)置將會(huì)被處理。例如:

                exists( /local/qt/qmake/main.cpp ) {        SOURCES += main.cpp    }如果/local/qt/qmake/main.cpp存在,那么main.cpp將會(huì)被添加到源文件列表中。

            注意可以不用考慮平臺(tái)使用“/”作為目錄的分隔符。

            include( filename )
            項(xiàng)目文件在這一點(diǎn)時(shí)包含這個(gè)文件名的內(nèi)容,所以指定文件中的任何設(shè)置都將會(huì)被處理。例如:

                 include( myotherapp.pro )myotherapp.pro項(xiàng)目文件中的任何設(shè)置現(xiàn)在都會(huì)被處理。

            isEmpty( variablename )
            這和使用count( variablename, 0 )是一樣的。如果叫做variablename的變量沒(méi)有任何元素,那么這個(gè)作用域中的設(shè)置將會(huì)被處理。例如:

                isEmpty( CONFIG ) {        CONFIG += qt warn_on debug    }message( string )
            這個(gè)函數(shù)只是簡(jiǎn)單地在控制臺(tái)上輸出消息。

                message( "This is a message" )文本“This is a message”被輸出到控制臺(tái)上并且對(duì)于項(xiàng)目文件的處理將會(huì)繼續(xù)進(jìn)行。

            system( command )
            特定指令被執(zhí)行并且如果它返回一個(gè)1的退出值,那么這個(gè)作用域中的設(shè)置將會(huì)被處理。例如:

                system( ls /bin ) {        SOURCES += bin/main.cpp        HEADERS += bin/main.h    }所以如果命令ls /bin返回1,那么bin/main.cpp將被添加到源文件列表中并且bin/main.h將被添加到頭文件列表中。

            infile( filename, var, val )
            如果filename文件(當(dāng)它被qmake自己解析時(shí))包含一個(gè)值為val的變量var,那么這個(gè)函數(shù)將會(huì)返回成功。你也可以不傳遞第三個(gè)參數(shù)(val),這時(shí)函數(shù)將只測(cè)試文件中是否分配有這樣一個(gè)變量var。

            以下為我的一個(gè)項(xiàng)目舉例
            # 項(xiàng)目目標(biāo):為一個(gè)庫(kù)文件

            TEMPLATE = lib# 編譯項(xiàng)目文件所需頭文件的路徑INCLUDEPATH += ../common .# 目標(biāo)文件路徑DESTDIR=../lib# 條件依賴(lài):Unix平臺(tái)上 定義本想目的 UI目錄, MOC目錄, 目的目錄unix {  UI_DIR = ../.ui  MOC_DIR = ../.moc  OBJECTS_DIR = ../.obj}# 本項(xiàng)目配置:CONFIG         += qt warn_on release thread# Input  頭文件 ,源文件HEADERS += COMControllerThread.h \           DecodeSMS.h \           monitor_common.h \           monitor_interface.h \           MonitorThread.h \          UserEvent.h \           MyCOM.h \           MySMS.h \           MyTagHandle.h \          SMSParseThread.h \           tag_dict.hSOURCES += COMControllerThread.cpp \          DecodeSMS.cpp \           monitor_common.cpp \           monitor_interface.cpp \          MonitorThread.cpp \           MyCOM.cpp \           MySMS.cpp \           MyTagHandle.cpp \           SMSParseThread.cpp \           tag_dict.cpp注:qmake -project 可以生成pro文件(可以根據(jù)項(xiàng)目需要,編輯改文件)
            qmake 可以生成Makefile文件
            make 編譯
            使用qmake -project時(shí),會(huì)把本目錄及其子目錄內(nèi)所有.cpp .h文件加入到項(xiàng)目輸入文件中,使用是注意移去其他無(wú)用的文件。
            qmake生成的Makefile文件,可以根據(jù)需要做相應(yīng)修改

            posted @ 2011-04-19 22:46 RTY 閱讀(614) | 評(píng)論 (0)編輯 收藏

            \a  (parameter marker) 參數(shù)標(biāo)記
            \abstract (The \abstract and \endabstract commands delimit a document's abstract section.)
            \badcode (The \badcode and \endcode commands delimit a snippet of code that doesn't compile or is wrong for some other reason.)
            \bold (The \bold command renders its argument in bold font.)
            \brief  (The \brief command introduces a one-sentence description of a class, namespace, header file, property or variable.)
            \c (The \c command is used for rendering variable names, user-defined class names, and C++ keywords (e.g. int and for) in the code font.()
            \caption  (The \caption command provides a caption for an image.)
            \chapter
            \class
            \code
            \codeline,
            \compat
            \contentspage
            \default (new)
            \div (new)
            \dots
            \else
            \endif
            \enum
            \example
            \expire
            \externalpage
            \fn
            \footnote
            \generatelist
            \group
            \header
            \headerfile
            \i
            \if
            \image
            \include
            \indexpage
            \ingroup
            \inherits (new)
            \inmodule
            \inlineimage
            \internal
            \keyword
            \l
            \legalese
            \list
            \macro
            \mainclass
            \meta
            \module
            \namespace
            \nextpage
            \newcode
            \nonreentrant
            \o
            \obsolete
            \oldcode
            \omit
            \omitvalue
            \overload
            \page
            \part
            \preliminary
            \previouspage
            \printline
            \printto
            \printuntil
            \property
            \qmlattachedproperty (new)
            \qmlattachedsignal (new)
            \qmlbasictype (new)
            \qmlclass (new)
            \qmlmethod (new)
            \qmlproperty (new)
            \qmlsignal (new)
            \quotation
            \quotefile
            \quotefromfile
            \raw (avoid)
            \reentrant
            \reimp
            \relates
            \row
            \sa
            \section1
            \section2
            \section3
            \section4
            \service
            \since
            \skipline
            \skipto
            \skipuntil
            \snippet,
            \span (new)
            \startpage
            \sub
            \subtitle
            \sup
            \table
            \tableofcontents
            \target
            \threadsafe
            \title
            \tt
            \typedef
            \underline
            \variable
            \value
            \warning

            posted @ 2011-04-19 21:18 RTY 閱讀(331) | 評(píng)論 (0)編輯 收藏

                 摘要:     CSS3 Buttons – 10+ Awesome Ready-To-Use Solutions (+All Related Tutorials You Need) 14APR   For years, it was im...  閱讀全文

            posted @ 2011-04-18 21:30 RTY 閱讀(549) | 評(píng)論 (0)編輯 收藏

            原文:http://www.cnblogs.com/codingmylife/archive/2010/05/08/1730832.html

                  找到qt安裝目錄下的mkspecs文件夾,在里面找到你使用的對(duì)應(yīng)版本編譯器,打開(kāi)qmake.conf。稍等:

            /MD:動(dòng)態(tài)鏈接多線(xiàn)程庫(kù)(msvcrt.lib)。使用該選項(xiàng)時(shí),需要用/NODEFAULTLIB選項(xiàng)來(lái)忽略掉libc.lib、 libcmt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib庫(kù),否則會(huì)有鏈接錯(cuò)誤;

            /MDd:動(dòng)態(tài)鏈接多線(xiàn)程調(diào)試庫(kù)(msvcrtd.lib)。使用該選項(xiàng)時(shí),需要用/NODEFAULTLIB選項(xiàng)來(lái)忽略掉libc.lib、 libcmt.lib、msvcrt.lib、libcd.lib、libcmtd.lib庫(kù),否則會(huì)有鏈接錯(cuò)誤;

            /MT:靜態(tài)鏈接多線(xiàn)程庫(kù)(libcmt.lib)。使用該選項(xiàng)時(shí),需要用/NODEFAULTLIB選項(xiàng)來(lái)忽略掉libc.lib、 msvcrt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib庫(kù),否則會(huì)有鏈接錯(cuò)誤;

            /MTd:靜態(tài)鏈接多線(xiàn)程調(diào)試庫(kù)(libcmtd.lib)。使用該選項(xiàng)時(shí),需要用/NODEFAULTLIB選項(xiàng)來(lái)忽略掉libc.lib、 libcmt.lib、msvcrt.lib、libcd.lib、msvcrtd.lib庫(kù),否則會(huì)有鏈接錯(cuò)誤。

                  左邊的是使用的鏈接方式,在qmake.conf中可以找到:

            QMAKE_CFLAGS_RELEASE    = -O2 -MD
            QMAKE_CFLAGS_DEBUG      = -Zi -MDd

            其中說(shuō)明release下使用/MD參數(shù),debug下使用/MDd參數(shù),該如何忽略呢?

            以我使用/MDd參數(shù)時(shí)的debug為例,添加部分為紅色:

            QMAKE_LFLAGS_DEBUG      = /DEBUG /NODEFAULTLIB:libc.lib /NODEFAULTLIB:libcmt.lib

            /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:libcd.lib /NODEFAULTLIB:libcmtd.lib

            posted @ 2011-04-15 23:05 RTY 閱讀(2728) | 評(píng)論 (0)編輯 收藏

            • 指定臨時(shí)文件生成目錄,使項(xiàng)目文件夾更干凈

                  QT默認(rèn)情況下把所有的編譯中間文件都生成到debug和release文件夾里。可以在.pro文件中加入:

            MOC_DIR = tmp/moc

            RCC_DIR = tmp/rcc

            UI_DIR = tmp/ui

            OBJECTS_DIR = tmp/obj

            這樣,編譯時(shí)生成的臨時(shí)文件就按不同類(lèi)型分類(lèi)放到項(xiàng)目下的tmp文件夾中了。

            posted @ 2011-04-15 23:03 RTY 閱讀(2709) | 評(píng)論 (0)編輯 收藏

            僅列出標(biāo)題
            共31頁(yè): First 23 24 25 26 27 28 29 30 31 
            精品久久久久久国产| 国产成人香蕉久久久久| 欧美熟妇另类久久久久久不卡| 久久精品国产精品亚洲毛片| 久久99精品综合国产首页| 女同久久| 久久香蕉国产线看观看99| 久久久久亚洲精品日久生情 | 久久久久se色偷偷亚洲精品av| 久久精品国产亚洲AV高清热| 久久国产欧美日韩精品| 777久久精品一区二区三区无码| 亚洲国产综合久久天堂 | 国产精品美女久久久久| 日本加勒比久久精品| 国产精品久久久久影院嫩草| 少妇无套内谢久久久久| 久久久久久噜噜精品免费直播| 99久久免费国产精品热| 一本色道久久综合亚洲精品| 亚洲欧美一级久久精品| 久久国产精品国语对白| 亚洲成色999久久网站| 精品综合久久久久久97超人| 欧美va久久久噜噜噜久久| 99久久精品国产一区二区 | 精品蜜臀久久久久99网站| 久久精品aⅴ无码中文字字幕不卡| 久久午夜无码鲁丝片午夜精品| 久久久久久久99精品免费观看| 97久久香蕉国产线看观看| 麻豆一区二区99久久久久| 色狠狠久久AV五月综合| 少妇精品久久久一区二区三区| 日韩欧美亚洲综合久久 | 久久永久免费人妻精品下载| 久久午夜无码鲁丝片秋霞| 久久久久国产精品嫩草影院| 日韩精品久久久久久久电影蜜臀| 亚洲精品乱码久久久久久自慰| 亚洲精品白浆高清久久久久久|