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

            每天早晨叫醒你的不是鬧鐘,而是夢想

              C++博客 :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
              62 Posts :: 0 Stories :: 5 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(1)

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            #

            題目:二叉樹的結(jié)點定義如下:

            struct TreeNode

            {

                    int m_nValue;

                    TreeNode* m_pLeft;

                    TreeNode* m_pRight;

            };

            輸入兩棵二叉樹AB,判斷樹B是不是A的子結(jié)構(gòu)。

            例如,下圖中的兩棵樹AB,由于A中有一部分子樹的結(jié)構(gòu)和B是一樣的,因此B就是A的子結(jié)構(gòu)。

                             1                                                   8
                           /    \                                               /    \
                          8    7                                             9    2
                        /    \
                       9    2
                            /  \
                           4  7

            分析:這是2010年微軟校園招聘時的一道題目。二叉樹一直是微軟面試題中經(jīng)常出現(xiàn)的數(shù)據(jù)結(jié)構(gòu)。對微軟有興趣的讀者一定要重點關(guān)注二叉樹。

                            回到這個題目的本身。要查找樹A中是否存在和樹B結(jié)構(gòu)一樣的子樹,我們可以分為兩步:第一步在樹A中找到和B的根結(jié)點的值一樣的結(jié)點N,第二步再判斷樹A中以N為根結(jié)點的子樹是不是包括和樹B一樣的結(jié)構(gòu)。

                            第一步在樹A中查找與根結(jié)點的值一樣的結(jié)點。這實際上就是樹的遍歷。對二叉樹這種數(shù)據(jù)結(jié)構(gòu)熟悉的讀者自然知道我們可以用遞歸的方法去遍歷,也可以用循環(huán)的方法去遍歷。由于遞歸的代碼實現(xiàn)比較簡潔,面試時如果沒有特別要求,我們通常都會采用遞歸的方式。下面是參考代碼:

            bool HasSubtree(TreeNode* pTreeHead1, TreeNode* pTreeHead2)

            {

                    if((pTreeHead1 == NULL && pTreeHead2 != NULL) ||

                            (pTreeHead1 != NULL && pTreeHead2 == NULL))

                            return false;

             

                    if(pTreeHead1 == NULL && pTreeHead2 == NULL)

                            return true;

             

                    return HasSubtreeCore(pTreeHead1, pTreeHead2);

            }

            bool HasSubtreeCore(TreeNode* pTreeHead1, TreeNode* pTreeHead2)

            {

                    bool result = false;

                    if(pTreeHead1->m_nValue == pTreeHead2->m_nValue)

                    {

                            result = DoesTree1HaveAllNodesOfTree2(pTreeHead1, pTreeHead2);

                    }

             

                    if(!result && pTreeHead1->m_pLeft != NULL)

                            result = HasSubtreeCore(pTreeHead1->m_pLeft, pTreeHead2);

             

                    if(!result && pTreeHead1->m_pRight != NULL)

                            result = HasSubtreeCore(pTreeHead1->m_pRight, pTreeHead2);

             

                    return result;

            }

            在上述代碼中,我們遞歸調(diào)用hasSubtreeCore遍歷二叉樹A。如果發(fā)現(xiàn)某一結(jié)點的值和樹B的頭結(jié)點的值相同,則調(diào)用DoesTree1HaveAllNodeOfTree2,做第二步判斷。

            在面試的時候,我們一定要注意邊界條件的檢查,即檢查空指針。當(dāng)樹A或樹B為空的時候,定義相應(yīng)的輸出。如果沒有檢查并做相應(yīng)的處理,程序非常容易崩潰,這是面試時非常忌諱的事情。由于沒有必要在每一次遞歸中做邊界檢查(每一次遞歸都做檢查,增加了不必要的時間開銷),上述代碼只在HasSubtree中作了邊界檢查后,在HasSubtreeCore中作遞歸遍歷。

            接下來考慮第二步,判斷以樹A中以N為根結(jié)點的子樹是不是和樹B具有相同的結(jié)構(gòu)。同樣,我們也可以用遞歸的思路來考慮:如果結(jié)點N的值和樹B的根結(jié)點不相同,則以N為根結(jié)點的子樹和樹B肯定不具有相同的結(jié)點;如果他們的值相同,則遞歸地判斷他們的各自的左右結(jié)點的值是不是相同。遞歸的終止條件是我們到達了樹A或者樹B的葉結(jié)點。參考代碼如下:

            bool DoesTree1HaveAllNodesOfTree2(TreeNode* pTreeHead1, TreeNode* pTreeHead2)

            {

                    if(pTreeHead2 == NULL)

                            return true;

             

                    if(pTreeHead1 == NULL)

                            return false;

             

                    if(pTreeHead1->m_nValue != pTreeHead2->m_nValue)

                            return false;

             

                    return DoesTree1HaveAllNodesOfTree2(pTreeHead1->m_pLeft, pTreeHead2->m_pLeft) &&

                            DoesTree1HaveAllNodesOfTree2(pTreeHead1->m_pRight, pTreeHead2->m_pRight);

            }

             

              博主何海濤對本博客文章享有版權(quán)。網(wǎng)絡(luò)轉(zhuǎn)載請注明出處http://zhedahht.blog.163.com/

            posted @ 2011-04-29 14:03 沛沛 閱讀(375) | 評論 (0)編輯 收藏

                 摘要:   題目(一):我們可以用static修飾一個類的成員函數(shù),也可以用const修飾類的成員函數(shù)(寫在函數(shù)的最后表示不能修改成員變量,不是指寫在前面表示返回值為常量)。請問:能不能同時用static和const修飾類的成員函數(shù)? 分析:答案是不可以。C++編譯器在實現(xiàn)const的成員函數(shù)的時候為了確保該函數(shù)不能修改類的實例的狀態(tài),會在函數(shù)中添加一個隱式的參數(shù)const this*。但當(dāng)...  閱讀全文
            posted @ 2011-04-29 13:44 沛沛 閱讀(723) | 評論 (0)編輯 收藏

            一、介紹字符編碼、內(nèi)碼,順帶介紹漢字編碼
             
            字符必須編碼后才能被計算機處理。計算機使用的缺省編碼方式就是計算機的內(nèi)碼。早
            期的計算機使用7位的ASCII編碼,為了處理漢字,程序員設(shè)計了用于簡體中文的GB2312
            和用于繁體中文的big5。
             
            GB2312(1980年)一共收錄了7445個字符,包括6763個漢字和682個其它符號。漢字區(qū)的內(nèi)
            碼范圍高字節(jié)從B0-F7,低字節(jié)從A1-FE,占用的碼位是72*94=6768。其中有5個空位是
            D7FA-D7FE。
             
            GB2312支持的漢字太少。1995年的漢字?jǐn)U展規(guī)范GBK1.0收錄了21886個符號,它分為漢字
            區(qū)和圖形符號區(qū)。漢字區(qū)包括21003個字符。2000年的GB18030是取代GBK1.0的正式國家
            標(biāo)準(zhǔn)。該標(biāo)準(zhǔn)收錄了27484個漢字,同時還收錄了藏文、蒙文、維吾爾文等主要的少數(shù)民
            族文字。現(xiàn)在的PC平臺必須支持GB18030,對嵌入式產(chǎn)品暫不作要求。所以手機、MP3一
            般只支持GB2312。
             
            從ASCII、GB2312、GBK到GB18030,這些編碼方法是向下兼容的,即同一個字符在這些方
            案中總是有相同的編碼,后面的標(biāo)準(zhǔn)支持更多的字符。在這些編碼中,英文和中文可以
            統(tǒng)一地處理。區(qū)分中文編碼的方法是高字節(jié)的最高位不為0。按照程序員的稱呼,
            GB2312、GBK到GB18030都屬于雙字節(jié)字符集 (DBCS)。
             
            有的中文Windows的缺省內(nèi)碼還是GBK,可以通過GB18030升級包升級到GB18030。不過
            GB18030相對GBK增加的字符,普通人是很難用到的,通常我們還是用GBK指代中文
            Windows內(nèi)碼。
             
            這里還有一些細(xì)節(jié):
             
            GB2312的原文還是區(qū)位碼,從區(qū)位碼到內(nèi)碼,需要在高字節(jié)和低字節(jié)上分別加上A0。
             
            在DBCS中,GB內(nèi)碼的存儲格式始終是big endian,即高位在前。
             
            GB2312的兩個字節(jié)的最高位都是1。但符合這個條件的碼位只有128*128=16384個。所以
            GBK和GB18030的低字節(jié)最高位都可能不是1。不過這不影響DBCS字符流的解析:在讀取
            DBCS字符流時,只要遇到高位為1的字節(jié),就可以將下兩個字節(jié)作為一個雙字節(jié)編碼,而
            不用管低字節(jié)的高位是什么。
             
             
             
            二、關(guān)于編碼
             
            所謂編碼,是以固定的順序排列字符,并以此做為記錄、存貯、傳遞、交換的統(tǒng)一內(nèi)部
            特征,這個字符排列順序被稱為“編碼”。和中文字庫有關(guān)的常見編碼有:大陸GB碼、
            GBK碼、港臺BIG-5碼等。下面簡要介紹一下。
             
            GB碼
             
            全稱是GB2312-80《信息交換用漢字編碼字符集 基本集》,1980年發(fā)布,是中文信息處
            理的國家標(biāo)準(zhǔn),在大陸及海外使用簡體中文的地區(qū)(如新加坡等)是強制使用的唯一中
            文編碼。P-Windows3.2和蘋果OS就是以GB2312為基本漢字編碼, Windows 95/98則以GBK
            為基本漢字編碼、但兼容支持GB2312。
             
            GB碼共收錄6763個簡體漢字、682個符號,其中漢字部分:一級字3755,以拼音排序,二
            級字3008,以偏旁排序。該標(biāo)準(zhǔn)的制定和應(yīng)用為規(guī)范、推動中文信息化進程起了很大作
            用。
             
            1990年又制定了繁體字的編碼標(biāo)準(zhǔn)GB12345-90《信息交換用漢字編碼字符集 第一輔助
            集》,目的在于規(guī)范必須使用繁體字的各種場合,以及古籍整理等。該標(biāo)準(zhǔn)共收錄6866
            個漢字(比GB2312多103個字,其它廠商的字庫大多不包括這些字),純繁體的字大概有
            2200余個。
             
            Unicode編碼(Universal Multiple Octet Coded Character Set)
             
            國際標(biāo)準(zhǔn)組織于1984年4月成立ISO/IEC JTC1/SC2/WG2工作組,針對各國文字、符號進行
            統(tǒng)一性編碼。1991年美國跨國公司成立Unicode Consortium,并于1991年10月與WG2達成
            協(xié)議,采用同一編碼字集。目前Unicode是采用16位編碼體系,其字符集內(nèi)容與ISO10646
            的BMP(Basic Multilingual Plane)相同。Unicode于1992年6月通過DIS(Draf
            International Standard),目前版本V2.0于1996公布,內(nèi)容包含符號6811個,漢字
            20902個,韓文拼音11172個,造字區(qū)6400個,保留20249個,共計65534個。
             
            GBK編碼(Chinese Internal Code Specification)
             
            GBK編碼是中國大陸制訂的、等同于UCS的新的中文編碼擴展國家標(biāo)準(zhǔn)。GBK工作小組于
            1995年10月,同年12月完成GBK規(guī)范。該編碼標(biāo)準(zhǔn)兼容GB2312,共收錄漢字21003個、符
            號883個,并提供1894個造字碼位,簡、繁體字融于一庫。
             
            Windows95/98簡體中文版的字庫表層編碼就采用的是GBK,通過GBK與UCS之間一一對應(yīng)的
            碼表與底層字庫聯(lián)系。

             
            BIG5編碼
             
            是目前臺灣、香港地區(qū)普遍使用的一種繁體漢字的編碼標(biāo)準(zhǔn),包括440個符號,一級漢字
            5401個、二級漢字7652個,共計13060個漢字。
             
            方正748編碼
             
            所謂748編碼,是指方正系統(tǒng)在長期應(yīng)用過程中實施、制定的簡、繁體字庫編碼方式,簡
            體兼容GB2312且有所擴展,共7156字;繁體兼容GB12345并擴展全部BIG-5漢字,計14943
            字。此外,方正748編碼還含有豐富的符號庫。748編碼僅用于方正軟件和系統(tǒng)。
             

             

            本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/coffeemay/archive/2006/04/17/666213.aspx

            posted @ 2011-04-29 11:22 沛沛 閱讀(477) | 評論 (0)編輯 收藏

            Socket(套接字)

            ◆先看定義:
            typedef unsigned int u_int;
            typedef u_int SOCKET;◆Socket相當(dāng)于進行網(wǎng)絡(luò)通信兩端的插座,只要對方的Socket和自己的Socket有通信聯(lián)接,雙方就可以發(fā)送和接收數(shù)據(jù)了。其定義類似于文件句柄的定義。

            ◆Socket有五種不同的類型:

            1、流式套接字(stream socket)
            定義:

            #define SOCK_STREAM 1 流式套接字提供了雙向、有序的、無重復(fù)的以及無記錄邊界的數(shù)據(jù)流服務(wù),適合處理大量數(shù)據(jù)。它是面向聯(lián)結(jié)的,必須建立數(shù)據(jù)傳輸鏈路,同時還必須對傳輸?shù)臄?shù)據(jù)進行驗證,確保數(shù)據(jù)的準(zhǔn)確性。因此,系統(tǒng)開銷較大。

            2、 數(shù)據(jù)報套接字(datagram socket)

            定義:

            #define SOCK_DGRAM 2 數(shù)據(jù)報套接字也支持雙向的數(shù)據(jù)流,但不保證傳輸數(shù)據(jù)的準(zhǔn)確性,但保留了記錄邊界。由于數(shù)據(jù)報套接字是無聯(lián)接的,例如廣播時的聯(lián)接,所以并不保證接收端是否正在偵聽。數(shù)據(jù)報套接字傳輸效率比較高。

            3、原始套接字(raw-protocol interface)

            定義:

            #define SOCK_RAW 3 原始套接字保存了數(shù)據(jù)包中的完整IP頭,前面兩種套接字只能收到用戶數(shù)據(jù)。因此可以通過原始套接字對數(shù)據(jù)進行分析。
            其它兩種套接字不常用,這里就不介紹了。

            ◆Socket開發(fā)所必須需要的文件(以WinSock V2.0為例):

            頭文件:Winsock2.h

            庫文件:WS2_32.LIB

            動態(tài)庫:W32_32.DLL

             一些重要的定義

            1、數(shù)據(jù)類型的基本定義:這個大家一看就懂。

            typedef unsigned char u_char;
            typedef unsigned short u_short;
            typedef unsigned int u_int;
            typedef unsigned long u_long;2、 網(wǎng)絡(luò)地址的數(shù)據(jù)結(jié)構(gòu),有一個老的和一個新的的,請大家留意,如果想知道為什么,
            請發(fā)郵件給Bill Gate。其實就是計算機的IP地址,不過一般不用用點分開的IP地
            址,當(dāng)然也提供一些轉(zhuǎn)換函數(shù)。

            ◆ 舊的網(wǎng)絡(luò)地址結(jié)構(gòu)的定義,為一個4字節(jié)的聯(lián)合:

            struct in_addr {
            union {
            struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
            struct { u_short s_w1,s_w2; } S_un_w;
            u_long S_addr;
            } S_un;
            #define s_addr S_un.S_addr /* can be used for most tcp & ip code */
            //下面幾行省略,反正沒什么用處。
            };其實完全不用這么麻煩,請看下面:

            ◆ 新的網(wǎng)絡(luò)地址結(jié)構(gòu)的定義:
            非常簡單,就是一個無符號長整數(shù) unsigned long。舉個例子:IP地址為127.0.0.1的網(wǎng)絡(luò)地址是什么呢?請看定義:

            #define INADDR_LOOPBACK 0x7f0000013、 套接字地址結(jié)構(gòu)

            (1)、sockaddr結(jié)構(gòu):

            struct sockaddr {
            u_short sa_family; /* address family */
            char sa_data[14]; /* up to 14 bytes of direct address */
            };sa_family為網(wǎng)絡(luò)地址類型,一般為AF_INET,表示該socket在Internet域中進行通信,該地址結(jié)構(gòu)隨選擇的協(xié)議的不同而變化,因此一般情況下另一個與該地址結(jié)構(gòu)大小相同的sockaddr_in結(jié)構(gòu)更為常用,sockaddr_in結(jié)構(gòu)用來標(biāo)識TCP/IP協(xié)議下的地址。換句話說,這個結(jié)構(gòu)是通用socket地址結(jié)構(gòu),而下面的sockaddr_in是專門針對Internet域的socket地址結(jié)構(gòu)。

            (2)、sockaddr_in結(jié)構(gòu)

            struct sockaddr_in {
            short sin_family;
            u_short sin_port;
            struct in_addr sin_addr;
            char sin_zero[8];
            };sin _family為網(wǎng)絡(luò)地址類型,必須設(shè)定為AF_INET。sin_port為服務(wù)端口,注意不要使用已固定的服務(wù)端口,如HTTP的端口80等。如果端口設(shè)置為0,則系統(tǒng)會自動分配一個唯一端口。sin_addr為一個unsigned long的IP地址。sin_zero為填充字段,純粹用來保證結(jié)構(gòu)的大小。

            ◆ 將常用的用點分開的IP地址轉(zhuǎn)換為unsigned long類型的IP地址的函數(shù):

            unsigned long inet_addr(const char FAR * cp )用法:

            unsigned long addr=inet_addr("192.1.8.84")◆ 如果將sin_addr設(shè)置為INADDR_ANY,則表示所有的IP地址,也即所有的計算機。

            #define INADDR_ANY (u_long)0x000000004、 主機地址:

            先看定義:

            struct hostent {
            char FAR * h_name; /* official name of host */
            char FAR * FAR * h_aliases; /* alias list */
            short h_addrtype; /* host address type */
            short h_length; /* length of address */
            char FAR * FAR * h_addr_list; /* list of addresses */
            #define h_addr h_addr_list[0] /* address, for backward compat */
            };
            h_name為主機名字。
            h_aliases為主機別名列表。
            h_addrtype為地址類型。
            h_length為地址類型。
            h_addr_list為IP地址,如果該主機有多個網(wǎng)卡,就包括地址的列表。另外還有幾個類似的結(jié)構(gòu),這里就不一一介紹了。

            5、 常見TCP/IP協(xié)議的定義:

            #define IPPROTO_IP 0
            #define IPPROTO_ICMP 1
            #define IPPROTO_IGMP 2
            #define IPPROTO_TCP 6
            #define IPPROTO_UDP 17
            #define IPPROTO_RAW 255 具體是什么協(xié)議,大家一看就知道了。

             套接字的屬性

            為了靈活使用套接字,我們可以對它的屬性進行設(shè)定。

            1、 屬性內(nèi)容:

            //允許調(diào)試輸出
            #define SO_DEBUG 0x0001 /* turn on debugging info recording */
            //是否監(jiān)聽模式
            #define SO_ACCEPTCONN 0x0002 /* socket has had listen() */
            //套接字與其他套接字的地址綁定
            #define SO_REUSEADDR 0x0004 /* allow local address reuse */
            //保持連接
            #define SO_KEEPALIVE 0x0008 /* keep connections alive */
            //不要路由出去
            #define SO_DONTROUTE 0x0010 /* just use interface addresses */
            //設(shè)置為廣播
            #define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */
            //使用環(huán)回不通過硬件
            #define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */
            //當(dāng)前拖延值
            #define SO_LINGER 0x0080 /* linger on close if data present */
            //是否加入帶外數(shù)據(jù)
            #define SO_OOBINLINE 0x0100 /* leave received OOB data in line */
            //禁用LINGER選項
            #define SO_DONTLINGER (int)(~SO_LINGER)
            //發(fā)送緩沖區(qū)長度
            #define SO_SNDBUF 0x1001 /* send buffer size */
            //接收緩沖區(qū)長度
            #define SO_RCVBUF 0x1002 /* receive buffer size */
            //發(fā)送超時時間
            #define SO_SNDTIMEO 0x1005 /* send timeout */
            //接收超時時間
            #define SO_RCVTIMEO 0x1006 /* receive timeout */
            //錯誤狀態(tài)
            #define SO_ERROR 0x1007 /* get error status and clear */
            //套接字類型
            #define SO_TYPE 0x1008 /* get socket type */2、 讀取socket屬性:

            int getsockopt(SOCKET s, int level, int optname, char FAR * optval, int FAR * optlen)s為欲讀取屬性的套接字。level為套接字選項的級別,大多數(shù)是特定協(xié)議和套接字專有的。如IP協(xié)議應(yīng)為 IPPROTO_IP。

            optname為讀取選項的名稱
            optval為存放選項值的緩沖區(qū)指針。
            optlen為緩沖區(qū)的長度用法:

            int ttl=0; //讀取TTL值
            int rc = getsockopt( s, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl));
            //來自MS platform SDK 20033、 設(shè)置socket屬性:

            int setsockopt(SOCKET s,int level, int optname,const char FAR * optval, int optlen)s為欲設(shè)置屬性的套接字。
            level為套接字選項的級別,用法同上。
            optname為設(shè)置選項的名稱
            optval為存放選項值的緩沖區(qū)指針。
            optlen為緩沖區(qū)的長度

            用法:

            int ttl=32; //設(shè)置TTL值
            int rc = setsockopt( s, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl)); 套接字的使用步驟

            1、啟動Winsock:對Winsock DLL進行初始化,協(xié)商Winsock的版本支持并分配必要的
            資源。(服務(wù)器端和客戶端)

            int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData )

            wVersionRequested為打算加載Winsock的版本,一般如下設(shè)置:
            wVersionRequested=MAKEWORD(2,0)
            或者直接賦值:wVersionRequested=2

            LPWSADATA為初始化Socket后加載的版本的信息,定義如下:
            typedef struct WSAData {
            WORD wVersion;
            WORD wHighVersion;
            char szDescription[WSADESCRIPTION_LEN+1];
            char szSystemStatus[WSASYS_STATUS_LEN+1];
            unsigned short iMaxSockets;
            unsigned short iMaxUdpDg;
            char FAR * lpVendorInfo;
            } WSADATA, FAR * LPWSADATA;如果加載成功后數(shù)據(jù)為:

            wVersion=2表示加載版本為2.0。
            wHighVersion=514表示當(dāng)前系統(tǒng)支持socket最高版本為2.2。
            szDescription="WinSock 2.0"
            szSystemStatus="Running"表示正在運行。
            iMaxSockets=0表示同時打開的socket最大數(shù),為0表示沒有限制。
            iMaxUdpDg=0表示同時打開的數(shù)據(jù)報最大數(shù),為0表示沒有限制。
            lpVendorInfo沒有使用,為廠商指定信息預(yù)留。該函數(shù)使用方法:

            WORD wVersion=MAKEWORD(2,0);
            WSADATA wsData;
            int nResult= WSAStartup(wVersion,&wsData);
            if(nResult !=0)
            {
            //錯誤處理
            }2、創(chuàng)建套接字:(服務(wù)器端和客戶端)

            SOCKET socket( int af, int type, int protocol );
            af為網(wǎng)絡(luò)地址類型,一般為AF_INET,表示在Internet域中使用。
            type為套接字類型,前面已經(jīng)介紹了。
            protocol為指定網(wǎng)絡(luò)協(xié)議,一般為IPPROTO_IP。用法:

            SOCKET sock=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
            if(sock==INVALID_SOCKET)
            {
            //錯誤處理
            }3、套接字的綁定:將本地地址綁定到所創(chuàng)建的套接字上。(服務(wù)器端和客戶端)

            int bind( SOCKET s, const struct sockaddr FAR * name, int namelen )
            s為已經(jīng)創(chuàng)建的套接字。
            name為socket地址結(jié)構(gòu),為sockaddr結(jié)構(gòu),如前面討論的,我們一般使用sockaddr_in
            結(jié)構(gòu),在使用再強制轉(zhuǎn)換為sockaddr結(jié)構(gòu)。
            namelen為地址結(jié)構(gòu)的長度。
            用法:

            sockaddr_in addr;
            addr. sin_family=AF_INET;
            addr. sin_port= htons(0); //保證字節(jié)順序
            addr. sin_addr.s_addr= inet_addr("192.1.8.84")
            int nResult=bind(s,(sockaddr*)&addr,sizeof(sockaddr));
            if(nResult==SOCKET_ERROR)
            {
            //錯誤處理
            }4、 套接字的監(jiān)聽:(服務(wù)器端)

            int listen(SOCKET s, int backlog )s為一個已綁定但未聯(lián)接的套接字。
            backlog為指定正在等待聯(lián)接的最大隊列長度,這個參數(shù)非常重要,因為服務(wù)器一般可
            以提供多個連接。
            用法:

            int nResult=listen(s,5) //最多5個連接
            if(nResult==SOCKET_ERROR)
            {
            //錯誤處理
            }5、套接字等待連接::(服務(wù)器端)

            SOCKET accept( SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen )s為處于監(jiān)聽模式的套接字。
            sockaddr為接收成功后返回客戶端的網(wǎng)絡(luò)地址。
            addrlen為網(wǎng)絡(luò)地址的長度。

            用法:

            sockaddr_in addr;
            SOCKET s_d=accept(s,(sockaddr*)&addr,sizeof(sockaddr));
            if(s==INVALID_SOCKET)
            {
            //錯誤處理
            }6、套接字的連結(jié):將兩個套接字連結(jié)起來準(zhǔn)備通信。(客戶端)

            int connect(SOCKET s, const struct sockaddr FAR * name, int namelen )s為欲連結(jié)的已創(chuàng)建的套接字。
            name為欲連結(jié)的socket地址。
            namelen為socket地址的結(jié)構(gòu)的長度。

            用法:

            sockaddr_in addr;
            addr. sin_family=AF_INET;
            addr. sin_port=htons(0); //保證字節(jié)順序
            addr. sin_addr.s_addr= htonl(INADDR_ANY) //保證字節(jié)順序
            int nResult=connect(s,(sockaddr*)&addr,sizeof(sockaddr));
            if(nResult==SOCKET_ERROR)
            {
            //錯誤處理
            }7、套接字發(fā)送數(shù)據(jù):(服務(wù)器端和客戶端)

            int send(SOCKET s, const char FAR * buf, int len, int flags )s為服務(wù)器端監(jiān)聽的套接字。
            buf為欲發(fā)送數(shù)據(jù)緩沖區(qū)的指針。
            len為發(fā)送數(shù)據(jù)緩沖區(qū)的長度。
            flags為數(shù)據(jù)發(fā)送標(biāo)記。
            返回值為發(fā)送數(shù)據(jù)的字符數(shù)。

            ◆這里講一下這個發(fā)送標(biāo)記,下面8中討論的接收標(biāo)記也一樣:

            flag取值必須為0或者如下定義的組合:0表示沒有特殊行為。

            #define MSG_OOB 0x1 /* process out-of-band data */
            #define MSG_PEEK 0x2 /* peek at incoming message */
            #define MSG_DONTROUTE 0x4 /* send without using routing tables */
            MSG_OOB表示數(shù)據(jù)應(yīng)該帶外發(fā)送,所謂帶外數(shù)據(jù)就是TCP緊急數(shù)據(jù)。
            MSG_PEEK表示使有用的數(shù)據(jù)復(fù)制到緩沖區(qū)內(nèi),但并不從系統(tǒng)緩沖區(qū)內(nèi)刪除。
            MSG_DONTROUTE表示不要將包路由出去。

            用法:

            char buf[]="xiaojin";
            int nResult=send(s,buf,strlen(buf));
            if(nResult==SOCKET_ERROR)
            {
            //錯誤處理
            }8、 套接字的數(shù)據(jù)接收:(客戶端)

            int recv( SOCKET s, char FAR * buf, int len, int flags )s為準(zhǔn)備接收數(shù)據(jù)的套接字。
            buf為準(zhǔn)備接收數(shù)據(jù)的緩沖區(qū)。
            len為準(zhǔn)備接收數(shù)據(jù)緩沖區(qū)的大小。
            flags為數(shù)據(jù)接收標(biāo)記。
            返回值為接收的數(shù)據(jù)的字符數(shù)。

            用法:

            char mess[1000];
            int nResult =recv(s,mess,1000,0);
            if(nResult==SOCKET_ERROR)
            {
            //錯誤處理
            }9、中斷套接字連接:通知服務(wù)器端或客戶端停止接收和發(fā)送數(shù)據(jù)。(服務(wù)器端和客戶端)

            int shutdown(SOCKET s, int how)s為欲中斷連接的套接字。
            How為描述禁止哪些操作,取值為:SD_RECEIVE、SD_SEND、SD_BOTH。

            #define SD_RECEIVE 0x00
            #define SD_SEND 0x01
            #define SD_BOTH 0x02用法:

            int nResult= shutdown(s,SD_BOTH);
            if(nResult==SOCKET_ERROR)
            {
            //錯誤處理
            }10、 關(guān)閉套接字:釋放所占有的資源。(服務(wù)器端和客戶端)

            int closesocket( SOCKET s )s為欲關(guān)閉的套接字。

            用法:

            int nResult=closesocket(s);
            if(nResult==SOCKET_ERROR)
            {
            //錯誤處理
            }
             

            本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/coffeemay/archive/2006/08/05/1023149.aspx

            posted @ 2011-04-29 11:18 沛沛 閱讀(447) | 評論 (0)編輯 收藏

            如果您想要下一代 Windows 版本 Windows Vista、Windows Server 2003、Windows XP 或 Windows 2000 的完整 Symbol,您可以下載 Symbol 封裝然後將它安裝在您的電腦上。

            在此 Symbol 下載封裝是依處理器型態(tài) (x86、Itanium 和 x64) 以及 Build 類別 (retail 以及 checked) 來列示。幾乎所有的客戶都需要用於 Retail 版本的 Symbol。如果您正在 Debugging 一個特殊版本的 Windows 需要額外的 Debugging 資訊,那您就應(yīng)該下載用於 Checked 版本的 Symbol。

            Windows XP 與 Windows Server 2003 不需要當(dāng)?shù)卣Z系化版本的 Symbol 來 Debug 當(dāng)?shù)卣Z系化版本的產(chǎn)品。每一個 Windows XP 與 Windows Server™ 2003 Symbol 下載封裝皆可用於 Debugging 所有已當(dāng)?shù)卣Z系化的版本。

            每一個 x86 Symbol 可能需要 750 MB 或更大的硬碟空間,每個 Itanium Symbol 可能需要 560 MB 或更大的硬碟空間,而每個 x64 Symbol 套件可能需要 640 MB 或更大空間。此空間需求量包含下載的封裝與其所需的暫存檔案所要求的空間大小,建議您在下載與安裝任可 Symbol 套件之前至少預(yù)留 1 GB 的可用硬碟空間。

            注意:若您為 MSDN Subscriber 且使用的是 Windows Vista CTP 版本,您可由 Microsoft Connect 伺服器取得 CTP 版本使用之 Symbols 封裝套件。若您目前並非 MSDN Subscriber 但想成為 MSDN Subscriber,您可以由此處加入。

            檢視用於 Symbol 封裝的下載連結(jié)

               Windows Vista Release Candidate 1 (RC1)
             
              這些封裝檔案中包含了所有用於 Debug Windows Vista Release Candidate 1 (RC1) 的完整 Symbol 套件。

            Windows Vista RC1 x86 retail symbols, all languages (檔案大小:270 MB - 大部份的客戶需要這個封裝套件。)
            Windows Vista RC1 Itanium retail symbols, all languages (檔案大小:148 MB)
            Windows Vista RC1 x64 retail symbols, all languages (檔案大小:205 MB)
            Windows Vista RC1 x86 checked symbols, all languages (檔案大小:248 MB)
            Windows Vista RC1 Itanium checked symbols, all languages (檔案大小:187 MB)
            Windows Vista RC1 x64 checked symbols, all languages (檔案大小:224 MB)
             
               Windows Server 2003 and Windows XP x64 Edition
             
              含 Service Pack 1 的 Windows Server 2003 Symbols

            此封裝包含在 Debug 己安裝 Service Pack 1 之 Windows Server 2003 的完整 Symbol 套件。其中 Windows Server 2003 的 Symbol 己被更新為符合 Windows Server 2003 Service Pack 1 更新檔案的 Symbol。

            附註:Windows Server 2003 SP1 x64-based Symbol 套件亦可用於 Windows XP x64 Edition。

            Windows Server 2003 with Service Pack 1 x86 retail symbols, all languages (檔案大小:153 MB - 大部份的客戶需要這個封裝套件。)
            Windows Server 2003 with Service Pack 1 x86 checked symbols, all languages (檔案大小:146 MB)
            Windows Server 2003 with Service Pack 1 Itanium-based retail symbols, all languages (檔案大小:102 MB)
            Windows Server 2003 with Service Pack 1 Itanium-based checked symbols, all languages (檔案大小:123 MB)
            Windows Server 2003 with Service Pack 1 x64-based retail symbols, all languages (檔案大小:123 MB)
            Windows Server 2003 with Service Pack 1 x64-based checked symbols, all languages (檔案大小:113 MB)
            減少下載的量:Windows Server 2003 Service Pack 1

            此封裝比含有 Service Pack 1 的 Windows Server 2003 完整套件的下載量來的小。它僅包含隨附於 Windows Server 2003 Service Pack 1 之檔案的 Symbol。若您己經(jīng)安裝了 Windows Server 2003 的 Symbol,您可以將它安裝在相同的路徑,這樣您就擁有了含有 Service Pack 1 的 Windows Server 2003 完整 Symbol 套件。

            Windows Server 2003 Service Pack 1 x86 retail symbols, all languages (檔案大小:130 MB - 此為大多數(shù)客戶所需要的封裝。)
            Windows Server 2003 Service Pack 1 x86 checked symbols, all languages (檔案大小:121 MB)
            Windows Server 2003 Service Pack 1 Itanium-based retail symbols, all languages (檔案大小:91 MB)
            Windows Server 2003 Service Pack 1 Itanium-based checked symbols, all languages (檔案大小:110 MB)
            不包含 Service Pack 的 Windows Server 2003 Symbols

             

            Windows Server 2003 x86 retail symbols, all languages (檔案大小:168 MB - 此為大多數(shù)客戶所需要的封裝。)
            Windows Server 2003 Itanium retail symbols, all languages (檔案大小:105 MB)
            Windows Server 2003 x86 checked symbols, all languages (檔案大小:163 MB)
            Windows Server 2003 Itanium checked symbols, all languages (檔案大小:123 MB)
             
               Windows XP
             
              含 Service Pack 2 的 Windows XP Symbol

            此封裝包含在 Debug 己安裝 Service Pack 2 之 Windows XP 的完整 Symbol 套件。其中 Windows XP 的 Symbol 己被更新為符合 Windows XP Service Pack 2 更新檔案的 Symbol。

             

            Windows XP with Service Pack 2 x86 retail symbols, all languages (檔案大小:195 MB - 此為大多數(shù)客戶所需要的封裝。)
            Windows XP with Service Pack 2 x86 checked symbols, all languages (檔案大小:188 MB)
            減少下載的量:Windows XP Service Pack 2

            此封裝比含有 Service Pack 2 的 Windows XP 完整套件的下載量來的小。它僅包含隨附於 Service Pack 2 之檔案的 Symbol。若您己經(jīng)安裝了 Windows XP 的 Symbol,您可以將它安裝在相同的路徑,這樣您就擁有了含有 Service Pack 2 的 Windows XP 完整 Symbol 套件。

             

            Windows XP Service Pack 2 x86 retail symbols, all languages (檔案大小:145 MB - 此為大多數(shù)客戶所需要的封裝。)
            Windows XP Service Pack 2 x86 checked symbols, all languages (檔案大小:132 MB)
            Windows XP with Service Pack 1 以及 Service Pack 1a Symbol

            此封裝包含在 Debug 己安裝 Service Pack 1 或 Service Pack 1a 之 Windows XP 的完整 Symbol 套件。其中 Windows XP 的 Symbol 己被更新為符合 Windows XP Service Pack 1 以及 Service Pack 1a 更新檔案的 Symbol。

             

            Windows XP with Service Pack 1 and Service Pack 1a x86 retail symbols, all languages (檔案大小:172 MB - 此為大多數(shù)客戶所需要的封裝。)
            Windows XP with Service Pack 1 and Service Pack 1a Itanium retail symbols, all languages (檔案大小:101 MB)
            Windows XP with Service Pack 1 and Service Pack 1a x86 checked symbols, all languages (檔案大小:168 MB)
            Windows XP with Service Pack 1 and Service Pack 1a Itanium checked symbols, all languages (檔案大小:124 MB)
            減少下載的量:Windows XP Service Pack 1 以及 Service Pack 1a Symbol

            此封裝比含有 Service Pack 1 以及 Service Pack 1a 的 Windows XP 完整套件的下載量來的小。它僅包含隨附於 Service Pack 1 以及 Service Pack 1a 之檔案的 Symbol。若您己經(jīng)安裝了 Windows XP 的 Symbol,您可以將它安裝在相同的路徑,這樣您就擁有了含有 Service Pack 1 以及 Service Pack 1a 的 Windows XP 完整 Symbol 套件。

             

            Windows XP Service Pack 1 and Service Pack 1a x86 retail symbols, all languages (檔案大小:103 MB - Most customers want this package.)
            Windows XP Service Pack 1 and Service Pack 1a Itanium retail symbols, all languages (檔案大小:50 MB)
            Windows XP Service Pack 1 and Service Pack 1a x86 checked symbols, all languages (檔案大小:96 MB)
            Windows XP Service Pack 1 and Service Pack 1a Itanium checked symbols, all languages (檔案大小:63 MB)
            Windows XP Symbol 其中不含 Service Pack

             

            Windows XP x86 retail symbols, all languages (檔案大小:149 MB - 此為大多數(shù)客戶所需要的封裝。)
            Windows XP IA-64 retail symbols, all languages (檔案大小:95 MB)
            Windows XP x86 checked symbols, all languages (檔案大小:147 MB)
            Windows XP IA-64 checked symbols, all languages (檔案大小:116 MB)
             
               Windows 2000
             
              下列連結(jié)連接至每一個 Symbols 封裝的下載處,或是帶領(lǐng)您至提供關(guān)於 Windows 2000 Symbol 下載資訊的網(wǎng)站。

            Windows 2000 SP4 的更新彙總套件 1 (Update Rollup 1)。欲安裝 Windows 2000 SP4 的更新彙總套件 1 中所包括的新增 Symbols,請在下方的下拉式選單中選擇您想要下載的語言版本然後在 "按一下這裡開始下載" 的連結(jié)按一下。欲擁有完整的 Symbol 套件,您必須先安裝 Windows 2000 Symbol,之後接著安裝 Service Pack 4 Symbol,最後再安裝 Windows 2000 SP4 更新彙總套件 1 的 Symbol。

            < language="javascript" type="text/javascript"> function ShowSelectInfoSP4UR1() { oElement = window.event.srcElement for (i=0; i 選擇所需的語言版本:
             
              Arabic Chinese (Simplified) Chinese (Traditional) Czech Danish Dutch English Finnish French German Greek Hebrew Hungarian Italian Japanese Japanese NEC98 Korean Norwegian Polish Portugese Portugese (Brazilian) Russian Spanish Swedish Turkish    按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)

              按一下這裡開始下載 (下載量: 22 MB)
             

            Windows 2000 Service Pack 4 。安裝 Service Pack 4 所提供的額外 Symbol,請在下面的下拉式表單中選擇您想要下載的語言然後按一下 "按一下這裡開始下載" 的連結(jié)項目。欲擁有完整的 Symbol 套件,您必須在安裝 Service Pack 4 Symbol 前先安裝 Windows 2000 Symbol。

            < language="javascript" type="text/javascript"> function ShowSelectInfoSP4() { oElement = window.event.srcElement for (i=0; i 選擇所需的語言版本:
             
              Arabic Chinese (Simplified) Chinese (Traditional) Chinese (Hong Kong SAR) Czech Danish Dutch English Finnish French German Greek Hebrew Hungarian Italian Japanese Japanese NEC98 Korean Norwegian Polish Portugese Portugese (Brazilian) Russian Spanish Swedish Turkish    按一下這裡開始下載 (下載量:72 MB)

              按一下這裡開始下載 (下載量:72 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)

              按一下這裡開始下載 (下載量:74 MB)
             

            若您有特殊版本的 Service Pack 4 且需要更多的 Debugging 資訊,那您應(yīng)該下載用於 Checked 版本的 Symbol。下載英文版的 Service Pack 4 Checked 版本,請按一下這裡 (下載量: 66 MB)。

            Windows 2000 Service Pack 3。安裝 Service Pack 3 所提供的額外 Symbol,請在下面的下拉式表單中選擇您想要下載的語言然後按一下 "按一下這裡開始下載" 的連結(jié)項目。欲擁有完整的 Symbol 套件,您必須在安裝 Service Pack 3 Symbol 前先安裝 Windows 2000 Symbol。

            < language="javascript" type="text/javascript"> function ShowSelectInfo() { oElement = window.event.srcElement for (i=0; i 選擇所需的語言:
             
              English French German Italian Japanese Japanese NEC98 Portugese (Brazilian) Spanish    按一下這裡開始下載 (下載量:67 MB)

              按一下這裡開始下載 (下載量:67 MB)

              按一下這裡開始下載 (下載量:67 MB)

              按一下這裡開始下載 (下載量:67 MB)

              按一下這裡開始下載 (下載量:67 MB)

              按一下這裡開始下載 (下載量:67 MB)

              按一下這裡開始下載 (下載量:67 MB)

              按一下這裡開始下載 (下載量:67 MB)
             

            若您有特殊版本的 Service Pack 3 且需要更多的 Debugging 資訊,那您應(yīng)該下載用於 Checked 版本的 Symbol。下載英文版的 Service Pack 3 Checked 版本,請按一下此處。

            Windows 2000 Service Pack 2 Security Rollup Package 1。安裝 Service Pack 2 Security Rollup Package 1 所提供的額外 Symbol,請在下面的下拉式表單中選擇您想要下載的語言然後按一下 "按一下這裡開始下載" 的連結(jié)項目。欲擁有完整的 Symbol 套件,您必須先安裝 Windows 2000 Symbol,再安裝 Service Pack 2 Symbol,最後再安裝 Service Pack 2 Security Rollup Package 1 Symbol。

            選擇所需的語言:
             
              English Arabic Chinese (Simplified) Chinese (Traditional) Czech Danish Dutch Finnish French German Greek Hebrew Hungarian Italian Japanese Japanese NEC98 Korean Norwegian Polish Portugese Portugese (Brazilian) Russian Spanish Swedish Turkish    按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)

              按一下這裡開始下載 (下載量:19 MB)
             

            Windows 2000 Service Pack 2 Symbols - 跟隨 SP2 所新增的 Symbol。欲擁有用於含有 Service Pack 2 之 Windows 2000 的完整 Symbol 套件,您必須先安裝 Windows 2000 Symbo 然後再安裝 Service Pack 2 Symbol。

            Windows 2000 Service Pack 1 Symbols - 跟隨 SP1 所新增的 Symbol。欲擁有用於含有 Service Pack 1 之 Windows 2000 的完整 Symbol 套件,您必須先安裝 Windows 2000 Symbo 然後再安裝 Service Pack 1 Symbol。

            Windows 2000 Symbols - 用於 Debugging Windows 2000 所需的檔案。
             


             


            Debugging 說明檔 - 為了幫助您 Debugging 問題,請?zhí)崴途€上說明檔的要求或是使用附加的資源於 DDK 開發(fā)人員支援。

            意見反應(yīng) - 我們期待得到您闗於 Symbol 的意見反應(yīng)。請將您的建議或是錯誤報告 windbgfb@microsoft.com。 雖然您無法從此處取得技術(shù)支援,但是您的意見將可以幫助我們未來在 Symbol 的變更計畫並且使得它在未來更合於您的需求。

            Last Updated: 2006 年 9 月 13 日


            本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/coffeemay/archive/2006/10/12/1331356.aspx

            posted @ 2011-04-29 11:13 沛沛 閱讀(795) | 評論 (0)編輯 收藏

            1、 在介紹靜態(tài)對象、全局對象與程序的運行機制之間的關(guān)系之前,我們首先看一下atexit函數(shù)。

            atexit函數(shù)的聲明為:int atexit( void ( __cdecl *func )( void ) );

            參數(shù)為函數(shù)指針,返回值為整型,0表示成功,其他表示失敗。當(dāng)程序運行結(jié)束時,他調(diào)用atexit函數(shù)注冊的所有函數(shù)。如果多次調(diào)用atexit函數(shù),那么系統(tǒng)將以LIFO(last-in-first-out)的方式調(diào)用所有的注冊函數(shù)。

            舉例如下(代碼摘自MSDN):

             1#include <stdlib.h>
             2#include <stdio.h>
             3 
             4void fn1( void ), fn2( void ), fn3( void ), fn4( void );
             5 
             6void main( void )
             7{
             8  atexit( fn1 );
             9  atexit( fn2 );
            10  atexit( fn3 );
            11  atexit( fn4 );
            12  printf( "This is executed first.\n" );
            13}

            14 
            15void fn1()
            16{
            17  printf( "next.\n" );
            18}

            19 
            20void fn2()
            21{
            22  printf( "executed " );
            23}

            24 
            25void fn3()
            26{
            27  printf( "is " );
            28}

            29 
            30void fn4()
            31{
            32  printf( "This " );
            33}

            34



            編譯、運行程序后,程序的輸出為:
            This is executed first.
            This is executed next.
            注冊函數(shù)的順序為:fn1、fn2、fn3、fn4,但是調(diào)用順序為fn4、fn3、fn2、fn1。

            2、理解了atexit函數(shù)之后,我們就可以來看看局部靜態(tài)對象了。

            1 class AAA{ … } ;
            2 AAA* createAAA() 
            3 {
            4         static AAA a ;
            5         return &a ;
            6 }
            7 


            在調(diào)試狀態(tài)下,匯編代碼如下(請觀察藍色標(biāo)記出來的代碼):
            AAA* createAAA()
            {
                 …
                 static AAA a ;

            [1] 00401056 call        AAA::AAA (4010A0h)
            [2] 0040105B push      offset `createAAA'::`2'::a::`dynamic atexit destructor' (442410h)
            [3] 00401060 call        atexit (409A50h)
            00401065 add            esp,4
            00401068 mov           dword ptr [ebp-4],0FFFFFFFFh
                 return &a ;
            0040106F mov           eax,offset a (452620h)
            }

            00401091 ret  
            注:[1]、[2]、[3]為方便說明加入的字符,實際代碼中并不存在。
            [1]語句很明顯的調(diào)用AAA的構(gòu)造函數(shù)。
            [2]語句將442410h壓入棧中。
            [3]語句調(diào)用atexit函數(shù),根據(jù)我們的了解,atexit的參數(shù)應(yīng)該是函數(shù)指針。那么我們來分析一下442410h處的代碼,從注釋來看,我們看到了destructor。代碼如下:
            `createAAA'::`2'::a::`dynamic atexit destructor':

            [1] 0044242E mov         ecx,offset a (452620h)
            [2] 00442433 call        AAA::~AAA (403A90h)

            0044244B ret           
            [1]語句將a的地址放在ecx寄存器中,這是this調(diào)用規(guī)范的風(fēng)格。
            [2]語句調(diào)用AAA的析構(gòu)函數(shù)。

            程序結(jié)束時,將調(diào)用atexit函數(shù)注冊的442410h處的函數(shù),進而調(diào)用了AAA的析構(gòu)函數(shù)。從而保證了析構(gòu)函數(shù)的調(diào)用。

             
            3、   了解了局部靜態(tài)對象之后,我們來看看全局對象。
            我們知道全局對象必須在main函數(shù)前已經(jīng)被構(gòu)造。為了弄清楚全局對象何時被構(gòu)造,我在全局對象的實例化處設(shè)置了斷點,調(diào)用堆棧如下:
            static.exe!aaaa::`dynamic initializer'() Line 22 C++
            static.exe!_initterm(void (void)* * pfbegin=0x00451038, void (void)* * pfend=0x00451064) Line 707 C
            static.exe!_cinit(int initFloatingPrecision=1) Line 208 + 0xf bytes C
            static.exe!mainCRTStartup() Line 266 + 0x7 bytes C


            作為對比,我在AAA的析構(gòu)函數(shù)出設(shè)置了斷點,調(diào)用堆棧如下:
                 static.exe!AAA::~AAA() Line 19  C++
                 static.exe!aaaa::`dynamic atexit destructor'() + 0x28 bytes  C++
                 static.exe!doexit(int code=0, int quick=0, int retcaller=0) Line 451  C
                 static.exe!exit(int code=0) Line 311 + 0xd bytes  C
                 static.exe!mainCRTStartup() Line 289  C


            由此我們可以看出程序的實際入口點位mainCRTStartup而不是main函數(shù)(相對于ANSI的控制臺程序而言)。
            我們來分析一下_cinit函數(shù):
            注釋中有一句[3. General C initializer routines],看來該函數(shù)的功能之一是完成C的初始化例程。
            函數(shù)的核心代碼如下:
            /*
                     * do initializations
                     */
                    initret = _initterm_e( __xi_a, __xi_z );
            /*
                     * do C++ initializations
                     */
                    _initterm( __xc_a, __xc_z );
            看來該函數(shù)主要進行C、C++的初始化。我們進一步分析函數(shù)_initterm_e和_initterm,兩個函數(shù)的功能進本相同,都是遍歷函數(shù)指針(由參數(shù)指定函數(shù)指針的開始位置[__xi_a、__xi_z]、結(jié)束位置[__xc_a、__xc_z]),如果函數(shù)指針不為null,那么調(diào)用該函數(shù)。
            那么__xi_a、__xi_z和__xc_a、__xc_z到底代表了什么呢?在cinitexe.c文件中有如下代碼:
            #pragma data_seg(".CRT$XIA")
            _CRTALLOC(".CRT$XIA") _PVFV __xi_a[] = { NULL };
             
            #pragma data_seg(".CRT$XIZ")
            _CRTALLOC(".CRT$XIZ") _PVFV __xi_z[] = { NULL };/* C initializers */
             
            #pragma data_seg(".CRT$XCA")
            _CRTALLOC(".CRT$XCA") _PVFV __xc_a[] = { NULL };
             
            #pragma data_seg(".CRT$XCZ")
            _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[] = { NULL };/* C++ initializers */
            #pragma comment(linker, "/merge:.CRT=.data")
            可以看出這四個變量分別在數(shù)據(jù)段.CRT$XIA、.CRT$XIZ、.CRT$XCA、.CRT$XCZ中。當(dāng)連接器布局代碼時,它按根據(jù)的名稱,按照字母排序的規(guī)則,排列所有段。這樣在段.CRT$XIA中的變量出現(xiàn)在段.CRT$XIZ所有變量之前,從而形成鏈表。對于.CRT$XCA、.CRT$XCZ數(shù)據(jù)段同理。最后這四個數(shù)據(jù)段被合并到.data數(shù)據(jù)段中。
            再看看這些變量的類型,typedef void (__cdecl *_PVFV)(void); 所以這些變量組成了2個初始化函數(shù)指針鏈表。
            調(diào)試過程中,看到__xc_a、__xc_z鏈表中,指向的初始化函數(shù)很多是構(gòu)造函數(shù),如:
            static std::_Init_locks initlocks;
            static filebuf fout(_cpp_stdout);
            extern _CRTDATA2 ostream cout(&fout);
            cout對象也在此時被構(gòu)造。
            對于析構(gòu)函數(shù)的調(diào)用也是采用相同的方式,只是此時每一種初始化,都有一種終止函數(shù)與之對應(yīng)。

            4、 總結(jié)
            l         編譯、連接程序時,編譯器將所有全局對象的初始化函數(shù)放入.CRT$Xx中,連接器將所有的.CRT$XCx段合并成為.rdata數(shù)據(jù)段。在.CRT$XCA 到 .CRT$XCZ的所有段的數(shù)據(jù)組成初始化函數(shù)指針列表。
            l   函數(shù)執(zhí)行時,_initterm( __xc_a, __xc_z )函數(shù)調(diào)用所有的初始化函數(shù)。構(gòu)造全局對象。構(gòu)造對象完畢,調(diào)用atexit函數(shù)來保證析構(gòu)函數(shù)的調(diào)用。Modern C++ Design就是通過控制調(diào)用atexit函數(shù)來決定對象的析構(gòu)順序的。
            l   對于靜態(tài)對象使用atexit來保證析構(gòu)函數(shù)的調(diào)用。
            l    程序結(jié)束時,調(diào)用exit來析構(gòu)全局對象或靜態(tài)對象。


            本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/coffeemay/archive/2006/11/19/1395250.aspx

            posted @ 2011-04-29 11:06 沛沛 閱讀(878) | 評論 (0)編輯 收藏

                 摘要: 標(biāo) 題: 【翻譯】“PE文件格式”1.9版 完整譯文(附注釋)作 者: ah007時 間: 2006-02-28,13:32鏈 接: http://bbs.pediy.com/showthread.php?threadid=21932 $Id: pe.txt,v 1.9 1999/03/20 23:55:09 LUEVELSMEYER Exp $ PE文件格式系列譯文之...  閱讀全文
            posted @ 2011-04-29 11:03 沛沛 閱讀(312) | 評論 (0)編輯 收藏

            我們知道,在NT/2K/XP中,操作系統(tǒng)利用虛擬內(nèi)存管理技術(shù)來維護地址空間映像,每個進程分配一個4GB的虛擬地址空間。運行在用戶態(tài)的應(yīng)用程序,不能直接訪問物理內(nèi)存地址;而運行在核心態(tài)的驅(qū)動程序,能將虛擬地址空間映射為物理地址空間,從而訪問物理內(nèi)存地址。

            如果要在應(yīng)用程序中以物理地址方式訪問內(nèi)存,自然而然的辦法,是編寫一個專用的驅(qū)動程序(如大家熟悉的WinIO),里面設(shè)置一定的IOCTL碼,應(yīng)用程序通過調(diào)用DeviceIoCtrol()來實現(xiàn)這樣的功能。

            那么,有沒有一種方法,省去編寫專用驅(qū)動程序這一步,很方便地就能訪問物理內(nèi)存呢?答案是肯定的。實際上,微軟早就給我們準(zhǔn)備好了一套辦法,只是他們秘而不宣罷了。系統(tǒng)內(nèi)建一個叫做PhysicalMemory的內(nèi)核對象,可以通過系統(tǒng)核心文件NTDLL.DLL中的有關(guān)API進行操縱,從而實現(xiàn)物理內(nèi)存的直接訪問。微軟聲稱這些API是用于驅(qū)動程序開發(fā)的,在VC/.NET中未提供原型說明和庫文件,然而事實證明在應(yīng)用程序中調(diào)用它們是沒有問題的。我們感興趣的API主要包括:

            ZwOpenSection 或 NtOpenSection - 打開內(nèi)核對象
            ZwMapViewOfSection 或 NtMapViewOfSection - 映射虛擬地址空間
            ZwUnmapViewOfSection 或 NtUnmapViewOfSection - 取消地址空間映射
            RtlInitUnicodeString - 用UNICODE串初始化UNICODE描述的結(jié)構(gòu)
            以下的代碼描述了如何利用NTDLL.DLL中的上述幾個API,實現(xiàn)對物理內(nèi)存的讀取。需要指出的是,只有system擁有讀寫權(quán)限,administrator只有讀權(quán)限,而user連讀權(quán)限都沒有。這一點,是不能與專用驅(qū)動程序方法向相比的。

            在VC/.NET中,由于沒有相應(yīng)的原型說明和庫文件,我們用GetProcAddress()進行DLL顯式調(diào)用。前面大段的代碼,用于說明必需的類型和結(jié)構(gòu)。讀取物理內(nèi)存的主要步驟為:打開內(nèi)核對象 → 映射虛擬地址空間 → 讀取(復(fù)制)內(nèi)存 → 取消地址空間映射。

            typedef LONG    NTSTATUS;
             
            typedef struct _UNICODE_STRING
            {
                USHORT Length;
                USHORT MaximumLength;
                PWSTR Buffer;
            } UNICODE_STRING, *PUNICODE_STRING;
             
            typedef enum _SECTION_INHERIT
            {
                ViewShare = 1,
                ViewUnmap = 2
            } SECTION_INHERIT, *PSECTION_INHERIT;
             
            typedef struct _OBJECT_ATTRIBUTES
            {
                ULONG Length;
                HANDLE RootDirectory;
                PUNICODE_STRING ObjectName;
                ULONG Attributes;
                PVOID SecurityDescriptor;
                PVOID SecurityQualityOfService;
            } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
             
            #define InitializeObjectAttributes( p, n, a, r, s ) { \
                (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
                (p)->RootDirectory = r; \
                (p)->Attributes = a; \
                (p)->ObjectName = n; \
                (p)->SecurityDescriptor = s; \
                (p)->SecurityQualityOfService = NULL; \
            }
             
            // Interesting functions in NTDLL
            typedef NTSTATUS (WINAPI *ZwOpenSectionProc)
            (
                PHANDLE SectionHandle,
                DWORD DesiredAccess,
                POBJECT_ATTRIBUTES ObjectAttributes
            );
            typedef NTSTATUS (WINAPI *ZwMapViewOfSectionProc)
            (
                HANDLE SectionHandle,
                HANDLE ProcessHandle,
                PVOID *BaseAddress,
                ULONG ZeroBits,
                ULONG CommitSize,
                PLARGE_INTEGER SectionOffset,
                PULONG ViewSize,
                SECTION_INHERIT InheritDisposition,
                ULONG AllocationType,
                ULONG Protect
            );
            typedef NTSTATUS (WINAPI *ZwUnmapViewOfSectionProc)
            (
                HANDLE ProcessHandle,
                PVOID BaseAddress
            );
            typedef VOID (WINAPI *RtlInitUnicodeStringProc)
            (
                IN OUT PUNICODE_STRING DestinationString,
                IN PCWSTR SourceString
            );
             
            // Global variables
            static HMODULE hModule = NULL;
            static HANDLE hPhysicalMemory = NULL;
            static ZwOpenSectionProc ZwOpenSection;
            static ZwMapViewOfSectionProc ZwMapViewOfSection;
            static ZwUnmapViewOfSectionProc ZwUnmapViewOfSection;
            static RtlInitUnicodeStringProc RtlInitUnicodeString;
             
            // initialize
            BOOL InitPhysicalMemory()
            {
                if (!(hModule = LoadLibrary("ntdll.dll")))
                {
                    return FALSE;
                }
             
                // 以下從NTDLL獲取我們需要的幾個函數(shù)指針
                if (!(ZwOpenSection = (ZwOpenSectionProc)GetProcAddress(hModule, "ZwOpenSection")))
                {
                    return FALSE;
                }
             
                if (!(ZwMapViewOfSection = (ZwMapViewOfSectionProc)GetProcAddress(hModule, "ZwMapViewOfSection")))
                {
                    return FALSE;
                }
             
                if (!(ZwUnmapViewOfSection = (ZwUnmapViewOfSectionProc)GetProcAddress(hModule, "ZwUnmapViewOfSection")))
                {
                    return FALSE;
                }
             
                if (!(RtlInitUnicodeString = (RtlInitUnicodeStringProc)GetProcAddress(hModule, "RtlInitUnicodeString")))
                {
                    return FALSE;
                }
             
                // 以下打開內(nèi)核對象
                WCHAR PhysicalMemoryName[] = L"\\Device\\PhysicalMemory";
                UNICODE_STRING PhysicalMemoryString;
                OBJECT_ATTRIBUTES attributes;
                RtlInitUnicodeString(&PhysicalMemoryString, PhysicalMemoryName);
                InitializeObjectAttributes(&attributes, &PhysicalMemoryString, 0, NULL, NULL);
                NTSTATUS status = ZwOpenSection(&hPhysicalMemory, SECTION_MAP_READ, &attributes );
             
                return (status >= 0);
            }
             
            // terminate -- free handles
            void ExitPhysicalMemory()
            {
                if (hPhysicalMemory != NULL)
                {
                    CloseHandle(hPhysicalMemory);
                }
             
                if (hModule != NULL)
                {
                    FreeLibrary(hModule);
                }
            }
             
            BOOL ReadPhysicalMemory(PVOID buffer, DWORD address, DWORD length)
            {
                DWORD outlen;            // 輸出長度,根據(jù)內(nèi)存分頁大小可能大于要求的長度
                PVOID vaddress;          // 映射的虛地址
                NTSTATUS status;         // NTDLL函數(shù)返回的狀態(tài)
                LARGE_INTEGER base;      // 物理內(nèi)存地址
             
                vaddress = 0;
                outlen = length;
                base.QuadPart = (ULONGLONG)(address);
             
                // 映射物理內(nèi)存地址到當(dāng)前進程的虛地址空間
                status = ZwMapViewOfSection(hPhysicalMemory,
                    (HANDLE) -1,
                    (PVOID *)&vaddress,
                    0,
                    length,
                    &base,
                    &outlen,
                    ViewShare,
                    0,
                    PAGE_READONLY);
             
                if (status < 0)
                {
                    return FALSE;
                }
             
                // 當(dāng)前進程的虛地址空間中,復(fù)制數(shù)據(jù)到輸出緩沖區(qū)
                memmove(buffer, vaddress, length);
             
                // 完成訪問,取消地址映射
                status = ZwUnmapViewOfSection((HANDLE)-1, (PVOID)vaddress);
             
                return (status >= 0);
            }
             
            // 一個測試函數(shù),從物理地址0xfe000開始,讀取4096個字節(jié)
            // 對于Award BIOS,可以從這段數(shù)據(jù)找到序列號等信息
            BOOL test()
            {
                UCHAR buf[4096];
             
                if (!InitPhysicalMemory())
                {
                    return FALSE;
                }
             
                if (!ReadPhysicalMemory(buf, 0xfe000, 4096))
                {
                    // ... 成功讀取了指定數(shù)據(jù)
                    ExitPhysicalMemory();
                    return FALSE;
                }
             
                ExitPhysicalMemory();
             
                return TRUE;
            }

            補充說明一點,由于Windows虛擬內(nèi)存頁面大小默認(rèn)是4KB,NtMapViewOfSection()返回的虛擬空間基址是按照4KB對齊的,返回的

            本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/coffeemay/archive/2006/10/28/1354465.aspx

            posted @ 2011-04-29 11:00 沛沛 閱讀(718) | 評論 (0)編輯 收藏

            Debug和Release有什么區(qū)別?怎么把Debug轉(zhuǎn)成Release ?
            1。Debug和Release有什么區(qū)別,為什么要使用Release版本! 
            2。怎么把Debug轉(zhuǎn)成Release 

            轉(zhuǎn)載: 

            Debug版本包括調(diào)試信息,所以要比Release版本大很多(可能大數(shù)百K至數(shù)M)。至于是否需要DLL支持,主要看你采用的編譯選項。如果是基于ATL的,則Debug和Release版本對DLL的要求差不多。如果采用的編譯選項為使用MFC動態(tài)庫,則需要MFC42D.DLL等庫支持,而Release版本需要MFC42.DLL支持。Release   Build不對源代碼進行調(diào)試,不考慮MFC的診斷宏,使用的是MFC   Release庫,編譯十對應(yīng)用程序的速度進行優(yōu)化,而Debug   Build則正好相反,它允許對源代碼進行調(diào)試,可以定義和使用MFC的診斷宏,采用MFC   Debug庫,對速度沒有優(yōu)化。   


            一、Debug   和   Release   編譯方式的本質(zhì)區(qū)別 

            Debug   通常稱為調(diào)試版本,它包含調(diào)試信息,并且不作任何優(yōu)化,便于程序員調(diào)試程序。Release   稱為發(fā)布版本,它往往是進行了各種優(yōu)化,使得程序在代碼大小和運行速度上都是最優(yōu)的,以便用戶很好地使用。 
            Debug   和   Release   的真正秘密,在于一組編譯選項。下面列出了分別針對二者的選項(當(dāng)然除此之外還有其他一些,如/Fd   /Fo,但區(qū)別并不重要,通常他們也不會引起   Release   版錯誤,在此不討論) 

            Debug   版本: 
            /MDd   /MLd   或   /MTd   使用   Debug   runtime   library(調(diào)試版本的運行時刻函數(shù)庫) 
            /Od   關(guān)閉優(yōu)化開關(guān) 
            /D   "_DEBUG"   相當(dāng)于   #define   _DEBUG,打開編譯調(diào)試代碼開關(guān)(主要針對 
            assert函數(shù)) 
            /ZI   創(chuàng)建   Edit   and   continue(編輯繼續(xù))數(shù)據(jù)庫,這樣在調(diào)試過 
            程中如果修改了源代碼不需重新編譯 
            /GZ   可以幫助捕獲內(nèi)存錯誤 
            /Gm   打開最小化重鏈接開關(guān),減少鏈接時間 

            Release   版本:   
            /MD   /ML   或   /MT   使用發(fā)布版本的運行時刻函數(shù)庫 
            /O1   或   /O2   優(yōu)化開關(guān),使程序最小或最快 
            /D   "NDEBUG"   關(guān)閉條件編譯調(diào)試代碼開關(guān)(即不編譯assert函數(shù)) 
            /GF   合并重復(fù)的字符串,并將字符串常量放到只讀內(nèi)存,防止 
            被修改 

            實際上,Debug   和   Release   并沒有本質(zhì)的界限,他們只是一組編譯選項的集合,編譯器只是按照預(yù)定的選項行動。事實上,我們甚至可以修改這些選項,從而得到優(yōu)化過的調(diào)試版本或是帶跟蹤語句的發(fā)布版本。 

            二、哪些情況下   Release   版會出錯 

            有了上面的介紹,我們再來逐個對照這些選項看看   Release   版錯誤是怎樣產(chǎn)生的 

            1.   Runtime   Library:鏈接哪種運行時刻函數(shù)庫通常只對程序的性能產(chǎn)生影響。調(diào)試版本的   Runtime   Library   包含了調(diào)試信息,并采用了一些保護機制以幫助發(fā)現(xiàn)錯誤,因此性能不如發(fā)布版本。編譯器提供的   Runtime   Library   通常很穩(wěn)定,不會造成   Release   版錯誤;倒是由于   Debug   的   Runtime   Library   加強了對錯誤的檢測,如堆內(nèi)存分配,有時會出現(xiàn)   Debug   有錯但   Release   正常的現(xiàn)象。應(yīng)當(dāng)指出的是,如果   Debug   有錯,即使   Release   正常,程序肯定是有   Bug   的,只不過可能是   Release   版的某次運行沒有表現(xiàn)出來而已。 

            2.   優(yōu)化:這是造成錯誤的主要原因,因為關(guān)閉優(yōu)化時源程序基本上是直接翻譯的,而打開優(yōu)化后編譯器會作出一系列假設(shè)。這類錯誤主要有以下幾種: 

            (1)   幀指針(Frame   Pointer)省略(簡稱   FPO   ):在函數(shù)調(diào)用過程中,所有調(diào)用信息(返回地址、參數(shù))以及自動變量都是放在棧中的。若函數(shù)的聲明與實現(xiàn)不同(參數(shù)、返回值、調(diào)用方式),就會產(chǎn)生錯誤————但   Debug   方式下,棧的訪問通過   EBP   寄存器保存的地址實現(xiàn),如果沒有發(fā)生數(shù)組越界之類的錯誤(或是越界“不多”),函數(shù)通常能正常執(zhí)行;Release   方式下,優(yōu)化會省略   EBP   棧基址指針,這樣通過一個全局指針訪問棧就會造成返回地址錯誤是程序崩潰。C++   的強類型特性能檢查出大多數(shù)這樣的錯誤,但如果用了強制類型轉(zhuǎn)換,就不行了。你可以在   Release   版本中強制加入   /Oy-   編譯選項來關(guān)掉幀指針省略,以確定是否此類錯誤。此類錯誤通常有: 

            ●   MFC   消息響應(yīng)函數(shù)書寫錯誤。正確的應(yīng)為 
            afx_msg   LRESULT   OnMessageOwn(WPARAM   wparam,   LPARAM   lparam); 
            ON_MESSAGE   宏包含強制類型轉(zhuǎn)換。防止這種錯誤的方法之一是重定義   ON_MESSAGE   宏,把下列代碼加到   stdafx.h   中(在#include   "afxwin.h"之后),函數(shù)原形錯誤時編譯會報錯 
            #undef   ON_MESSAGE 
            #define   ON_MESSAGE(message,   memberFxn)   \ 
            {   message,   0,   0,   0,   AfxSig_lwl,   \ 
            (AFX_PMSG)(AFX_PMSGW)(static_cast<   LRESULT   (AFX_MSG_CALL   \ 
            CWnd::*)(WPARAM,   LPARAM)   >   (&memberFxn)   }, 

            (2)   volatile   型變量:volatile   告訴編譯器該變量可能被程序之外的未知方式修改(如系統(tǒng)、其他進程和線程)。優(yōu)化程序為了使程序性能提高,常把一些變量放在寄存器中(類似于   register   關(guān)鍵字),而其他進程只能對該變量所在的內(nèi)存進行修改,而寄存器中的值沒變。如果你的程序是多線程的,或者你發(fā)現(xiàn)某個變量的值與預(yù)期的不符而你確信已正確的設(shè)置了,則很可能遇到這樣的問題。這種錯誤有時會表現(xiàn)為程序在最快優(yōu)化出錯而最小優(yōu)化正常。把你認(rèn)為可疑的變量加上   volatile   試試。 

            (3)   變量優(yōu)化:優(yōu)化程序會根據(jù)變量的使用情況優(yōu)化變量。例如,函數(shù)中有一個未被使用的變量,在   Debug   版中它有可能掩蓋一個數(shù)組越界,而在   Release   版中,這個變量很可能被優(yōu)化調(diào),此時數(shù)組越界會破壞棧中有用的數(shù)據(jù)。當(dāng)然,實際的情況會比這復(fù)雜得多。與此有關(guān)的錯誤有: 
            ●   非法訪問,包括數(shù)組越界、指針錯誤等。例如 
            void   fn(void) 

            int   i; 
            i   =   1; 
            int   a[4]; 

            int   j; 
            j   =   1; 

            a[-1]   =   1;//當(dāng)然錯誤不會這么明顯,例如下標(biāo)是變量 
            a[4]   =   1; 

            j   雖然在數(shù)組越界時已出了作用域,但其空間并未收回,因而   i   和   j   就會掩蓋越界。而   Release   版由于   i、j   并未其很大作用可能會被優(yōu)化掉,從而使棧被破壞。 

            3.   _DEBUG   與   NDEBUG   :當(dāng)定義了   _DEBUG   時,assert()   函數(shù)會被編譯,而   NDEBUG   時不被編譯。除此之外,VC++中還有一系列斷言宏。這包括: 

            ANSI   C   斷言   void   assert(int   expression   ); 
            C   Runtime   Lib   斷言   _ASSERT(   booleanExpression   ); 
            _ASSERTE(   booleanExpression   ); 
            MFC   斷言   ASSERT(   booleanExpression   ); 
            VERIFY(   booleanExpression   ); 
            ASSERT_VALID(   pObject   ); 
            ASSERT_KINDOF(   classname,   pobject   ); 
            ATL   斷言   ATLASSERT(   booleanExpression   ); 
            此外,TRACE()   宏的編譯也受   _DEBUG   控制。 

            所有這些斷言都只在   Debug版中才被編譯,而在   Release   版中被忽略。唯一的例外是   VERIFY()   。事實上,這些宏都是調(diào)用了   assert()   函數(shù),只不過附加了一些與庫有關(guān)的調(diào)試代碼。如果你在這些宏中加入了任何程序代碼,而不只是布爾表達式(例如賦值、能改變變量值的函數(shù)調(diào)用   等),那么   Release   版都不會執(zhí)行這些操作,從而造成錯誤。初學(xué)者很容易犯這類錯誤,查找的方法也很簡單,因為這些宏都已在上面列出,只要利用   VC++   的   Find   in   Files   功能在工程所有文件中找到用這些宏的地方再一一檢查即可。另外,有些高手可能還會加入   #ifdef   _DEBUG   之類的條件編譯,也要注意一下。 
            順便值得一提的是   VERIFY()   宏,這個宏允許你將程序代碼放在布爾表達式里。這個宏通常用來檢查   Windows   API   的返回值。有些人可能為這個原因而濫用   VERIFY()   ,事實上這是危險的,因為   VERIFY()   違反了斷言的思想,不能使程序代碼和調(diào)試代碼完全分離,最終可能會帶來很多麻煩。因此,專家們建議盡量少用這個宏。 

            4.   /GZ   選項:這個選項會做以下這些事 

            (1)   初始化內(nèi)存和變量。包括用   0xCC   初始化所有自動變量,0xCD   (   Cleared   Data   )   初始化堆中分配的內(nèi)存(即動態(tài)分配的內(nèi)存,例如   new   ),0xDD   (   Dead   Data   )   填充已被釋放的堆內(nèi)存(例如   delete   ),0xFD(   deFencde   Data   )   初始化受保護的內(nèi)存(debug   版在動態(tài)分配內(nèi)存的前后加入保護內(nèi)存以防止越界訪問),其中括號中的詞是微軟建議的助記詞。這樣做的好處是這些值都很大,作為指針是不可能的(而且   32   位系統(tǒng)中指針很少是奇數(shù)值,在有些系統(tǒng)中奇數(shù)的指針會產(chǎn)生運行時錯誤),作為數(shù)值也很少遇到,而且這些值也很容易辨認(rèn),因此這很有利于在   Debug   版中發(fā)現(xiàn)   Release   版才會遇到的錯誤。要特別注意的是,很多人認(rèn)為編譯器會用   0   來初始化變量,這是錯誤的(而且這樣很不利于查找錯誤)。 
            (2)   通過函數(shù)指針調(diào)用函數(shù)時,會通過檢查棧指針驗證函數(shù)調(diào)用的匹配性。(防止原形不匹配) 
            (3)   函數(shù)返回前檢查棧指針,確認(rèn)未被修改。(防止越界訪問和原形不匹配,與第二項合在一起可大致模擬幀指針省略   FPO   ) 

            通常   /GZ   選項會造成   Debug   版出錯而   Release   版正常的現(xiàn)象,因為   Release   版中未初始化的變量是隨機的,這有可能使指針指向一個有效地址而掩蓋了非法訪問。 

            除此之外,/Gm   /GF   等選項造成錯誤的情況比較少,而且他們的效果顯而易見,比較容易發(fā)現(xiàn)。 
            -------------------------------------------------------------- 
            Release是發(fā)行版本,比Debug版本有一些優(yōu)化,文件比Debug文件小 
            Debug是調(diào)試版本,包括的程序信息更多 
            Release方法: 
            build->batch   build->build就OK. 

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

            一、"Debug是調(diào)試版本,包括的程序信息更多" 

            補充:只有DEBUG版的程序才能設(shè)置斷點、單步執(zhí)行、使用TRACE/ASSERT等調(diào)試輸出語句。REALEASE不包含任何調(diào)試信息,所以體積小、運行速度快。 

            二、一般發(fā)布release的方法除了hzh_shat(水)   所說的之外,還可以project->Set   Active   Config,選中release版本。此后,按F5或F7編譯所得的結(jié)果就是release版本。


            Trackback: http://tb.donews.net/TrackBack.aspx?PostId=131016

            ----------------------------------------------------------------------------------------
            -------------------------------------------------------------------------------------------
            VC下關(guān)于debug和release的不同的討論
            在使用VC開發(fā)軟件的過程中,正當(dāng)要享受那種興奮的時候突然發(fā)現(xiàn):release與debug運行結(jié)果不一致,甚至出錯,而release又不方便調(diào)試,真的是當(dāng)頭一棒啊,可是疼歸疼,問題總要解決,下面將講述一下我的幾點經(jīng)驗,看看是不是其中之一:

            1. 變量。
            大家都知道,debug跟release在初始化變量時所做的操作是不同的,debug是將每個字節(jié)位都賦成0xcc(注1),而release的賦值近似于隨機(我想是直接從內(nèi)存中分配的,沒有初始化過)。這樣就明確了,如果你的程序中的某個變量沒被初始化就被引用,就很有可能出現(xiàn)異常:用作控制變量將導(dǎo)致流程導(dǎo)向不一致;用作數(shù)組下標(biāo)將會使程序崩潰;更加可能是造成其他變量的不準(zhǔn)確而引起其他的錯誤。所以在聲明變量后馬上對其初始化一個默認(rèn)的值是最簡單有效的辦法,否則項目大了你找都沒地方找。代碼存在錯誤在debug方式下可能會忽略而不被察覺到,如debug方式下數(shù)組越界也大多不會出錯,在release中就暴露出來了,這個找起來就比較難了:( 還是自己多加注意吧

            2. 自定義消息的消息參數(shù)。
            MFC為我們提供了很好的消息機制,更增加了自定義消息,好處我就不用多說了。這也存在debug跟release的問題嗎?答案是肯定的。在自定義消息的函數(shù)體聲明時,時常會看到這樣的寫法:afx_msg LRESULT OnMessageOwn(); Debug情況下一般不會有任何問題,而當(dāng)你在Release下且多線程或進程間使用了消息傳遞時就會導(dǎo)致無效句柄之類的錯誤。導(dǎo)致這個錯誤直接原因是消息體的參數(shù)沒有添加,即應(yīng)該寫成:afx_msg LRESULT OnMessageOwn(WPARAM wparam, LPARAM lparam); (注2)

            3. release模式下不出錯,但debug模式下報錯。
            這種情況下大多也是因為代碼書寫不正確引起的,查看MFC的源碼,可以發(fā)現(xiàn)好多ASSERT的語句(斷言),這個宏只是在debug模式下才有效,那么就清楚了,release版不報錯是忽略了錯誤而不是沒有錯誤,這可能存在很大的隱患,因為是Debug模式下,比較方便調(diào)試,好好的檢查自己的代碼,再此就不多說了。

            4. ASSERT, VERIFY, TRACE……….調(diào)試宏
            這種情況很容易解釋。舉個例子:請在VC下輸入ASSERT然后選中按F12跳到宏定義的地方,這里你就能夠發(fā)現(xiàn)Debug中ASSERT要執(zhí)行AfxAssertFailedLine,而Release下的宏定義卻為”#define ASSERT(f) ((void)0)”。所以注意在這些調(diào)試宏的語句不要用程序相關(guān)變量如i++寫操作的語句。VERIFY是個例外,”#define VERIFY(f) ((void)(f))”,即執(zhí)行,這里的作用就不多追究了,有興趣可自己研究:)。

            總結(jié):
            Debug與Release不同的問題在剛開始編寫代碼時會經(jīng)常發(fā)生,99%是因為你的代碼書寫錯誤而導(dǎo)致的,所以不要動不動就說系統(tǒng)問題或編譯器問題,努力找找自己的原因才是根本。我從前就常常遇到這情況,經(jīng)歷過一次次的教訓(xùn)后我就開始注意了,現(xiàn)在我所寫過的代碼我已經(jīng)好久沒遇到這種問題了。下面是幾個避免的方面,即使沒有這種問題也應(yīng)注意一下:

            1. 注意變量的初始化,尤其是指針變量,數(shù)組變量的初始化(很大的情況下另作考慮了)。
            2. 自定義消息及其他聲明的標(biāo)準(zhǔn)寫法
            3. 使用調(diào)試宏時使用后最好注釋掉
            4. 盡量使用try - catch(…)
            5. 盡量使用模塊,不但表達清楚而且方便調(diào)試。
            注1:
            afc(afc) 網(wǎng)友提供:
            debug版初始化成0xcc是因為0xcc在x86下是一條int 3單步中斷指令,這樣程序如果跑飛了遇到0xcc就會停下來,這和單片機編程時一般將沒用的代碼空間填入jmp 0000語句是一樣地
            注2:
            不知大家有沒有遇到過這種情況,具體原因我也不太清楚,是不是調(diào)用時按著默認(rèn)的參數(shù)多分配了WPARAM+LPARAM的空間而破壞了應(yīng)用程序的內(nèi)存空間?還請高手來補充。
            NightWolf 網(wǎng)友提供:我遇見過,好像是在函數(shù)調(diào)用的時候參數(shù)入棧的問題。因為MFC的消息使用宏寫的,所以如果定義了OnMessage()的函數(shù),編譯能夠通過,但是調(diào)用一次后,堆棧指針發(fā)生了偏移。然后就。。。
            ------------------------------------------------------------
            _________________________________________________________
            ---------------------------------------------------------
            DEBUG和RELEASE 版本差異及調(diào)試相關(guān)問題:

            I.          內(nèi)存分配問題

            1.           變量未初始化。下面的程序在debug中運行的很好。

                   thing * search(thing * something)
                     BOOL found;
                     for(int i = 0; i < whatever.GetSize(); i++)
                       {
                       if(whatever[i]->field == something->field)
                          { /* found it */
                           found = TRUE;
                           break;
                          } /* found it */
                        }
                 if(found)
                          return whatever[i];
                 else
                          return NULL;
            而在release中卻不行,因為debug中會自動給變量初始化found=FALSE,而在release版中則不會。所以盡可能的給變量、類或結(jié)構(gòu)初始化。

            2.             數(shù)據(jù)溢出的問題 

                     如:char buffer[10];
                          int counter;

                    lstrcpy(buffer, "abcdefghik");

            在debug版中buffer的NULL覆蓋了counter的高位,但是除非counter>16M,什么問題也沒有。但是在release版中,counter可能被放在寄存器中,這樣NULL就覆蓋了buffer下面的空間,可能就是函數(shù)的返回地址,這將導(dǎo)致ACCESS ERROR。

            3.          DEBUG版和RELEASE版的內(nèi)存分配方式是不同的 。如果你在DEBUG版中申請    ele 為 6*sizeof(DWORD)=24bytes,實際上分配給你的是32bytes(debug版以32bytes為單位分配), 而在release版,分配給你的就是24bytes(release版以8bytes為單位),所以在debug版中如果你寫ele[6],可能不會有什么問題,而在release版中,就有ACCESS VIOLATE。

            II.       ASSERT和VERIFY

            1.          ASSERT在Release版本中是不會被編譯的。

            ASSERT宏是這樣定義的

                     #ifdef _DEBUG
                     #define ASSERT(x) if( (x) == 0) report_assert_failure()
                     #else
                     #define ASSERT(x)
                     #endif
                     實際上復(fù)雜一些,但無關(guān)緊要。假如你在這些語句中加了程序中必須要有的代碼
            比如

            ASSERT(pNewObj = new CMyClass);

            pNewObj->MyFunction();

            這種時候Release版本中的pNewObj不會分配到空間

            所以執(zhí)行到下一個語句的時候程序會報該程序執(zhí)行了非法操作的錯誤。這時可以用VERIFY :

                     #ifdef _DEBUG
                     #define VERIFY(x) if( (x) == 0) report_assert_failure()
                 #else
                     #define VERIFY(x) (x)
                     #endif
            這樣的話,代碼在release版中就可以執(zhí)行了。

            III.    參數(shù)問題:

            自定義消息的處理函數(shù),必須定義如下:

            afx_msg LRESULT OnMyMessage(WPARAM, LPARAM);

            返回值必須是HRESULT型,否則Debug會過,而Release出錯

            IV.   內(nèi)存分配

            保證數(shù)據(jù)創(chuàng)建和清除的統(tǒng)一性:如果一個DLL提供一個能夠創(chuàng)建數(shù)據(jù)的函數(shù),那么這個DLL同時應(yīng)該提供一個函數(shù)銷毀這些數(shù)據(jù)。數(shù)據(jù)的創(chuàng)建和清除應(yīng)該在同一個層次上。

            V.      DLL的災(zāi)難

            人們將不同版本DLL混合造成的不一致性形象的稱為 “動態(tài)連接庫的地獄“(DLL Hell) ,甚至微軟自己也這么說(http://msdn.microsoft.com/library/techart/dlldanger1.htm)。

                    如果你的程序使用你自己的DLL時請注意:

            1.        不能將debug和release版的DLL混合在一起使用。debug都是debug版,release版都是release版。

            解決辦法是將debug和release的程序分別放在主程序的debug和release目錄下


            2.          千萬不要以為靜態(tài)連接庫會解決問題,那只會使情況更糟糕。

            VI.   RELEASE板中的調(diào)試 :

            1.          將ASSERT() 改為 VERIFY() 。找出定義在"#ifdef _DEBUG"中的代碼,如果在RELEASE版本中需要這些代碼請將他們移到定義外。查找TRACE(...)中代碼,因為這些代碼在RELEASE中也不被編譯。 請認(rèn)真檢查那些在RELEASE中需要的代碼是否并沒有被便宜。

            2.          變量的初始化所帶來的不同,在不同的系統(tǒng),或是在DEBUG/RELEASE版本間都存在這樣的差異,所以請對變量進行初始化。

            3.          是否在編譯時已經(jīng)有了警告?請將警告級別設(shè)置為3或4,然后保證在編譯時沒有警告出現(xiàn).

            VII.    將Project Settings" 中 "C++/C " 項目下優(yōu)化選項改為Disbale(Debug)。編譯器的優(yōu)化可能導(dǎo)致許多意想不到的錯誤,請參考http://www.pgh.net/~newcomer/debug_release.htm

            1.          此外對RELEASE版本的軟件也可以進行調(diào)試,請做如下改動:

            在"Project Settings" 中 "C++/C " 項目下設(shè)置 "category" 為 "General" 并且將"Debug Info"設(shè)置為 "Program Database"。

            在"Link"項目下選中"Generate Debug Info"檢查框。

            "Rebuild All"

            如此做法會產(chǎn)生的一些限制:

            無法獲得在MFC DLL中的變量的值。

            必須對該軟件所使用的所有DLL工程都進行改動。

            另:

            MS BUG:MS的一份技術(shù)文檔中表明,在VC5中對于DLL的"Maximize Speed"優(yōu)化選項并未被完全支持,因此這將會引起內(nèi)存錯誤并導(dǎo)致程序崩潰。

            2.         www.sysinternals.com有一個程序DebugView,用來捕捉OutputDebugString的輸出,運行起來后(估計是自設(shè)為system debugger)就可以觀看所有程序的OutputDebugString的輸出。此后,你可以脫離VC來運行你的程序并觀看調(diào)試信息。

            3.          有一個叫Gimpel Lint的靜態(tài)代碼檢查工具,據(jù)說比較好用。

             

            本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/Jiao2_vc/archive/2009/03/07/3967364.aspx

            posted @ 2011-04-29 10:48 沛沛 閱讀(354) | 評論 (0)編輯 收藏


                _stdcall將參數(shù)壓棧是按C語言的順序(從右到左),但與C 語言不同的是它是由被調(diào)用者將參數(shù)從棧中清除的,所以它的編譯文件比_cdecl小。_stdcall是Windows API函數(shù)中默認(rèn)的調(diào)用約定,VB、VFP等也采用這個約定。 
                _cdecl是C語言采用的默認(rèn)調(diào)用方法,它的優(yōu)點是支持printf這樣的可變參數(shù)調(diào)用。 
                另外,VC++對于兩種調(diào)用方法的名稱轉(zhuǎn)換方法也不同。

             在C語言中,假設(shè)我們有這樣的一個函數(shù):
              
              int function(int a,int b)
              
              調(diào)用時只要用result = function(1,2)這樣的方式就可以使用這個函數(shù)。但是,當(dāng)高級語言被編譯成計算機可以識別的機器碼時,有一個問題就凸現(xiàn)出來:在CPU中,計算機沒有辦法知道一個函數(shù)調(diào)用需要多少個、什么樣的參數(shù),也沒有硬件可以保存這些參數(shù)。也就是說,計算機不知道怎么給這個函數(shù)傳遞參數(shù),傳遞參數(shù)的工作必須由函數(shù)調(diào)用者和函數(shù)本身來協(xié)調(diào)。為此,計算機提供了一種被稱為棧的數(shù)據(jù)結(jié)構(gòu)來支持參數(shù)傳遞。

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

              函數(shù)調(diào)用時,調(diào)用者依次把參數(shù)壓棧,然后調(diào)用函數(shù),函數(shù)被調(diào)用以后,在堆棧中取得數(shù)據(jù),并進行計算。函數(shù)計算結(jié)束以后,或者調(diào)用者、或者函數(shù)本身修改堆棧,使堆棧恢復(fù)原裝。

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

              stdcall 
              cdecl 
              fastcall 
              thiscall 
              naked call

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

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

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

              push 2        第二個參數(shù)入棧
              push 1        第一個參數(shù)入棧
              call function    調(diào)用參數(shù),注意此時自動把cs:eip入棧

              而對于函數(shù)自身,則可以翻譯為: 
              push ebp       保存ebp寄存器,該寄存器將用來保存堆棧的棧頂指針,可以在函數(shù)退出時恢復(fù)
              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    恢復(fù)esp
              pop ebp
              ret 8

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

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

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

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

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

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

              調(diào)用處
              push 1
              push 2
              call function
              add esp, 8     注意:這里調(diào)用者在恢復(fù)堆棧

              被調(diào)用函數(shù)_function處
              push ebp       保存ebp寄存器,該寄存器將用來保存堆棧的棧頂指針,可以在函數(shù)退出時恢復(fù)
              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     恢復(fù)esp
              pop ebp
              ret         注意,這里沒有修改堆棧

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

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

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

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

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

              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函數(shù)被翻譯成匯編后就變成: 
              //函數(shù)function1調(diào)用
              00401C1D  push    2
              00401C1F  push    1
              00401C21  lea     ecx,[ebp-8]
              00401C24  call    function1     注意,這里this沒有被入棧

              //函數(shù)function2調(diào)用
              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
              
              可見,對于參數(shù)個數(shù)固定情況下,它類似于stdcall,不定時則類似cdecl

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

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

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

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

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

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

              至于這種函數(shù)被調(diào)用,則和普通的cdecl及stdcall調(diào)用函數(shù)一致。

              函數(shù)調(diào)用約定導(dǎo)致的常見問題
              如果定義的約定和使用的約定不一致,則將導(dǎo)致堆棧被破壞,導(dǎo)致嚴(yán)重問題,下面是兩種常見的問題:

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

              __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(...)//這里修改了調(diào)用約定
              result = func(1,2);//導(dǎo)致錯誤

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

            1)調(diào)用約定(Calling convention)

                決定函數(shù)參數(shù)傳送時入棧和出棧的順序,由調(diào)用者還是被調(diào)用者把參數(shù)彈出棧,以及編譯器用來識別函數(shù)名字的修飾約定。函數(shù)調(diào)用約定有多種:

            1、__stdcall調(diào)用約定

                相當(dāng)于16位動態(tài)庫中經(jīng)常使用的 PASCAL 調(diào)用約定。在32位的 VC++5.0 中PASCAL 調(diào)用約定不再被支持(實際上它已被定義為__stdcall。函數(shù)的參數(shù)自右向左通過棧傳遞,被調(diào)用的函數(shù)在返回前清理傳送參數(shù)的內(nèi)存棧。

                _stdcall 是 Pascal 程序的缺省調(diào)用方式,通常用于 Win32 API 中,函數(shù)采用從右到左的壓棧方式,自己在退出時清空堆棧。VC 將函數(shù)編譯后會在函數(shù)名前面加上下劃線前綴,在函數(shù)名后加上 "@" 和參數(shù)的字節(jié)數(shù)。

            2、C 調(diào)用約定(即用__cdecl)

                按從右至左的順序壓參數(shù)入棧,由調(diào)用者把參數(shù)彈出棧。對于傳送參數(shù)的內(nèi)存棧是由調(diào)用者來維護的(正因為如此,實現(xiàn)可變參數(shù)的函數(shù)只能使用該調(diào)用約定)。

                _cdecl 是 C 和 C++ 程序缺省的調(diào)用方式。每一個調(diào)用它的函數(shù)都包含清空堆棧的代碼,所以產(chǎn)生的可執(zhí)行文件大小會比調(diào)用 _stdcall 函數(shù)的大。函數(shù)采用從右到左的壓棧方式。VC 將函數(shù)編譯后會在函數(shù)名前面加上下劃線前綴。 它是 MFC 缺省調(diào)用約定。

            3、__fastcall 調(diào)用約定

                "人" 如其名,它的主要特點就是快,因為它是通過寄存器來傳送參數(shù)的(實際上,它用 ECX 和 EDX 傳送前兩個雙字(DWORD)或更小的參數(shù),剩下的參數(shù)仍舊自右向左壓棧傳送,被調(diào)用的函數(shù)在返回前清理傳送參數(shù)的內(nèi)存棧)。

                _fastcall方式的函數(shù)采用寄存器傳遞參數(shù),VC 將函數(shù)編譯后會在函數(shù)名前面加上"@"前綴,在函數(shù)名后加上"@"和參數(shù)的字節(jié)數(shù)。

            4、thiscall調(diào)用約定

                僅僅應(yīng)用于 "C++" 成員函數(shù)。this 指針存放于 CX 寄存器,參數(shù)從右到左壓。thiscall 不是關(guān)鍵詞,因此不能被程序員指定。

            5、naked call調(diào)用約定

                采用 1-4 的調(diào)用約定時,如果必要的話,進入函數(shù)時編譯器會產(chǎn)生代碼來保存ESI,EDI,EBX,EBP寄存器,退出函數(shù)時則產(chǎn)生代碼恢復(fù)這些寄存器的內(nèi)容。

                naked call不產(chǎn)生這樣的代碼。naked call不是類型修飾符,故必須和_declspec 共同使用。

                關(guān)鍵字 __stdcall、__cdecl 和 __fastcall 可以直接加在要輸出的函數(shù)前,也可以在編譯環(huán)境的 Setting...\C/C++ \Code Generation 項選擇。當(dāng)加在輸出函數(shù)前的關(guān)鍵字與編譯環(huán)境中的選擇不同時,直接加在輸出函數(shù)前的關(guān)鍵字有效。它們對應(yīng)的命令行參數(shù)分別為/Gz、/Gd 和 /Gr。缺省狀態(tài)為/Gd,即__cdecl。


            2)名字修飾約定

            1、修飾名(Decoration name)

                "C" 或者 "C++" 函數(shù)在內(nèi)部(編譯和鏈接)通過修飾名識別。修飾名是編譯器在編譯函數(shù)定義或者原型時生成的字符串。有些情況下使用函數(shù)的修飾名是必要的,如在模塊定義文件里頭指定輸出"C++"重載函數(shù)、構(gòu)造函數(shù)、析構(gòu)函數(shù),又如在匯編代碼里調(diào)用"C""或"C++"函數(shù)等。修飾名由函數(shù)名、類名、調(diào)用約定、返回類型、參數(shù)等共同決定。

            2、函數(shù)名修飾約定隨編譯種類(C或C++)和調(diào)用約定的不同而不同,下面分別說明。

            a、C編譯時函數(shù)名修飾約定規(guī)則:

            __stdcall調(diào)用約定:

                在輸出函數(shù)名前加上一個下劃線前綴,后面加上一個"@"符號和其參數(shù)的字節(jié)數(shù),格式為 _functionname@number

            __cdecl調(diào)用約定:

                僅在輸出函數(shù)名前加上一個下劃線前綴,格式為 _functionname。

            __fastcall調(diào)用約定:

                在輸出函數(shù)名前加上一個"@"符號,后面也是一個"@"符號和其參數(shù)的字節(jié)數(shù),格式為@functionname@number。

            它們均不改變輸出函數(shù)名中的字符大小寫,這和PASCAL調(diào)用約定不同,PASCAL約定輸出的函數(shù)名無任何修飾且全部大寫。

            b、C++編譯時函數(shù)名修飾約定規(guī)則:

            __stdcall調(diào)用約定:

            以"?"標(biāo)識函數(shù)名的開始,后跟函數(shù)名;
            函數(shù)名后面以"@@YG"標(biāo)識參數(shù)表的開始,后跟參數(shù)表;
            參數(shù)表以代號表示:
                X——void,

                D——char,

                E——unsigned char,

                F——short,

                H——int,

                I——unsigned int,

                J——long,

                K——unsigned long,

                M——float,

                N——double,

                _N——bool,

                ....

                PA——表示指針,后面的代號表明指針類型,如果相同類型的指針連    續(xù)出現(xiàn),以"0"代替,一個"0"代表一次重復(fù);


            參數(shù)表的第一項為該函數(shù)的返回值類型,其后依次為參數(shù)的數(shù)據(jù)類型,指針標(biāo)識在其所指數(shù)據(jù)類型前;
            參數(shù)表后以"@Z"標(biāo)識整個名字的結(jié)束,如果該函數(shù)無參數(shù),則以"Z"標(biāo)識結(jié)束。其格式為
                "?functionname@@YG*****@Z"或

                "?functionname@@YG*XZ",

                例如

                int Test1(char *var1,unsigned long)    -----“?Test1@@YGHPADK@Z
                void Test2()                    -----“?Test2@@YGXXZ

            __cdecl調(diào)用約定:

                規(guī)則同上面的_stdcall調(diào)用約定,只是參數(shù)表的開始標(biāo)識由上面的"@@YG"變?yōu)?@@YA"。VC++對函數(shù)的省缺聲明是"__cedcl",將只能被C/C++調(diào)用。

            __fastcall調(diào)用約定:

                規(guī)則同上面的_stdcall調(diào)用約定,只是參數(shù)表的開始標(biāo)識由上面的"@@YG"變?yōu)?@@YI"。


            文章出處:http://www.diybl.com/course/3_program/c++/cppjs/2008617/126024.html

            posted @ 2011-04-29 10:45 沛沛 閱讀(338) | 評論 (0)編輯 收藏

            僅列出標(biāo)題
            共7頁: 1 2 3 4 5 6 7 
            99久久夜色精品国产网站| 国产亚洲综合久久系列| 久久久久九九精品影院| 久久久久黑人强伦姧人妻| 午夜精品久久久久久影视riav| 中文字幕久久精品无码| 99久久国产综合精品网成人影院 | 伊人久久大香线蕉AV色婷婷色| 久久精品国产久精国产思思| 国产精品永久久久久久久久久| 久久久久久精品成人免费图片| a高清免费毛片久久| 亚洲伊人久久综合中文成人网| 狠狠色婷婷久久一区二区三区| 亚洲AⅤ优女AV综合久久久| .精品久久久麻豆国产精品| 欧美精品一区二区久久| 久久国产精品久久久| 性欧美丰满熟妇XXXX性久久久 | 久久精品国产男包| 99热热久久这里只有精品68| 久久嫩草影院免费看夜色| 久久午夜伦鲁片免费无码| 亚洲?V乱码久久精品蜜桃 | 国产精品99久久精品爆乳| 久久Av无码精品人妻系列| 欧美久久久久久| 亚洲国产小视频精品久久久三级| 亚洲午夜久久久精品影院 | 精品久久久久香蕉网| 久久精品一区二区三区AV| 色综合久久中文字幕综合网| 国产伊人久久| 精品久久人人爽天天玩人人妻| 色噜噜狠狠先锋影音久久| 久久精品国产99国产精品澳门 | 99久久婷婷免费国产综合精品| 色婷婷综合久久久久中文一区二区 | 亚洲精品tv久久久久久久久久| 国产三级精品久久| 国产精品午夜久久|