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

            Where there is a dream ,there is hope

              C++博客 :: 首頁 :: 聯系 :: 聚合  :: 管理
              64 Posts :: 0 Stories :: 8 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(1)

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            #

            原文地址:http://hi.baidu.com/freedomknightduzhi/blog/item/a0504560d1277555ebf8f8ff.html

            1:神馬是Dll和Lib,神馬是靜態鏈接和動態鏈接

            大家都懂的,DLL就是動態鏈接庫,LIB是靜態鏈接庫。DLL其實就是EXE,只不過沒main。

            動態鏈接是相對于靜態鏈接而言的。所謂靜態鏈接就是把函數或過程直接鏈接到可執行文件中,成為可執行程序中的一部分,當多個程序調用同樣的函數時,內存里就會有這個函數的多個拷貝,浪費內存資源。而動態鏈接則是提供了一個函數的描述信息給可執行文件(并沒有內存拷貝),當程序被夾在到內存里開始運行的時候,系統會在底層創建DLL和應用程序之間的連接關系,當執行期間需要調用DLL函數時,系統才會真正根據鏈接的定位信息去執行DLL中的函數代碼。

            在WINDOWS32系統底下,每個進程有自己的32位的線性地址空間,若一個DLL被進程使用,則該DLL首先會被調入WIN32系統的全局堆棧,然后通過內存映射文件方式映射到這個DLL的進程地址空間。若一個DLL被多個進程調用,則每個進程都會接收到該DLL的一個映像,而非多份的拷貝。但,在WIN16系統下,每個進程需要擁有自己的一份DLL空間,可以理解為何靜態鏈接沒啥區別。

             

            2:DLL和LIB區別和聯系。

            DLL是程序在運行階段才需要的文件。

            LIB是程序編譯時需要鏈接的文件。

            DLL只有一種,其中一定是函數和過程的實現。

            LIB是有兩種。若只生成LIB的話,則這個LIB是靜態編譯出來的,它內部包含了函數索引以及實現,這個LIB會比較大。若生成DLL的話,則也會生成一個LIB,這個LIB和剛才那個LIB不同,它是只有函數索引,沒有實現的,它很小。但是這倆LIB依然遵循上個原則,是在編譯時候是需要被鏈接的。若不鏈接第一個LIB的話,在程序運行時會無法找到函數實現,當掉。若不鏈接第二個LIB的話,在程序運行時依然會無法找到函數實現。但第二種LIB有一種替代方式,就是在程序里,使用LoadLibrary,GetProcAddress替代第二個LIB的功能。第一種LIB生成的EXE文件會很大,因為LIB所有信息被靜態鏈接進EXE里了。第二種LIB生成的EXE文件會比較小,因為函數過程實現依舊在DLL內。

            (啰嗦了一堆,某志希望大家能夠明白兩個LIB的區別。要再不行的話,我們可以將靜態編譯的LIB稱為 靜態鏈接庫。但動態編譯的LIB稱為 引入庫。可能會比較好一些。)

            靜態鏈接LIB的優點是免除掛接動態鏈接庫,缺點是EXE大,版本控制麻煩些。

            動態鏈接DLL的優點是文件小,版本更換時換DLL就好了,缺點是多了點文件。動態鏈接若是被多個進程使用,會更加方便和節省內存。

             

            3:為什么編譯DLL時總會同時生成一個LIB?這個LIB有用嗎?

            若我們不是用靜態鏈接,而使用DLL,那么我們也需要一個LIB,這個LIB的作用是被鏈接到程序里,在程序運行時告訴系統你需要什么DLL文件。這個LIB里保存的是DLL的名字和輸出函數入口的順序表。它是有意義的。

            當然,若我們的應用程序里不鏈接這個LIB,則可以使用LoadLibrary,GetProcAddress來告訴系統我們在運行時需要怎么著DLL以及其內的函數。

             

            4:DLL意義。

            1:DLL真正實現了跨語言。各種語言都可以生成DLL,而對系統以及應用程序來說,哪種語言生成的DLL是沒有區別的。

            2:DLL有足夠的封裝性,對于版本更新有很大好處。因為DLL是運行期間才會使用,所以,即使DLL內函數實現有變化(只要參數和返回值不發生變化),程序是不需要進行編譯的。大大提高了軟件開發和維護的效率。

            3:DLL被多個進程使用,因為有內存映射機制,無需占用更多內存。

             

            5:創建DLL。(注意:某志就不再講解使用MFC AppWizard[dll] 方式創建DLL了。有興趣的自己去百度。這里創建DLL只指使用Win32 Dynamic-link Library創建Non-MFC DLL。呃,DLL的三種類型就不解釋了,依舊那句話:百度一下你就知道。)

            每個應用程序必須有一個main或者winmain函數作為入口,DLL一樣,有自己的缺省的入口函數,就是DllMain。函數如下

            BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
            {
             switch (ul_reason_for_call)
             {
             case DLL_PROCESS_ATTACH:   // 進程被調用
             case DLL_THREAD_ATTACH:     // 線程被調用
             case DLL_THREAD_DETACH:   // 線程被停止
             case DLL_PROCESS_DETACH:  // 進程被停止
              break;
             }
             return TRUE;
            }

            一般情況下,我們不需要對這個缺省的入口函數進行什么修改,它就會使動態鏈接庫得到正確的初始化。但是,當我們的DLL需要額外分配內存或者資源的時候,或者,DLL希望對調用自己的進程或線程進行初始化或清除的額外操作時,可以在上述代碼case中加一些自己感冒的東東。(懶……不想細寫了- -Orz,現在是晚上2點了,明天還一堆的事情)

            DLL對于導出類和導出函數沒啥不同。只要加上 __declspec( dllexport ) 修飾函數或者類就好了。

            但是有查看過DLL代碼的人員都會經常見到這么一段代碼

            #ifdef FK_DLL_EXPORTS

            #define FK_DLL __declspec( dllexport )

            #else

            #define FK_DLL __declspec( dllimport )

            #endif

            意義很明顯,但是,問題是  FK_DLL_EXPORTS 這個宏是應該在哪兒定義呢?在DLL項目內,還是在使用DLL的應用程序內?

            這點某志曾迷糊很久,呵呵~其實后來想想,還是蠻明確的。export是導出。import是導入。對于DLL來說,是要導出這些函數給其他應用程序使用的,所以應當定義 FK_DLL_EXPORTS 宏。對于使用DLL的應用程序來說,是導入,是無需定義的。

            使用時候也很簡單。

            class FK_DLL CMyDllClass{} ;

            則整個類被導出。

            FK_DLL void MyTestFun( int a );

            則該函數被導出。

            但是有時我們可以見到這樣的代碼

            extern "C" FK_DLL void MyTestFun2( float b );

            其中extern "C"的原理就是標示該函數要求以C形式去進行編譯,不要以C++形式去編譯。具體的編譯原理就不羅嗦了,簡而言之,被extern "C"定義函數,可以被C以及其他語言進行DLL調用,而未被extern "C"定義的函數,C是無法訪問DLL中這個函數的。

             

            在VS中開發DLL還有一種方式,使用.def文件。

            新建個文本文檔,改后綴為FKDll.def,加入到工程里。

            FKDll.def里加入以下代碼

            LIBRARY FKDll

            EXPORTS

            MyTestFun@1

            MyTestFun2@2

            就可以了。其中,LIBRARY語句是說明.def文件是屬于FKDll這個Dll的。EXPORTS下面是我們需要導出的函數名。后面加的@+數字,是表示導出函數的順序編號。這樣就足夠了。(詳細的自己百度,好困,zzzZZZ)

             

            6:使用DLL

            使用DLL有兩種方式。顯式鏈接和隱式鏈接。

            隱式鏈接很容易。直接#progam comment(lib, "FKDll.lib") 就可以。當然,也可以在項目工程->屬性->鏈接庫里加上庫和路徑(相對路徑和絕對路徑都可以)。

            顯式鏈接則麻煩些。在程序中使用LoadLibrary加載DLL,再GetProcAddress獲取函數實現,在程序退出之前,調用FreeLibrary來動態釋放掉鏈接庫。

            ‍例如:

            void Main()

            {

                 typedef void (*FKDllFun1)(int a);

                FKDllFun1 pFun1;

                HINSTANCE hDLL  = LoadLibrary("FKDll.dll");   // 若hDll為空則讀取Dll失敗。

                pFun1 = (pFun1)GetProcAddress(hDll, "MyTestFun1" );   // 從應用程序中的DLL鏡像中獲取名為 MyTestFun1 的函數指針

                pFun1( 100 );

                FreeLibrary(hDll);

            }

            當然,我們剛才.def里面還指定了導出函數的導出順序,那么我們可以修改里面獲取函數指針那一段為

            ‍pFun1 = (pFun1)GetProcAddress(hDll, MAKEINTERSOURCE(1) );  // 1 是剛才指定的MyTestFun1函數導出順序編號。

            這樣可以更快,但是別將編號記混了,會導致詭異的錯誤。

             

            7:比較顯式鏈接和隱式鏈接。

            可能的話,盡量使用顯式鏈接。

            顯式鏈接可以在程序執行時動態的加載DLL和卸載DLL文件,隱式鏈接是做不到的。

            顯式鏈接LoadLibrary,GetProcAddress時能獲知是否加載失敗,我們可以對其進行檢查錯誤處理。而顯式鏈接可能是一個很惡劣的提示或是程序崩潰的結果。

            對于有些Ex類型的加強函數,顯式鏈接可以允許我們找到替代方案。也包括選擇D3d9.dll和OpenGL.dll時也可采用同樣處理。

            例如:

            if( GetProcAddress( hDll, "FKDllFunEx") == NULL )

            {

            ‍    pFun = GetProcAddress( hDll, "FKDllFun");    // 然后使用pFun進行處理

            }

             

            8:導出類和導出函數

            類和函數的導出方式上面給出了說明,原本極其類似的。

            我們說下使用導出類。

            若我們隱式的使用了一個導出類,則我們在應用程序里繼承它的時候,就如同該類就在應用程序代碼里一樣,無需任何處理。

            例如:

            class FK_DLL CMyDllClass{} ;    // Dll文件內的代碼

            -----------------------

            class CAppClass : public CMyDllClass      // 應用程序內代碼,無需做任何處理。

            {

                   ....

            }

            也可以直接使用DLL導出類

            void main

            {

                 CMyDllClass* pClass = new CMyDllClass ();

            }

            但是,若應用程序聲明或者分類一個DLL中導出類的對象時會存在一個很討厭的問題:這個操作會使內存跟蹤系統失效,使其錯誤的報告內存分配和釋放情況。

            為解決這個問題,我們可以給出兩個接口函數對DLL導出類進行創建銷毀支持,就可以使內存跟蹤系統正常了。例如

            class FK_DLL CMyDllClass{} ; 

            額外增加倆函數

            FK_DLL CMyDllClass* CreateMyDllClass(){ return new CMyDllClass(); }

            FK_DLL void DestoryMyDllClass( CMyDllClass* p_pClass ){ delete p_pClass; }

            -----------------------------------------------

            上面的方法可以正確進行內存跟蹤了,但是,因為DLL導出類CMyDllClass依舊是導出的狀態,用戶同樣可以跳過我們提供的接口直接使用。那么怎么辦呢。方法是不再對類進行DLL導出,而對類內的函數全部進行DLL導出即可,

            -----------------------------------------------

            但是若僅僅提供上面兩個接口函數以及類內全部函數,的確功能可以實現,卻無法進行類繼承了。若這個類繼承很重要,必須開放,那么就需要使用新的內存跟蹤程序替換應用程序內的原有內存跟蹤程序。或者使用下面的一個方法。(見模塊9:復雜問題)

            -----------------------------------------------

            同樣,我們也可以發現,在不導出DLL類本身,而只導出DLL類內函數也有一些好處,一些我們不希望外界知道的函數可以不設置導出標記,這進一步保護了DLL內函數的安全性。

             

            9:復雜問題。

            若我們使用LoadLibrary顯式加載一個DLL,并嘗試在應用程序中調用一個類內成員函數的話,無論該函數是否在頭文件中有聲明,VS會給出一個"unresolved external symbol(未解析的外部符號)"的錯誤。我們此時可以將項目屬性中的內聯函數擴展選項修改為"Only __inline"或"Any Suitable"即可。但,我們可能在調試連編的時候期望關閉內聯函數擴展,那么另一種解決方案是,將希望導出的函數聲明為虛函數,例如

            class CMyDllClass

            {

               FK_DLL virtual void MyTestFun( int a ){  dosth(); }  

               // 用上面代碼替換 FK_DLL void MyTestFun( int a ){  dosth(); }  

            }

            這樣做還有一個額外的好處。將導出的類成員函數設置為虛函數之后,該虛函數所在的類在應用程序中也如同被聲明一樣,可以接受繼承。

            例如若是上面的做法,應用程序就可以進行順利繼承,而不必要求CMyDllClass 被標示為導出。(原理不知,希望精通底層的高手協助解釋。)

            class CAppClass : public CMyDllClass      // 應用程序內代碼,無需做任何處理。

            {

                   ....

            }


            posted @ 2011-01-17 11:55 IT菜鳥 閱讀(571) | 評論 (0)編輯 收藏

             

             見到這兩個符號的很多不同的用法,整理在一起

            宏中"#""##"的用法

            一、一般用法

            我們使用#把宏參數變為一個字符串,##把兩個宏參數貼合在一起.

            用法:

            #include<cstdio>

            #include<climits>

            using namespace std;

            #define STR(s)   #s

            #define CONS(a,b) int(a##e##b)

            int main()

            {

             printf(STR(vck));       // 輸出字符串"vck"

             printf("%d\n", CONS(2,3)); // 2e3 輸出:2000

             return 0;

            }

             

            二、當宏參數是另一個宏的時候

            需要注意的是凡宏定義里有用'#''##'的地方宏參數是不會再展開.

            1, '#''##'的情況

            #define TOW     (2)

            #define MUL(a,b) (a*b)

            printf("%d*%d=%d\n", TOW, TOW, MUL(TOW,TOW));

            這行的宏會被展開為:

            printf("%d*%d=%d\n", (2), (2), ((2)*(2)));

            MUL里的參數TOW會被展開為(2).

            2, 當有'#''##'的時候

            #define A       (2)

            #define STR(s)   #s

            #define CONS(a,b) int(a##e##b)

            printf("int max: %s\n", STR(INT_MAX));   // INT_MAX #include<climits>

            這行會被展開為:

            printf("int max: %s\n", "INT_MAX");

            printf("%s\n", CONS(A, A));           // compile error

            這一行則是:

            printf("%s\n", int(AeA));

            A不會再被展開, 然而解決這個問題的方法很簡單. 加多一層中間轉換宏.

            加這層宏的用意是把所有宏的參數在這層里全部展開, 那么在轉換宏里的那一個宏(_STR)就能得到正確的宏參數.

            #define A       (2)

            #define _STR(s)   #s

            #define STR(s)     _STR(s)       // 轉換宏

            #define _CONS(a,b) int(a##e##b)

            #define CONS(a,b)   _CONS(a,b)     // 轉換宏

            printf("int max: %s\n", STR(INT_MAX));       // INT_MAX,int型的最大值,為一個變量 #include<climits>

            輸出為: int max: 0x7fffffff

            STR(INT_MAX) --> _STR(0x7fffffff) 然后再轉換成字符串;

            printf("%d\n", CONS(A, A));

            輸出為:200

            CONS(A, A) --> _CONS((2), (2)) --> int((2)e(2))

            三、'#''##'的一些應用特例

            1、合并匿名變量名

            #define ___ANONYMOUS1(type, var, line) type var##line

            #define __ANONYMOUS0(type, line) ___ANONYMOUS1(type, _anonymous, line)

            #define ANONYMOUS(type) __ANONYMOUS0(type, __LINE__)

            例:ANONYMOUS(static int); : static int _anonymous70; 70表示該行行號;

            第一層:ANONYMOUS(static int); --> __ANONYMOUS0(static int, __LINE__);

            第二層:                 --> ___ANONYMOUS1(static int, _anonymous, 70);

            第三層:                 --> static int _anonymous70;

            即每次只能解開當前層的宏,所以__LINE__在第二層才能被解開;

            2、填充結構

            #define FILL(a)   {a, #a}

            enum IDD{OPEN, CLOSE};

            typedef struct MSG{

            IDD id;

            const char * msg;

            }MSG;

            MSG _msg[] = {FILL(OPEN), FILL(CLOSE)};

            相當于:

            MSG _msg[] = {{OPEN, "OPEN"},

                    {CLOSE, "CLOSE"}};

            3、記錄文件名

            #define _GET_FILE_NAME(f)   #f

            #define GET_FILE_NAME(f)   _GET_FILE_NAME(f)

            static char FILE_NAME[] = GET_FILE_NAME(__FILE__);

            4、得到一個數值類型所對應的字符串緩沖大小

            #define _TYPE_BUF_SIZE(type) sizeof #type

            #define TYPE_BUF_SIZE(type)   _TYPE_BUF_SIZE(type)

            char buf[TYPE_BUF_SIZE(INT_MAX)];

             --> char buf[_TYPE_BUF_SIZE(0x7fffffff)];

             --> char buf[sizeof "0x7fffffff"];

            這里相當于:

            char buf[11];

            posted @ 2010-12-17 14:34 IT菜鳥 閱讀(393) | 評論 (0)編輯 收藏

            原文地址:http://zhedahht.blog.163.com/blog/static/254111742007127104759245/
            題目:輸入一棵二元查找樹,將該二元查找樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只調整指針的指向。

              比如將二元查找樹
                
                                                    10
                                                      /    \
                                                    6       14
                                                  /  \     /  \
                                                4     8  12    16
            轉換成雙向鏈表

            4=6=8=10=12=14=16

              分析:本題是微軟的面試題。很多與樹相關的題目都是用遞歸的思路來解決,本題也不例外。下面我們用兩種不同的遞歸思路來分析。

              思路一:當我們到達某一結點準備調整以該結點為根結點的子樹時,先調整其左子樹將左子樹轉換成一個排好序的左子鏈表,再調整其右子樹轉換右子鏈表。最近鏈接左子鏈表的最右結點(左子樹的最大結點)、當前結點和右子鏈表的最左結點(右子樹的最小結點)。從樹的根結點開始遞歸調整所有結點。

              思路二:我們可以中序遍歷整棵樹。按照這個方式遍歷樹,比較小的結點先訪問。如果我們每訪問一個結點,假設之前訪問過的結點已經調整成一個排序雙向鏈表,我們再把調整當前結點的指針將其鏈接到鏈表的末尾。當所有結點都訪問過之后,整棵樹也就轉換成一個排序雙向鏈表了。

            參考代碼:

            首先我們定義二元查找樹結點的數據結構如下:
                struct BSTreeNode // a node in the binary search tree
                {
                    int          m_nValue; // value of node
                    BSTreeNode  *m_pLeft;  // left child of node
                    BSTreeNode  *m_pRight; // right child of node
                };

            思路一對應的代碼:
            ///////////////////////////////////////////////////////////////////////
            // Covert a sub binary-search-tree into a sorted double-linked list
            // Input: pNode - the head of the sub tree
            //        asRight - whether pNode is the right child of its parent
            // Output: if asRight is true, return the least node in the sub-tree
            //         else return the greatest node in the sub-tree
            ///////////////////////////////////////////////////////////////////////
            BSTreeNode* ConvertNode(BSTreeNode* pNode, bool asRight)
            {
                  if(!pNode)
                        return NULL;

                  BSTreeNode *pLeft = NULL;
                  BSTreeNode *pRight = NULL;

                  // Convert the left sub-tree
                  if(pNode->m_pLeft)
                        pLeft = ConvertNode(pNode->m_pLeft, false);

                  // Connect the greatest node in the left sub-tree to the current node
                  if(pLeft)
                  {
                        pLeft->m_pRight = pNode;
                        pNode->m_pLeft = pLeft;
                  }

                  // Convert the right sub-tree
                  if(pNode->m_pRight)
                        pRight = ConvertNode(pNode->m_pRight, true);

                  // Connect the least node in the right sub-tree to the current node
                  if(pRight)
                  {
                        pNode->m_pRight = pRight;
                        pRight->m_pLeft = pNode;
                  }

                  BSTreeNode *pTemp = pNode;

                  // If the current node is the right child of its parent, 
                  // return the least node in the tree whose root is the current node
                  if(asRight)
                  {
                        while(pTemp->m_pLeft)
                              pTemp = pTemp->m_pLeft;
                  }
                  // If the current node is the left child of its parent, 
                  // return the greatest node in the tree whose root is the current node
                  else
                  {
                        while(pTemp->m_pRight)
                              pTemp = pTemp->m_pRight;
                  }
             
                  return pTemp;
            }

            ///////////////////////////////////////////////////////////////////////
            // Covert a binary search tree into a sorted double-linked list
            // Input: the head of tree
            // Output: the head of sorted double-linked list
            ///////////////////////////////////////////////////////////////////////
            BSTreeNode* Convert(BSTreeNode* pHeadOfTree)
            {
                  // As we want to return the head of the sorted double-linked list,
                  // we set the second parameter to be true
                  return ConvertNode(pHeadOfTree, true);
            }

            思路二對應的代碼:
            ///////////////////////////////////////////////////////////////////////
            // Covert a sub binary-search-tree into a sorted double-linked list
            // Input: pNode -           the head of the sub tree
            //        pLastNodeInList - the tail of the double-linked list
            ///////////////////////////////////////////////////////////////////////
            void ConvertNode(BSTreeNode* pNode, BSTreeNode*& pLastNodeInList)
            {
                  if(pNode == NULL)
                        return;

                  BSTreeNode *pCurrent = pNode;

                  // Convert the left sub-tree
                  if (pCurrent->m_pLeft != NULL)
                        ConvertNode(pCurrent->m_pLeft, pLastNodeInList);

                  // Put the current node into the double-linked list
                  pCurrent->m_pLeft = pLastNodeInList; 
                  if(pLastNodeInList != NULL)
                        pLastNodeInList->m_pRight = pCurrent;

                  pLastNodeInList = pCurrent;

                  // Convert the right sub-tree
                  if (pCurrent->m_pRight != NULL)
                        ConvertNode(pCurrent->m_pRight, pLastNodeInList);
            }

            ///////////////////////////////////////////////////////////////////////
            // Covert a binary search tree into a sorted double-linked list
            // Input: pHeadOfTree - the head of tree
            // Output: the head of sorted double-linked list
            ///////////////////////////////////////////////////////////////////////
            BSTreeNode* Convert_Solution1(BSTreeNode* pHeadOfTree)
            {
                  BSTreeNode *pLastNodeInList = NULL;
                  ConvertNode(pHeadOfTree, pLastNodeInList);

                  // Get the head of the double-linked list
                  BSTreeNode *pHeadOfList = pLastNodeInList;
                  while(pHeadOfList && pHeadOfList->m_pLeft)
                        pHeadOfList = pHeadOfList->m_pLeft;

                  return pHeadOfList;
            }

            posted @ 2010-12-17 08:58 IT菜鳥 閱讀(333) | 評論 (0)編輯 收藏

            原文地址:
            http://blog.csdn.net/fly2k5/archive/2005/12/05/544112.aspx
            在C語言中,假設我們有這樣的一個函數:
              
              int function(int a,int b)
              
              調用時只要用result = function(1,2)這樣的方式就可以使用這個函數。但是,當高級語言被編譯成計算機可以識別的機器碼時,有一個問題就凸現出來:在CPU中,計算機沒有辦法知道一個函數調用需要多少個、什么樣的參數,也沒有硬件可以保存這些參數。也就是說,計算機不知道怎么給這個函數傳遞參數,傳遞參數的工作必須由函數調用者和函數本身來協調。為此,計算機提供了一種被稱為棧的數據結構來支持參數傳遞。

              棧是一種先進后出的數據結構,棧有一個存儲區、一個棧頂指針。棧頂指針指向堆棧中第一個可用的數據項(被稱為棧頂)。用戶可以在棧頂上方向棧中加入數據,這個操作被稱為壓棧(Push),壓棧以后,棧頂自動變成新加入數據項的位置,棧頂指針也隨之修改。用戶也可以從堆棧中取走棧頂,稱為彈出棧(pop),彈出棧后,棧頂下的一個元素變成棧頂,棧頂指針隨之修改。

              函數調用時,調用者依次把參數壓棧,然后調用函數,函數被調用以后,在堆棧中取得數據,并進行計算。函數計算結束以后,或者調用者、或者函數本身修改堆棧,使堆棧恢復原裝。

              在參數傳遞中,有兩個很重要的問題必須得到明確說明:
              
              當參數個數多于一個時,按照什么順序把參數壓入堆棧 
              函數調用后,由誰來把堆棧恢復原裝 
              在高級語言中,通過函數調用約定來說明這兩個問題。常見的調用約定有:

              stdcall 
              cdecl 
              fastcall 
              thiscall 
              naked call

              stdcall調用約定
              stdcall很多時候被稱為pascal調用約定,因為pascal是早期很常見的一種教學用計算機程序設計語言,其語法嚴謹,使用的函數調用約定就是stdcall。在Microsoft C++系列的C/C++編譯器中,常常用PASCAL宏來聲明這個調用約定,類似的宏還有WINAPI和CALLBACK。

              stdcall調用約定聲明的語法為(以前文的那個函數為例):
              
              int __stdcall function(int a,int b)
              
              stdcall的調用約定意味著:1)參數從右向左壓入堆棧,2)函數自身修改堆棧 3)函數名自動加前導的下劃線,后面緊跟一個@符號,其后緊跟著參數的尺寸

              以上述這個函數為例,參數b首先被壓棧,然后是參數a,函數調用function(1,2)調用處翻譯成匯編語言將變成:

              push 2        第二個參數入棧
              push 1        第一個參數入棧
              call function    調用參數,注意此時自動把cs:eip入棧

              而對于函數自身,則可以翻譯為: 
              push ebp       保存ebp寄存器,該寄存器將用來保存堆棧的棧頂指針,可以在函數退出時恢復
              mov ebp, esp    保存堆棧指針
              mov eax,[ebp + 8H] 堆棧中ebp指向位置之前依次保存有ebp, cs:eip, a, b, ebp +8指向a
              add eax,[ebp + 0CH] 堆棧中ebp + 12處保存了b
              mov esp, ebp    恢復esp
              pop ebp
              ret 8

              而在編譯時,這個函數的名字被翻譯成_function@8

              注意不同編譯器會插入自己的匯編代碼以提供編譯的通用性,但是大體代碼如此。其中在函數開始處保留esp到ebp中,在函數結束恢復是編譯器常用的方法。

              從函數調用看,2和1依次被push進堆棧,而在函數中又通過相對于ebp(即剛進函數時的堆棧指針)的偏移量存取參數。函數結束后,ret 8表示清理8個字節的堆棧,函數自己恢復了堆棧。

              
              cdecl調用約定
              cdecl調用約定又稱為C調用約定,是C語言缺省的調用約定,它的定義語法是:

              int function (int a ,int b) //不加修飾就是C調用約定
              int __cdecl function(int a,int b)//明確指出C調用約定

              在寫本文時,出乎我的意料,發現cdecl調用約定的參數壓棧順序是和stdcall是一樣的,參數首先由右向左壓入堆棧。所不同的是,函數本身不清理堆棧,調用者負責清理堆棧。由于這種變化,C調用約定允許函數的參數的個數是不固定的,這也是C語言的一大特色。對于前面的function函數,使用cdecl后的匯編碼變成:

              調用處
              push 1
              push 2
              call function
              add esp, 8     注意:這里調用者在恢復堆棧

              被調用函數_function處
              push ebp       保存ebp寄存器,該寄存器將用來保存堆棧的棧頂指針,可以在函數退出時恢復
              mov ebp,esp     保存堆棧指針
              mov eax,[ebp + 8H] 堆棧中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a
              add eax,[ebp + 0CH] 堆棧中ebp + 12處保存了b
              mov esp,ebp     恢復esp
              pop ebp
              ret         注意,這里沒有修改堆棧

              MSDN中說,該修飾自動在函數名前加前導的下劃線,因此函數名在符號表中被記錄為_function,但是我在編譯時似乎沒有看到這種變化。

              由于參數按照從右向左順序壓棧,因此最開始的參數在最接近棧頂的位置,因此當采用不定個數參數時,第一個參數在棧中的位置肯定能知道,只要不定的參數個數能夠根據第一個后者后續的明確的參數確定下來,就可以使用不定參數,例如對于CRT中的sprintf函數,定義為:
              int sprintf(char* buffer,const char* format,...)
              由于所有的不定參數都可以通過format確定,因此使用不定個數的參數是沒有問題的。

              fastcall
              fastcall調用約定和stdcall類似,它意味著: 
              
              函數的第一個和第二個DWORD參數(或者尺寸更小的)通過ecx和edx傳遞,其他參數通過從右向左的順序壓棧 
              被調用函數清理堆棧 
              函數名修改規則同stdcall 
              其聲明語法為:int fastcall function(int a, int b)

              thiscall
              thiscall是唯一一個不能明確指明的函數修飾,因為thiscall不是關鍵字。它是C++類成員函數缺省的調用約定。由于成員函數調用還有一個this指針,因此必須特殊處理,thiscall意味著:

              參數從右向左入棧 
              如果參數個數確定,this指針通過ecx傳遞給被調用者;如果參數個數不確定,this指針在所有參數壓棧后被壓入堆棧。對參數個數不定的,調用者清理堆棧,否則函數自己清理堆棧為了說明這個調用約定,定義如下類和使用代碼:

              class A
              {
              public:
                int function1(int a,int b);
                int function2(int a,...);
              };

              int A::function1 (int a,int b)
              {
                return a+b;
              }

              #include <stdarg.h>
              int A::function2(int a,...)
              {
                va_list ap;
                va_start(ap,a);
                int i;
                int result = 0;
                for(i = 0 ; i < a ; i ++)
                {
                 result += va_arg(ap,int);
                }
                return result;
              }

              void callee()
              {
                A a;
                a.function1(1, 2);
                a.function2(3, 1, 2, 3);
              }

            callee函數被翻譯成匯編后就變成: 
              //函數function1調用
              00401C1D  push    2
              00401C1F  push    1
              00401C21  lea     ecx,[ebp-8]
              00401C24  call    function1     注意,這里this沒有被入棧

              //函數function2調用
              00401C29  push    3
              00401C2B  push    2
              00401C2D  push    1
              00401C2F  push    3
              00401C31  lea     eax, [ebp-8]    這里引入this指針
              00401C34  push    eax
              00401C35  call    function2
              00401C3A  add     esp, 14h
              
              可見,對于參數個數固定情況下,它類似于stdcall,不定時則類似cdecl

              naked call
              這是一個很少見的調用約定,一般程序設計者建議不要使用。編譯器不會給這種函數增加初始化和清理代碼,更特殊的是,你不能用return返回返回值,只能用插入匯編返回結果。這一般用于實模式驅動程序設計,假設定義一個求和的加法程序,可以定義為:

              __declspec(naked) int add(int a,int b)
              {
                __asm mov eax,a
                __asm add eax,b
                __asm ret 
              }

              注意,這個函數沒有顯式的return返回值,返回通過修改eax寄存器實現,而且連退出函數的ret指令都必須顯式插入。上面代碼被翻譯成匯編以后變成:

              mov eax,[ebp+8]
              add eax,[ebp+12]
              ret 8

              注意這個修飾是和__stdcall及cdecl結合使用的,前面是它和cdecl結合使用的代碼,對于和stdcall結合的代碼,則變成:

              __declspec(naked) int __stdcall function(int a,int b)
              {
                __asm mov eax,a
                __asm add eax,b
                __asm ret 8    //注意后面的8
              }

              至于這種函數被調用,則和普通的cdecl及stdcall調用函數一致。

              函數調用約定導致的常見問題
              如果定義的約定和使用的約定不一致,則將導致堆棧被破壞,導致嚴重問題,下面是兩種常見的問題:

              函數原型聲明和函數體定義不一致 
              DLL導入函數時聲明了不同的函數約定 
              以后者為例,假設我們在dll種聲明了一種函數為:

              __declspec(dllexport) int func(int a,int b);//注意,這里沒有stdcall,使用的是cdecl
              使用時代碼為:

              typedef int (*WINAPI DLLFUNC)func(int a,int b);
              hLib = LoadLibrary(...);

              DLLFUNC func = (DLLFUNC)GetProcAddress(...)//這里修改了調用約定
              result = func(1,2);//導致錯誤

              由于調用者沒有理解WINAPI的含義錯誤的增加了這個修飾,上述代碼必然導致堆棧被破壞,MFC在編譯時插入的checkesp函數將告訴你,堆棧被破壞

            posted @ 2010-12-15 15:42 IT菜鳥 閱讀(188) | 評論 (0)編輯 收藏

            ebp和esp是32位的SP,BP 
            esp是堆棧指針    
            ebp是基址指針 
            ESP與SP的關系就象AX與AL,AH的關系.

            32位CPU所含有的寄存器有:

            4個數據寄存器(EAX、EBX、ECX和EDX)
            2個變址和指針寄存器(ESI和EDI) 2個指針寄存器(ESP和EBP)
            6個段寄存器(ES、CS、SS、DS、FS和GS)
            1個指令指針寄存器(EIP) 1個標志寄存器(EFlags)

            1、數據寄存器

            數據寄存器主要用來保存操作數和運算結果等信息,從而節省讀取操作數所需占用總線和訪問存儲器的時間。

            32位CPU有4個32位的通用寄存器EAX、EBX、ECX和EDX。對低16位數據的存取,不會影響高16位的數據。這些
            低16位寄存器分別命名為:AX、BX、CX和DX,它和先前的CPU中的寄存器相一致。

            4個16位寄存器又可分割成8個獨立的8位寄存器(AX:AH-AL、BX:BH-BL、CX:CH-CL、DX:DH-DL),每個寄
            存器都有自己的名稱,可獨立存取。程序員可利用數據寄存器的這種“可分可合”的特性,靈活地處理字/字
            節的信息。

            寄存器AX和AL通常稱為累加器(Accumulator),用累加器進行的操作可能需要更少時間。累加器可用于乘、
            除、輸入/輸出等操作,它們的使用頻率很高;
            寄存器BX稱為基地址寄存器(Base Register)。它可作為存儲器指針來使用;
            寄存器CX稱為計數寄存器(Count Register)。在循環和字符串操作時,要用它來控制循環次數;在位操作
            中,當移多位時,要用CL來指明移位的位數;
            寄存器DX稱為數據寄存器(Data Register)。在進行乘、除運算時,它可作為默認的操作數參與運算,也
            可用于存放I/O的端口地址。


            在16位CPU中,AX、BX、CX和DX不能作為基址和變址寄存器來存放存儲單元的地址,但在32位CPU中,其32位
            寄存器EAX、EBX、ECX和EDX不僅可傳送數據、暫存數據保存算術邏輯運算結果,而且也可作為指針寄存器,
            所以,這些32位寄存器更具有通用性。

            2、變址寄存器

            32位CPU有2個32位通用寄存器ESI和EDI。其低16位對應先前CPU中的SI和DI,對低16位數據的存取,不影響
            高16位的數據。

            寄存器ESI、EDI、SI和DI稱為變址寄存器(Index Register),它們主要用于存放存儲單元在段內的偏移量,
            用它們可實現多種存儲器操作數的尋址方式,為以不同的地址形式訪問存儲單元提供方便。

            變址寄存器不可分割成8位寄存器。作為通用寄存器,也可存儲算術邏輯運算的操作數和運算結果。

            它們可作一般的存儲器指針使用。在字符串操作指令的執行過程中,對它們有特定的要求,而且還具有特
            殊的功能。

            3、指針寄存器

            32位CPU有2個32位通用寄存器EBP和ESP。其低16位對應先前CPU中的SBP和SP,對低16位數據的存取,不影
            響高16位的數據。

            寄存器EBP、ESP、BP和SP稱為指針寄存器(Pointer Register),主要用于存放堆棧內存儲單元的偏移量,
            用它們可實現多種存儲器操作數的尋址方式,為以不同的地址形式訪問存儲單元提供方便。

            指針寄存器不可分割成8位寄存器。作為通用寄存器,也可存儲算術邏輯運算的操作數和運算結果。

            它們主要用于訪問堆棧內的存儲單元,并且規定:

            BP為基指針(Base Pointer)寄存器,用它可直接存取堆棧中的數據;
            SP為堆棧指針(Stack Pointer)寄存器,用它只可訪問棧頂。

            4、段寄存器

            段寄存器是根據內存分段的管理模式而設置的。內存單元的物理地址由段寄存器的值和一個偏移量組合而成
            的,這樣可用兩個較少位數的值組合成一個可訪問較大物理空間的內存地址。

            CPU內部的段寄存器:

            CS——代碼段寄存器(Code Segment Register),其值為代碼段的段值;
            DS——數據段寄存器(Data Segment Register),其值為數據段的段值;
            ES——附加段寄存器(Extra Segment Register),其值為附加數據段的段值;
            SS——堆棧段寄存器(Stack Segment Register),其值為堆棧段的段值;
            FS——附加段寄存器(Extra Segment Register),其值為附加數據段的段值;
            GS——附加段寄存器(Extra Segment Register),其值為附加數據段的段值。

            在16位CPU系統中,它只有4個段寄存器,所以,程序在任何時刻至多有4個正在使用的段可直接訪問;在32位
            微機系統中,它有6個段寄存器,所以,在此環境下開發的程序最多可同時訪問6個段。

            32位CPU有兩個不同的工作方式:實方式和保護方式。在每種方式下,段寄存器的作用是不同的。有關規定簡
            單描述如下:

            實方式: 前4個段寄存器CS、DS、ES和SS與先前CPU中的所對應的段寄存器的含義完全一致,內存單元的邏輯
            地址仍為“段值:偏移量”的形式。為訪問某內存段內的數據,必須使用該段寄存器和存儲單元的偏移量。
            保護方式: 在此方式下,情況要復雜得多,裝入段寄存器的不再是段值,而是稱為“選擇子”(Selector)的某個值。。

            5、指令指針寄存器

            32位CPU把指令指針擴展到32位,并記作EIP,EIP的低16位與先前CPU中的IP作用相同。

            指令指針EIP、IP(Instruction Pointer)是存放下次將要執行的指令在代碼段的偏移量。在具有預取指令功
            能的系統中,下次要執行的指令通常已被預取到指令隊列中,除非發生轉移情況。所以,在理解它們的功能
            時,不考慮存在指令隊列的情況。

            在實方式下,由于每個段的最大范圍為64K,所以,EIP中的高16位肯定都為0,此時,相當于只用其低16位
            的IP來反映程序中指令的執行次序。

            6、標志寄存器

            一、運算結果標志位
            1、進位標志CF(Carry Flag)
            進位標志CF主要用來反映運算是否產生進位或借位。如果運算結果的最高位產生了一個進位或借位,那么,其值為1,否則其值為0。

            使用該標志位的情況有:多字(字節)數的加減運算,無符號數的大小比較運算,移位操作,字(字節)之間移位,專門改變CF值的指令等。

            2、奇偶標志PF(Parity Flag)
            奇偶標志PF用于反映運算結果中“1”的個數的奇偶性。如果“1”的個數為偶數,則PF的值為1,否則其值為0。

            利用PF可進行奇偶校驗檢查,或產生奇偶校驗位。在數據傳送過程中,為了提供傳送的可靠性,如果采用奇偶校驗的方法,就可使用該標志位。

            3、輔助進位標志AF(Auxiliary Carry Flag)
            在發生下列情況時,輔助進位標志AF的值被置為1,否則其值為0:

            (1)、在字操作時,發生低字節向高字節進位或借位時;
            (2)、在字節操作時,發生低4位向高4位進位或借位時。

            對以上6個運算結果標志位,在一般編程情況下,標志位CF、ZF、SF和OF的使用頻率較高,而標志位PF和AF的使用頻率較低。

            4、零標志ZF(Zero Flag)
            零標志ZF用來反映運算結果是否為0。如果運算結果為0,則其值為1,否則其值為0。在判斷運算結果是否為0時,可使用此標志位。

            5、符號標志SF(Sign Flag)
            符號標志SF用來反映運算結果的符號位,它與運算結果的最高位相同。在微機系統中,有符號數采用補碼表示法,所以,SF也就反映運算結果的正負號。運算結果為正數時,SF的值為0,否則其值為1。

            6、溢出標志OF(Overflow Flag)
            溢出標志OF用于反映有符號數加減運算所得結果是否溢出。如果運算結果超過當前運算位數所能表示的范圍,則稱為溢出,OF的值被置為1,否則,OF的值被清為0。

            “溢出”和“進位”是兩個不同含義的概念,不要混淆。如果不太清楚的話,請查閱《計算機組成原理》課程中的有關章節。

            二、狀態控制標志位
            狀態控制標志位是用來控制CPU操作的,它們要通過專門的指令才能使之發生改變。

            1、追蹤標志TF(Trap Flag)
            當追蹤標志TF被置為1時,CPU進入單步執行方式,即每執行一條指令,產生一個單步中斷請求。這種方式主要用于程序的調試。

            指令系統中沒有專門的指令來改變標志位TF的值,但程序員可用其它辦法來改變其值。

            2、中斷允許標志IF(Interrupt-enable Flag)
            中斷允許標志IF是用來決定CPU是否響應CPU外部的可屏蔽中斷發出的中斷請求。但不管該標志為何值,CPU都必須響應CPU外部的不可屏蔽中斷所發出的中斷請求,以及CPU內部產生的中斷請求。具體規定如下:

            (1)、當IF=1時,CPU可以響應CPU外部的可屏蔽中斷發出的中斷請求;

            (2)、當IF=0時,CPU不響應CPU外部的可屏蔽中斷發出的中斷請求。

            CPU的指令系統中也有專門的指令來改變標志位IF的值。

            3、方向標志DF(Direction Flag)
            方向標志DF用來決定在串操作指令執行時有關指針寄存器發生調整的方向。具體規定在第5.2.11節——字符串操作指令——中給出。在微機的指令系統中,還提供了專門的指令來改變標志位DF的值。

            三、32位標志寄存器增加的標志位
            1、I/O特權標志IOPL(I/O Privilege Level)
            I/O特權標志用兩位二進制位來表示,也稱為I/O特權級字段。該字段指定了要求執行I/O指令的特權級。如果當前的特權級別在數值上小于等于IOPL的值,那么,該I/O指令可執行,否則將發生一個保護異常。

            2、嵌套任務標志NT(Nested Task)
            嵌套任務標志NT用來控制中斷返回指令IRET的執行。具體規定如下:

            (1)、當NT=0,用堆棧中保存的值恢復EFLAGS、CS和EIP,執行常規的中斷返回操作;

            (2)、當NT=1,通過任務轉換實現中斷返回。

            3、重啟動標志RF(Restart Flag)
            重啟動標志RF用來控制是否接受調試故障。規定:RF=0時,表示“接受”調試故障,否則拒絕之。在成功執行完一條指令后,處理機把RF置為0,當接受到一個非調試故障時,處理機就把它置為1。

            4、虛擬8086方式標志VM(Virtual 8086 Mode)
            如果該標志的值為1,則表示處理機處于虛擬的8086方式下的工作狀態,否則,處理機處于一般保護方式下的工作狀態
            posted @ 2010-12-15 11:17 IT菜鳥 閱讀(1036) | 評論 (0)編輯 收藏

            一、常用指令 

            1. 通用數據傳送指令

               MOV 傳送字或字節

               MOVSX 先符號擴展,再傳送

               MOVZX 先零擴展,再傳送

               PUSH 把字壓入堆棧

               POP 把字彈出堆棧

               PUSHA AX,CX,DX,BX,SP,BP,SI,DI依次壓入堆棧

               POPA DI,SI,BP,SP,BX,DX,CX,AX依次彈出堆棧

               PUSHAD EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次壓入堆棧

               POPAD EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次彈出堆棧

               BSWAP 交換32位寄存器里字節的順序 

               XCHG 交換字或字節.( 至少有一個操作數為寄存器,段寄存器不可作為操作數

               CMPXCHG 比較并交換操作數.( 第二個操作數必須為累加器AL/AX/EAX ) 

               XADD 先交換再累加.( 結果在第一個操作數里

               XLAT 字節查表轉換

               BX 指向一張 256 字節的表的起點, AL 為表的索引值 (0-255, 0-FFH); 返回 AL 為查表結果. ( [BX+AL]-> AL

            2. 輸入輸出端口傳送指令

               IN I/O端口輸入. ( 語法: IN 累加器, {端口號│DX} ) 

               OUT I/O端口輸出. ( 語法: OUT {端口號│DX},累加器

               輸入輸出端口由立即方式指定時, 其范圍是 0-255; 由寄存器 DX 指定時,其范圍是 0-65535. 

            3. 目的地址傳送指令

               LEA 裝入有效地址

                 : LEA DX,string ;把偏移地址存到DX. 

               LDS 傳送目標指針,把指針內容裝入DS. 

                 : LDS SI,string ;把段地址:偏移地址存到DS:SI. 

               LES 傳送目標指針,把指針內容裝入ES. 

                 : LES DI,string ;把段地址:偏移地址存到ESDI. 

               LFS 傳送目標指針,把指針內容裝入FS. 

                 : LFS DI,string ;把段地址:偏移地址存到FSD. 

               LGS 傳送目標指針,把指針內容裝入GS. 

                 : LGS DI,string ;把段地址:偏移地址存到GSDI. 

               LSS 傳送目標指針,把指針內容裝入SS. 

                 : LSS DI,string ;把段地址:偏移地址存到SSDI. 

            4. 標志傳送指令

               LAHF 標志寄存器傳送,把標志裝入AH. 

               SAHF 標志寄存器傳送,AH內容裝入標志寄存器

               PUSHF 標志入棧

               POPF 標志出棧

               PUSHD 32位標志入棧

               POPD 32位標志出棧

            二、算術運算指令 

               ADD 加法

               ADC 帶進位加法

               INC 1. 

               AAA 加法的ASCII碼調整

               DAA 加法的十進制調整

               SUB 減法

               SBB 帶借位減法

               DEC 1. 

               NEC 求反( 0 減之). 

               CMP 比較.(兩操作數作減法,僅修改標志位,不回送結果). 

               AAS 減法的ASCII碼調整

               DAS 減法的十進制調整

               MUL 無符號乘法

              IMUL 整數乘法

                 以上兩條,結果回送AHAL(字節運算),DXAX(字運算), 

               AAM 乘法的ASCII碼調整

               DIV 無符號除法

               IDIV 整數除法

                 以上兩條,結果回送

                 商回送AL,余數回送AH, (字節運算); 

                 商回送AX,余數回送DX, (字運算). 

               AAD 除法的ASCII碼調整

               CBW 字節轉換為字. (AL中字節的符號擴展到AH中去

               CWD 字轉換為雙字. (AX中的字的符號擴展到DX中去

               CWDE 字轉換為雙字. (AX中的字符號擴展到EAX中去

               CDQ 雙字擴展. (EAX中的字的符號擴展到EDX中去

            三、邏輯運算指令 

               AND 與運算

               OR 或運算

               XOR 異或運算

               NOT 取反

               TEST 測試.(兩操作數作與運算,僅修改標志位,不回送結果). 

               SHL 邏輯左移

               SAL 算術左移.(=SHL) 

               SHR 邏輯右移

               SAR 算術右移.(=SHR) 

               ROL 循環左移

               ROR 循環右移

               RCL 通過進位的循環左移

               RCR 通過進位的循環右移

                 以上八種移位指令,其移位次數可達255

                 移位一次時, 可直接用操作碼. SHL AX,1. 

                 移位>1次時, 則由寄存器CL給出移位次數

                  MOV CL,04 

                     SHL AX,CL 

            四、串指令 

               DS:SI 源串段寄存器 :源串變址

               ES I 目標串段寄存器:目標串變址

               CX 重復次數計數器

               AL/AX 掃描值

               D標志 0表示重復操作中SIDI應自動增量; 1表示應自動減量

            Z標志 用來控制掃描或比較操作的結束

               MOVS 串傳送

               ( MOVSB 傳送字符. MOVSW 傳送字. MOVSD 傳送雙字. ) 

               CMPS 串比較

               ( CMPSB 比較字符. CMPSW 比較字. ) 

               SCAS 串掃描

                 ALAX的內容與目標串作比較,比較結果反映在標志位

               LODS 裝入串

                 把源串中的元素(字或字節)逐一裝入ALAX

               ( LODSB 傳送字符. LODSW 傳送字. LODSD 傳送雙字. ) 

               STOS 保存串

               LODS的逆過程

               REP CX/ECX<>0時重復

               REPE/REPZ ZF=1或比較結果相等,CX/ECX<>0時重復

               REPNE/REPNZ ZF=0或比較結果不相等,CX/ECX<>0時重復

              REPC CF=1CX/ECX<>0時重復

               REPNC CF=0CX/ECX<>0時重復

            五、程序轉移指令 

            1>無條件轉移指令 (長轉移

               JMP 無條件轉移指令 

               CALL 過程調用 

               RET/RETF過程返回

            2>條件轉移指令 (短轉移,-128+127的距離內

               ( 當且僅當(SF XOR OF)=1,OP1<OP2 ) 

               JA/JNBE <

            posted @ 2010-12-15 11:01 IT菜鳥 閱讀(293) | 評論 (0)編輯 收藏

            今天寫一個小程序出現了這種問題:
            >c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(91) : warning C4005: “AF_IPX”: 宏重定義
            1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(460) : 參見“AF_IPX”的前一個定義
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(127) : warning C4005: “AF_MAX”: 宏重定義
            1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(479) : 參見“AF_MAX”的前一個定義
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(163) : warning C4005: “SO_DONTLINGER”: 宏重定義
            1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(402) : 參見“SO_DONTLINGER”的前一個定義
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(206) : error C2011: “sockaddr”: “struct”類型重定義
            1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(485) : 參見“sockaddr”的聲明
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2143: 語法錯誤 : 缺少“}”(在“常量”的前面)
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2143: 語法錯誤 : 缺少“;”(在“常量”的前面)
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2059: 語法錯誤 : “常量”
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C2143: 語法錯誤 : 缺少“;”(在“}”的前面)
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C4430: 缺少類型說明符 - 假定為 int。注意: C++ 不支持默認 int
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C4430: 缺少類型說明符 - 假定為 int。注意: C++ 不支持默認 int
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(518) : warning C4005: “IN_CLASSA”: 宏重定義
            1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(287) : 參見“IN_CLASSA”的前一個定義
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(524) : warning C4005: “IN_CLASSB”: 宏重定義
            1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(293) : 參見“IN_CLASSB”的前一個定義
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(530) : warning C4005: “IN_CLASSC”: 宏重定義
            1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(299) : 參見“IN_CLASSC”的前一個定義
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(541) : warning C4005: “INADDR_ANY”: 宏重定義
            1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(304) : 參見“INADDR_ANY”的前一個定義
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(543) : warning C4005: “INADDR_BROADCAST”: 宏重定義
            1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(306) : 參見“INADDR_BROADCAST”的前一個定義
            1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(577) : error C2011: “sockaddr_in”: “struct”類型重定義
            1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(312) : 參見“sockaddr_in”的聲明
            1>c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(132) : error C2011: “fd_set”: “struct”類型重定義
            1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(68) : 參見“fd_set”的聲明
            1>c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(167) : warning C4005: “FD_SET”: 宏重定義
            1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(102) : 參見“FD_SET”的前一個定義
            1>c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(176) : error C2011: “timeval”: “struct”類型重定義
            1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(111) : 參見“timeval”的聲明


            好久沒寫TCP的程序了,都忘記是怎么回事了,隱約記得解決方法很簡單。搜索了一下,記錄下來:

            引用鏈接:http://www.cnblogs.com/tonyyang132/archive/2009/10/14/1583110.html

            初看到如此一堆的錯誤委實不爽,但是只要將二者的包含順序調換一下問題就會解決,原因參見下面那個鏈接。另外,上述問題不僅影響直接包含二者的文件,還影響間接包含的情形。比如,a.h包含了windows.h,b.h包含了winsock2.h,如果在c.h當中要引用a.h和b.h,那么正確的順序應當是b.h先于a.h。當然,實踐當中有時很難找到究竟是哪兩個文件順序不對了,終極的解決辦法是,在當前工程(就是編譯不過的這個工程)所有include語句最前面加上#include <winsock2.h>和#include<windows.h>,世界清靜了。


            關于WINSOCK.H與winsock2.h中的重定義解決辦法分析
            問題描述:在 VC 6.0中使用socket相關的函數時沒有什么問題,可是到了.net下就有以下類似的錯誤,
            [C++ Error] winsock2.h(109): E2238 Multiple declaration for 'fd_set'
            [C++ Error] winsock.h(54): E2344 Earlier declaration of 'fd_set'
            [C++ Error] winsock2.h(112): E2146 Need an identifier to declare
            [C++ Warning] winsock2.h(144): W8017 Redefinition of 'FD_SET' is not identical
            [C++ Error] winsock2.h(153): E2238 Multiple declaration for 'timeval'
            [C++ Error] winsock.h(97): E2344 Earlier declaration of 'timeval'
            [C++ Error] winsock2.h(209): E2238 Multiple declaration for 'hostent'
            [C++ Error] winsock.h(153): E2344 Earlier declaration of 'hostent'
            [C++ Error] winsock2.h(222): E2238 Multiple declaration for 'netent'
            [C++ Error] winsock.h(166): E2344 Earlier declaration of 'netent'
            [C++ Error] winsock2.h(229): E2238 Multiple declaration for 'servent'
            [C++ Error] winsock.h(173): E2344 Earlier declaration of 'servent'
            [C++ Error] winsock2.h(241): E2238 Multiple declaration for 'protoent'
            [C++ Error] winsock.h(185): E2344 Earlier declaration of 'protoent'
            [C++ Error] winsock2.h(327): E2238 Multiple declaration for 'in_addr'
            [C++ Error] winsock.h(269): E2344 Earlier declaration of 'in_addr'
            [C++ Error] winsock2.h(385): E2238 Multiple declaration for 'sockaddr_in'
            [C++ Error] winsock.h(319): E2344 Earlier declaration of 'sockaddr_in'
            [C++ Error] winsock2.h(395): E2238 Multiple declaration for 'WSAData'
            [C++ Error] winsock.h(329): E2344 Earlier declaration of 'WSAData'
            [C++ Error] winsock2.h(411): E2146 Need an identifier to declare
            [C++ Warning] winsock2.h(455): W8017 Redefinition of 'SO_DONTLINGER' is not identical
            [C++ Warning] winsock2.h(512): W8017 Redefinition of 'AF_IPX' is not identical
            [C++ Warning] winsock2.h(540): W8017 Redefinition of 'AF_MAX' is not identical
            [C++ Error] winsock2.h(546): E2238 Multiple declaration for 'sockaddr'
            [C++ Error] winsock.h(492): E2344 Earlier declaration of 'sockaddr'
            [C++ Error] winsock2.h(586): E2238 Multiple declaration for 'sockproto'
            [C++ Error] winsock.h(501): E2344 Earlier declaration of 'sockproto'
            [C++ Error] winsock2.h(625): E2238 Multiple declaration for 'linger'
            [C++ Error] winsock2.h(625): E2228 Too many error or warning messages

            Solution:

            This problem arises because windows.h (at least, that version of it) includes not winsock2.h but winsock.h; sadly when Microsoft wrote winsock2.h they chose neither to change windows.h to include winsock2.h, which replaces winsock.h, nor to include windows.h from winsock2.h and then add the definitions for the new Winsock 2 API methods & structures (this might seem reasonable since Winsock 2 does, strictly speaking, replace Winsock 1, but since the API must be fully backwards-compatible the distinction is somewhat meaningless and there's no real benefit to making winsock2.h standalone).

            The fix is thankfully simple: always "#include <winsock2.h>" before windows.h.

            However, you must remember that if windows.h has been included by (for example) a higher-level header file that is subsequently including your header file, it's too late - so you must make sure that the higher-level header files respect this convention also.

            It is however rarely necessary to modify the header files of libraries or other code modules you are using just because you include their header files, and their header files include windows.h - you can just include winsock2.h before you include the library's header files.


            在包含jrtplib有時候我也遇到這個問題,解決方法與之相同。一句話,在#include<windows.h>之前 #include <winsock2.h> 問題就可以解決。

            問題描述]
               在包含了<windows.h>以及<winsock2.h>的工程中,編譯有時會出現如
            下錯誤:

                 error C2011: 'fd_set' : 'struct' type redefinition
                 error C2011: 'timeval' : 'struct' type redefinition
                                 ....
                 error C2375: 'accept' : redefinition; different linkage
            [原因分析]
               主要原因是因為<windows.h>中包含了<winsock.h>頭文件,由于其版
            本的不同,導致出
            現上述的錯誤。<windows.h>中相關代碼如下:
                           #ifndef WIN32_LEAN_AND_MEAN
                           #include <cderr.h>
                           #include <dde.h>
                           #include <ddeml.h>
                           ........
                            #ifndef _MAC
                           #include <winperf.h>
                           #include <winsock.h>
                           #endif
                            .......

                           #include <commdlg.h>
                           #endif
                           #endif
            [解決方案]
                由以上代碼可以看出如果在沒有定義WIN32_LEAN_AND_MEAN宏
            的大前
            提下windows.h有可能包含winsock.h 頭文件,因此我們得出一個很簡單
            的解決方
            法就是在包含<windows.h>之前定義WIN32_LEAN_AND_MEAN宏,如
            下所示:
            #define WIN32_LEAN_AND_MEAN
            #include <windows.h>

            posted @ 2010-12-07 15:30 IT菜鳥 閱讀(1160) | 評論 (0)編輯 收藏

                 摘要: 原地址:http://www.cnblogs.com/DonLiang/archive/2008/02/16/1070717.html近段時間,有幾個剛剛開始學習C#語言的愛好者問我:C#中的函數,其參數的傳遞,按值傳遞和按引用傳遞有什么區別。針對這一問題,我簡單寫了個示例程序,用以講解,希望我沒有把他們繞暈。因為,常聽別人說起:“你不說我還明白,你一說,我就糊涂了”。&n...  閱讀全文
            posted @ 2010-12-03 09:38 IT菜鳥 閱讀(521) | 評論 (0)編輯 收藏

            看如下代碼:
            template <int I> class Test
            {
                union Obj
                
            {
                    union Obj 
            *next;
                    
            char data[1];
                }
            ;

                
            static Obj* freeList[16];
                
            static T* ms_singleton;
            }
            ;

            第二個靜態變量初始化很容易:
            template<Class T>
            T
            * Test<T>::ms_singleton=0;

            第一個呢?
            要這樣:
            template<class T>
            typename Test
            <T>::Obj* Test<T>::freeList[16]={0};

            用typename關鍵字來告訴編譯器Obj是個類型。
            posted @ 2010-11-26 09:30 IT菜鳥 閱讀(1119) | 評論 (0)編輯 收藏

            C#結構體定義:
            [Serializable]
                
            struct DR2DE_Send
                
            {
                    
            public UInt32 AA;
                    
            public UInt32 BB;
                    
            public UInt16 CC;   //! Encryption method.
                    public UInt16 DD;            //! message version  // used to decide message version.
                    public UInt32 EE;           //! message id.
                    public UInt32 FF;          //! Message order. it is a increseable integer and set when checkEncrypt called and when ReturnMessage is set.
                    public Byte GG;
                    
            public Byte LL;
                    
            public char[] MM;
                }

            序列化操作:
            byte[] sendMsg = new byte[1024];
                        BinaryFormatter bf 
            = new BinaryFormatter();
                        MemoryStream stream 
            = new MemoryStream();
                        DR2DE_Send dr2deMessage;
                        dr2deMessage.AA
            = (UInt32)(0);
                        dr2deMessage.BB
            = (UInt16)(0);
                        dr2deMessage.CC
            =(UInt32)(0);
                        dr2deMessage.DD
            = (UInt16)(0);
                        dr2deMessage.EE
            = "Hello".ToCharArray();
                        dr2deMessage.FF
            = 40010;
                        dr2deMessage.GG
            = (UInt32)(22 + dr2deMessage.data.Length);
                        dr2deMessage.HH
            = 1;
                        dr2deMessage.II
            = 1;

                        bf.Serialize(stream, dr2deMessage);
                        sendMsg 
            = stream.ToArray();
            sendMsg就可以放到sock中發送出去了。
            posted @ 2010-11-24 10:53 IT菜鳥 閱讀(1262) | 評論 (0)編輯 收藏

            僅列出標題
            共7頁: 1 2 3 4 5 6 7 
            久久男人AV资源网站| 久久青青草原精品国产不卡| 亚洲精品乱码久久久久久| 国产成人精品综合久久久| 波多野结衣中文字幕久久| 国产精品99久久久久久董美香 | 亚洲人成精品久久久久| 国内精品久久久久久99蜜桃| 狠狠人妻久久久久久综合| 色综合合久久天天给综看| 久久人人爽爽爽人久久久| 一本大道加勒比久久综合| 午夜精品久久久久成人| 久久精品99久久香蕉国产色戒 | 国产免费久久精品99re丫y| 亚洲熟妇无码另类久久久| 久久综合中文字幕| 无码人妻久久一区二区三区蜜桃| 久久无码av三级| 国产Av激情久久无码天堂| 要久久爱在线免费观看| 7国产欧美日韩综合天堂中文久久久久 | 亚洲午夜无码久久久久小说| 99久久综合狠狠综合久久止| 久久久久99这里有精品10| 久久高清一级毛片| 91精品国产综合久久香蕉| 国产精品禁18久久久夂久| 精品国产青草久久久久福利| 久久久久久无码国产精品中文字幕| 久久亚洲精品无码AV红樱桃| 亚洲国产精品无码久久久蜜芽| 狠狠色丁香久久婷婷综合图片| 久久久久久久久久免免费精品| 一级做a爱片久久毛片| 九九久久99综合一区二区| 国产亚洲欧美成人久久片| 久久99国产亚洲高清观看首页| 国产精品国色综合久久| 99久久人妻无码精品系列| 日本一区精品久久久久影院|