• <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++博客 :: 首頁 :: 聯系 :: 聚合  :: 管理
              62 Posts :: 0 Stories :: 5 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(1)

            我參與的團隊

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            #

            題目:二叉樹的結點定義如下:

            struct TreeNode

            {

                    int m_nValue;

                    TreeNode* m_pLeft;

                    TreeNode* m_pRight;

            };

            輸入兩棵二叉樹AB,判斷樹B是不是A的子結構。

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

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

            分析:這是2010年微軟校園招聘時的一道題目。二叉樹一直是微軟面試題中經常出現的數據結構。對微軟有興趣的讀者一定要重點關注二叉樹。

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

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

            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;

            }

            在上述代碼中,我們遞歸調用hasSubtreeCore遍歷二叉樹A。如果發現某一結點的值和樹B的頭結點的值相同,則調用DoesTree1HaveAllNodeOfTree2,做第二步判斷。

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

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

            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);

            }

             

              博主何海濤對本博客文章享有版權。網絡轉載請注明出處http://zhedahht.blog.163.com/

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

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

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

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

             

            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/coffeemay/archive/2006/04/17/666213.aspx

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

            Socket(套接字)

            ◆先看定義:
            typedef unsigned int u_int;
            typedef u_int SOCKET;◆Socket相當于進行網絡通信兩端的插座,只要對方的Socket和自己的Socket有通信聯接,雙方就可以發送和接收數據了。其定義類似于文件句柄的定義。

            ◆Socket有五種不同的類型:

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

            #define SOCK_STREAM 1 流式套接字提供了雙向、有序的、無重復的以及無記錄邊界的數據流服務,適合處理大量數據。它是面向聯結的,必須建立數據傳輸鏈路,同時還必須對傳輸的數據進行驗證,確保數據的準確性。因此,系統開銷較大。

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

            定義:

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

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

            定義:

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

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

            頭文件:Winsock2.h

            庫文件:WS2_32.LIB

            動態庫:W32_32.DLL

             一些重要的定義

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

            typedef unsigned char u_char;
            typedef unsigned short u_short;
            typedef unsigned int u_int;
            typedef unsigned long u_long;2、 網絡地址的數據結構,有一個老的和一個新的的,請大家留意,如果想知道為什么,
            請發郵件給Bill Gate。其實就是計算機的IP地址,不過一般不用用點分開的IP地
            址,當然也提供一些轉換函數。

            ◆ 舊的網絡地址結構的定義,為一個4字節的聯合:

            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 */
            //下面幾行省略,反正沒什么用處。
            };其實完全不用這么麻煩,請看下面:

            ◆ 新的網絡地址結構的定義:
            非常簡單,就是一個無符號長整數 unsigned long。舉個例子:IP地址為127.0.0.1的網絡地址是什么呢?請看定義:

            #define INADDR_LOOPBACK 0x7f0000013、 套接字地址結構

            (1)、sockaddr結構:

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

            (2)、sockaddr_in結構

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

            ◆ 將常用的用點分開的IP地址轉換為unsigned long類型的IP地址的函數:

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

            unsigned long addr=inet_addr("192.1.8.84")◆ 如果將sin_addr設置為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地址,如果該主機有多個網卡,就包括地址的列表。另外還有幾個類似的結構,這里就不一一介紹了。

            5、 常見TCP/IP協議的定義:

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

             套接字的屬性

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

            1、 屬性內容:

            //允許調試輸出
            #define SO_DEBUG 0x0001 /* turn on debugging info recording */
            //是否監聽模式
            #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 */
            //設置為廣播
            #define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */
            //使用環回不通過硬件
            #define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */
            //當前拖延值
            #define SO_LINGER 0x0080 /* linger on close if data present */
            //是否加入帶外數據
            #define SO_OOBINLINE 0x0100 /* leave received OOB data in line */
            //禁用LINGER選項
            #define SO_DONTLINGER (int)(~SO_LINGER)
            //發送緩沖區長度
            #define SO_SNDBUF 0x1001 /* send buffer size */
            //接收緩沖區長度
            #define SO_RCVBUF 0x1002 /* receive buffer size */
            //發送超時時間
            #define SO_SNDTIMEO 0x1005 /* send timeout */
            //接收超時時間
            #define SO_RCVTIMEO 0x1006 /* receive timeout */
            //錯誤狀態
            #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為套接字選項的級別,大多數是特定協議和套接字專有的。如IP協議應為 IPPROTO_IP。

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

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

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

            用法:

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

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

            int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData )

            wVersionRequested為打算加載Winsock的版本,一般如下設置:
            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;如果加載成功后數據為:

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

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

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

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

            int bind( SOCKET s, const struct sockaddr FAR * name, int namelen )
            s為已經創建的套接字。
            name為socket地址結構,為sockaddr結構,如前面討論的,我們一般使用sockaddr_in
            結構,在使用再強制轉換為sockaddr結構。
            namelen為地址結構的長度。
            用法:

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

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

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

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

            用法:

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

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

            用法:

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

            int send(SOCKET s, const char FAR * buf, int len, int flags )s為服務器端監聽的套接字。
            buf為欲發送數據緩沖區的指針。
            len為發送數據緩沖區的長度。
            flags為數據發送標記。
            返回值為發送數據的字符數。

            ◆這里講一下這個發送標記,下面8中討論的接收標記也一樣:

            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表示數據應該帶外發送,所謂帶外數據就是TCP緊急數據。
            MSG_PEEK表示使有用的數據復制到緩沖區內,但并不從系統緩沖區內刪除。
            MSG_DONTROUTE表示不要將包路由出去。

            用法:

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

            int recv( SOCKET s, char FAR * buf, int len, int flags )s為準備接收數據的套接字。
            buf為準備接收數據的緩沖區。
            len為準備接收數據緩沖區的大小。
            flags為數據接收標記。
            返回值為接收的數據的字符數。

            用法:

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

            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、 關閉套接字:釋放所占有的資源。(服務器端和客戶端)

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

            用法:

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

            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/coffeemay/archive/2006/08/05/1023149.aspx

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

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

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

            Windows XP 與 Windows Server 2003 不需要當地語系化版本的 Symbol 來 Debug 當地語系化版本的產品。每一個 Windows XP 與 Windows Server™ 2003 Symbol 下載封裝皆可用於 Debugging 所有已當地語系化的版本。

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

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

            檢視用於 Symbol 封裝的下載連結

               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。若您己經安裝了 Windows Server 2003 的 Symbol,您可以將它安裝在相同的路徑,這樣您就擁有了含有 Service Pack 1 的 Windows Server 2003 完整 Symbol 套件。

            Windows Server 2003 Service Pack 1 x86 retail symbols, all languages (檔案大小:130 MB - 此為大多數客戶所需要的封裝。)
            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 - 此為大多數客戶所需要的封裝。)
            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 - 此為大多數客戶所需要的封裝。)
            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。若您己經安裝了 Windows XP 的 Symbol,您可以將它安裝在相同的路徑,這樣您就擁有了含有 Service Pack 2 的 Windows XP 完整 Symbol 套件。

             

            Windows XP Service Pack 2 x86 retail symbols, all languages (檔案大小:145 MB - 此為大多數客戶所需要的封裝。)
            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 - 此為大多數客戶所需要的封裝。)
            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。若您己經安裝了 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 - 此為大多數客戶所需要的封裝。)
            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
             
              下列連結連接至每一個 Symbols 封裝的下載處,或是帶領您至提供關於 Windows 2000 Symbol 下載資訊的網站。

            Windows 2000 SP4 的更新彙總套件 1 (Update Rollup 1)。欲安裝 Windows 2000 SP4 的更新彙總套件 1 中所包括的新增 Symbols,請在下方的下拉式選單中選擇您想要下載的語言版本然後在 "按一下這裡開始下載" 的連結按一下。欲擁有完整的 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,請在下面的下拉式表單中選擇您想要下載的語言然後按一下 "按一下這裡開始下載" 的連結項目。欲擁有完整的 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 資訊,那您應該下載用於 Checked 版本的 Symbol。下載英文版的 Service Pack 4 Checked 版本,請按一下這裡 (下載量: 66 MB)。

            Windows 2000 Service Pack 3。安裝 Service Pack 3 所提供的額外 Symbol,請在下面的下拉式表單中選擇您想要下載的語言然後按一下 "按一下這裡開始下載" 的連結項目。欲擁有完整的 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 資訊,那您應該下載用於 Checked 版本的 Symbol。下載英文版的 Service Pack 3 Checked 版本,請按一下此處。

            Windows 2000 Service Pack 2 Security Rollup Package 1。安裝 Service Pack 2 Security Rollup Package 1 所提供的額外 Symbol,請在下面的下拉式表單中選擇您想要下載的語言然後按一下 "按一下這裡開始下載" 的連結項目。欲擁有完整的 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 問題,請提送線上說明檔的要求或是使用附加的資源於 DDK 開發人員支援。

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

            Last Updated: 2006 年 9 月 13 日


            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/coffeemay/archive/2006/10/12/1331356.aspx

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

            1、 在介紹靜態對象、全局對象與程序的運行機制之間的關系之前,我們首先看一下atexit函數。

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

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

            舉例如下(代碼摘自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.
            注冊函數的順序為:fn1、fn2、fn3、fn4,但是調用順序為fn4、fn3、fn2、fn1。

            2、理解了atexit函數之后,我們就可以來看看局部靜態對象了。

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


            在調試狀態下,匯編代碼如下(請觀察藍色標記出來的代碼):
            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]語句很明顯的調用AAA的構造函數。
            [2]語句將442410h壓入棧中。
            [3]語句調用atexit函數,根據我們的了解,atexit的參數應該是函數指針。那么我們來分析一下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調用規范的風格。
            [2]語句調用AAA的析構函數。

            程序結束時,將調用atexit函數注冊的442410h處的函數,進而調用了AAA的析構函數。從而保證了析構函數的調用。

             
            3、   了解了局部靜態對象之后,我們來看看全局對象。
            我們知道全局對象必須在main函數前已經被構造。為了弄清楚全局對象何時被構造,我在全局對象的實例化處設置了斷點,調用堆棧如下:
            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的析構函數出設置了斷點,調用堆棧如下:
                 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函數(相對于ANSI的控制臺程序而言)。
            我們來分析一下_cinit函數:
            注釋中有一句[3. General C initializer routines],看來該函數的功能之一是完成C的初始化例程。
            函數的核心代碼如下:
            /*
                     * do initializations
                     */
                    initret = _initterm_e( __xi_a, __xi_z );
            /*
                     * do C++ initializations
                     */
                    _initterm( __xc_a, __xc_z );
            看來該函數主要進行C、C++的初始化。我們進一步分析函數_initterm_e和_initterm,兩個函數的功能進本相同,都是遍歷函數指針(由參數指定函數指針的開始位置[__xi_a、__xi_z]、結束位置[__xc_a、__xc_z]),如果函數指針不為null,那么調用該函數。
            那么__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")
            可以看出這四個變量分別在數據段.CRT$XIA、.CRT$XIZ、.CRT$XCA、.CRT$XCZ中。當連接器布局代碼時,它按根據的名稱,按照字母排序的規則,排列所有段。這樣在段.CRT$XIA中的變量出現在段.CRT$XIZ所有變量之前,從而形成鏈表。對于.CRT$XCA、.CRT$XCZ數據段同理。最后這四個數據段被合并到.data數據段中。
            再看看這些變量的類型,typedef void (__cdecl *_PVFV)(void); 所以這些變量組成了2個初始化函數指針鏈表。
            調試過程中,看到__xc_a、__xc_z鏈表中,指向的初始化函數很多是構造函數,如:
            static std::_Init_locks initlocks;
            static filebuf fout(_cpp_stdout);
            extern _CRTDATA2 ostream cout(&fout);
            cout對象也在此時被構造。
            對于析構函數的調用也是采用相同的方式,只是此時每一種初始化,都有一種終止函數與之對應。

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


            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/coffeemay/archive/2006/11/19/1395250.aspx

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

                 摘要: 標 題: 【翻譯】“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 沛沛 閱讀(317) | 評論 (0)編輯 收藏

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

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

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

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

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

            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獲取我們需要的幾個函數指針
                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;
                }
             
                // 以下打開內核對象
                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;            // 輸出長度,根據內存分頁大小可能大于要求的長度
                PVOID vaddress;          // 映射的虛地址
                NTSTATUS status;         // NTDLL函數返回的狀態
                LARGE_INTEGER base;      // 物理內存地址
             
                vaddress = 0;
                outlen = length;
                base.QuadPart = (ULONGLONG)(address);
             
                // 映射物理內存地址到當前進程的虛地址空間
                status = ZwMapViewOfSection(hPhysicalMemory,
                    (HANDLE) -1,
                    (PVOID *)&vaddress,
                    0,
                    length,
                    &base,
                    &outlen,
                    ViewShare,
                    0,
                    PAGE_READONLY);
             
                if (status < 0)
                {
                    return FALSE;
                }
             
                // 當前進程的虛地址空間中,復制數據到輸出緩沖區
                memmove(buffer, vaddress, length);
             
                // 完成訪問,取消地址映射
                status = ZwUnmapViewOfSection((HANDLE)-1, (PVOID)vaddress);
             
                return (status >= 0);
            }
             
            // 一個測試函數,從物理地址0xfe000開始,讀取4096個字節
            // 對于Award BIOS,可以從這段數據找到序列號等信息
            BOOL test()
            {
                UCHAR buf[4096];
             
                if (!InitPhysicalMemory())
                {
                    return FALSE;
                }
             
                if (!ReadPhysicalMemory(buf, 0xfe000, 4096))
                {
                    // ... 成功讀取了指定數據
                    ExitPhysicalMemory();
                    return FALSE;
                }
             
                ExitPhysicalMemory();
             
                return TRUE;
            }

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

            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/coffeemay/archive/2006/10/28/1354465.aspx

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

            Debug和Release有什么區別?怎么把Debug轉成Release ?
            1。Debug和Release有什么區別,為什么要使用Release版本! 
            2。怎么把Debug轉成Release 

            轉載: 

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


            一、Debug   和   Release   編譯方式的本質區別 

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

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

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

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

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

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

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

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

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

            ●   MFC   消息響應函數書寫錯誤。正確的應為 
            afx_msg   LRESULT   OnMessageOwn(WPARAM   wparam,   LPARAM   lparam); 
            ON_MESSAGE   宏包含強制類型轉換。防止這種錯誤的方法之一是重定義   ON_MESSAGE   宏,把下列代碼加到   stdafx.h   中(在#include   "afxwin.h"之后),函數原形錯誤時編譯會報錯 
            #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   告訴編譯器該變量可能被程序之外的未知方式修改(如系統、其他進程和線程)。優化程序為了使程序性能提高,常把一些變量放在寄存器中(類似于   register   關鍵字),而其他進程只能對該變量所在的內存進行修改,而寄存器中的值沒變。如果你的程序是多線程的,或者你發現某個變量的值與預期的不符而你確信已正確的設置了,則很可能遇到這樣的問題。這種錯誤有時會表現為程序在最快優化出錯而最小優化正常。把你認為可疑的變量加上   volatile   試試。 

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

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

            int   j; 
            j   =   1; 

            a[-1]   =   1;//當然錯誤不會這么明顯,例如下標是變量 
            a[4]   =   1; 

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

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

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

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

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

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

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

            一、"Debug是調試版本,包括的程序信息更多" 

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

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


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

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

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

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

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

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

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

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

            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版中則不會。所以盡可能的給變量、類或結構初始化。

            2.             數據溢出的問題 

                     如:char buffer[10];
                          int counter;

                    lstrcpy(buffer, "abcdefghik");

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

            3.          DEBUG版和RELEASE版的內存分配方式是不同的 。如果你在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
                     實際上復雜一些,但無關緊要。假如你在這些語句中加了程序中必須要有的代碼
            比如

            ASSERT(pNewObj = new CMyClass);

            pNewObj->MyFunction();

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

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

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

            III.    參數問題:

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

            afx_msg LRESULT OnMyMessage(WPARAM, LPARAM);

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

            IV.   內存分配

            保證數據創建和清除的統一性:如果一個DLL提供一個能夠創建數據的函數,那么這個DLL同時應該提供一個函數銷毀這些數據。數據的創建和清除應該在同一個層次上。

            V.      DLL的災難

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

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

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

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


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

            VI.   RELEASE板中的調試 :

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

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

            3.          是否在編譯時已經有了警告?請將警告級別設置為3或4,然后保證在編譯時沒有警告出現.

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

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

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

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

            "Rebuild All"

            如此做法會產生的一些限制:

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

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

            另:

            MS BUG:MS的一份技術文檔中表明,在VC5中對于DLL的"Maximize Speed"優化選項并未被完全支持,因此這將會引起內存錯誤并導致程序崩潰。

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

            3.          有一個叫Gimpel Lint的靜態代碼檢查工具,據說比較好用。

             

            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/Jiao2_vc/archive/2009/03/07/3967364.aspx

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


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

             在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函數將告訴你,堆棧被破壞

            1)調用約定(Calling convention)

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

            1、__stdcall調用約定

                相當于16位動態庫中經常使用的 PASCAL 調用約定。在32位的 VC++5.0 中PASCAL 調用約定不再被支持(實際上它已被定義為__stdcall。函數的參數自右向左通過棧傳遞,被調用的函數在返回前清理傳送參數的內存棧。

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

            2、C 調用約定(即用__cdecl)

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

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

            3、__fastcall 調用約定

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

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

            4、thiscall調用約定

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

            5、naked call調用約定

                采用 1-4 的調用約定時,如果必要的話,進入函數時編譯器會產生代碼來保存ESI,EDI,EBX,EBP寄存器,退出函數時則產生代碼恢復這些寄存器的內容。

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

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


            2)名字修飾約定

            1、修飾名(Decoration name)

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

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

            a、C編譯時函數名修飾約定規則:

            __stdcall調用約定:

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

            __cdecl調用約定:

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

            __fastcall調用約定:

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

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

            b、C++編譯時函數名修飾約定規則:

            __stdcall調用約定:

            以"?"標識函數名的開始,后跟函數名;
            函數名后面以"@@YG"標識參數表的開始,后跟參數表;
            參數表以代號表示:
                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——表示指針,后面的代號表明指針類型,如果相同類型的指針連    續出現,以"0"代替,一個"0"代表一次重復;


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

                "?functionname@@YG*XZ",

                例如

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

            __cdecl調用約定:

                規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的"@@YG"變為"@@YA"。VC++對函數的省缺聲明是"__cedcl",將只能被C/C++調用。

            __fastcall調用約定:

                規則同上面的_stdcall調用約定,只是參數表的開始標識由上面的"@@YG"變為"@@YI"。


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

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

            僅列出標題
            共7頁: 1 2 3 4 5 6 7 
            伊人久久大香线蕉AV色婷婷色| 亚洲乱码中文字幕久久孕妇黑人| 久久精品无码一区二区三区日韩 | 国产精品免费久久| 精品国产91久久久久久久a| 久久精品国产欧美日韩| 久久九九久精品国产免费直播| 亚洲av成人无码久久精品| 久久精品人人做人人爽电影| 久久精品一本到99热免费| 久久精品女人天堂AV麻| 久久久久久人妻无码| 四虎影视久久久免费| 久久精品国产亚洲一区二区| 2021最新久久久视精品爱| 亚洲国产精品久久久久婷婷软件 | 久久热这里只有精品在线观看| 久久99精品久久久久久不卡| 久久只有这精品99| 91久久香蕉国产熟女线看| 亚洲中文久久精品无码ww16 | 国产一区二区久久久| 一本大道久久a久久精品综合| 无码人妻久久一区二区三区免费丨| 国产成人无码精品久久久免费 | 成人精品一区二区久久| 久久精品久久久久观看99水蜜桃| 天天久久狠狠色综合| 久久婷婷五月综合色高清| 久久天天躁狠狠躁夜夜avapp| 亚洲国产综合久久天堂| 久久久国产精品网站| 激情伊人五月天久久综合| 亚洲AV日韩精品久久久久| 日产精品久久久久久久| 欧美日韩精品久久久久| 久久久久久久久久久| 久久婷婷五月综合国产尤物app| 日产精品久久久久久久| 亚洲国产另类久久久精品小说| 亚洲伊人久久精品影院|