青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Where there is a dream ,there is hope

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

常用鏈接

留言簿(1)

我參與的團隊

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

#

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

1:神馬是Dll和Lib,神馬是靜態(tài)鏈接和動態(tài)鏈接

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

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

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

 

2:DLL和LIB區(qū)別和聯(lián)系。

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

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

DLL只有一種,其中一定是函數(shù)和過程的實現(xiàn)。

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

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

靜態(tài)鏈接LIB的優(yōu)點是免除掛接動態(tài)鏈接庫,缺點是EXE大,版本控制麻煩些。

動態(tài)鏈接DLL的優(yōu)點是文件小,版本更換時換DLL就好了,缺點是多了點文件。動態(tài)鏈接若是被多個進程使用,會更加方便和節(jié)省內(nèi)存。

 

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

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

當然,若我們的應(yīng)用程序里不鏈接這個LIB,則可以使用LoadLibrary,GetProcAddress來告訴系統(tǒng)我們在運行時需要怎么著DLL以及其內(nèi)的函數(shù)。

 

4:DLL意義。

1:DLL真正實現(xiàn)了跨語言。各種語言都可以生成DLL,而對系統(tǒng)以及應(yīng)用程序來說,哪種語言生成的DLL是沒有區(qū)別的。

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

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

 

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

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

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

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

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

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

#ifdef FK_DLL_EXPORTS

#define FK_DLL __declspec( dllexport )

#else

#define FK_DLL __declspec( dllimport )

#endif

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

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

使用時候也很簡單。

class FK_DLL CMyDllClass{} ;

則整個類被導(dǎo)出。

FK_DLL void MyTestFun( int a );

則該函數(shù)被導(dǎo)出。

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

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

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

 

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

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

FKDll.def里加入以下代碼

LIBRARY FKDll

EXPORTS

MyTestFun@1

MyTestFun2@2

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

 

6:使用DLL

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

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

顯式鏈接則麻煩些。在程序中使用LoadLibrary加載DLL,再GetProcAddress獲取函數(shù)實現(xiàn),在程序退出之前,調(diào)用FreeLibrary來動態(tài)釋放掉鏈接庫。

‍例如:

void Main()

{

     typedef void (*FKDllFun1)(int a);

    FKDllFun1 pFun1;

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

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

    pFun1( 100 );

    FreeLibrary(hDll);

}

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

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

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

 

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

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

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

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

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

例如:

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

{

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

}

 

8:導(dǎo)出類和導(dǎo)出函數(shù)

類和函數(shù)的導(dǎo)出方式上面給出了說明,原本極其類似的。

我們說下使用導(dǎo)出類。

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

例如:

class FK_DLL CMyDllClass{} ;    // Dll文件內(nèi)的代碼

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

class CAppClass : public CMyDllClass      // 應(yīng)用程序內(nèi)代碼,無需做任何處理。

{

       ....

}

也可以直接使用DLL導(dǎo)出類

void main

{

     CMyDllClass* pClass = new CMyDllClass ();

}

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

為解決這個問題,我們可以給出兩個接口函數(shù)對DLL導(dǎo)出類進行創(chuàng)建銷毀支持,就可以使內(nèi)存跟蹤系統(tǒng)正常了。例如

class FK_DLL CMyDllClass{} ; 

額外增加倆函數(shù)

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

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

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

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

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

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

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

同樣,我們也可以發(fā)現(xiàn),在不導(dǎo)出DLL類本身,而只導(dǎo)出DLL類內(nèi)函數(shù)也有一些好處,一些我們不希望外界知道的函數(shù)可以不設(shè)置導(dǎo)出標記,這進一步保護了DLL內(nèi)函數(shù)的安全性。

 

9:復(fù)雜問題。

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

class CMyDllClass

{

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

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

}

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

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

class CAppClass : public CMyDllClass      // 應(yīng)用程序內(nèi)代碼,無需做任何處理。

{

       ....

}


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

 

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

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

一、一般用法

我們使用#把宏參數(shù)變?yōu)橐粋€字符串,##把兩個宏參數(shù)貼合在一起.

用法:

#include<cstdio>

#include<climits>

using namespace std;

#define STR(s)   #s

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

int main()

{

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

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

 return 0;

}

 

二、當宏參數(shù)是另一個宏的時候

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

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

#define TOW     (2)

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

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

這行的宏會被展開為:

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

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

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

#define A       (2)

#define STR(s)   #s

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

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

這行會被展開為:

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

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

這一行則是:

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

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

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

#define A       (2)

#define _STR(s)   #s

#define STR(s)     _STR(s)       // 轉(zhuǎn)換宏

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

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

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

輸出為: int max: 0x7fffffff

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

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

輸出為:200

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

三、'#''##'的一些應(yīng)用特例

1、合并匿名變量名

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

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

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

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

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

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

第三層:                 --> static int _anonymous70;

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

2、填充結(jié)構(gòu)

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

enum IDD{OPEN, CLOSE};

typedef struct MSG{

IDD id;

const char * msg;

}MSG;

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

相當于:

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

        {CLOSE, "CLOSE"}};

3、記錄文件名

#define _GET_FILE_NAME(f)   #f

#define GET_FILE_NAME(f)   _GET_FILE_NAME(f)

static char FILE_NAME[] = GET_FILE_NAME(__FILE__);

4、得到一個數(shù)值類型所對應(yīng)的字符串緩沖大小

#define _TYPE_BUF_SIZE(type) sizeof #type

#define TYPE_BUF_SIZE(type)   _TYPE_BUF_SIZE(type)

char buf[TYPE_BUF_SIZE(INT_MAX)];

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

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

這里相當于:

char buf[11];

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

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

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

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

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

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

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

參考代碼:

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

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

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

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

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

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

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

      BSTreeNode *pTemp = pNode;

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

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

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

      BSTreeNode *pCurrent = pNode;

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

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

      pLastNodeInList = pCurrent;

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

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

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

      return pHeadOfList;
}

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

原文地址:
http://blog.csdn.net/fly2k5/archive/2005/12/05/544112.aspx
在C語言中,假設(shè)我們有這樣的一個函數(shù):
  
  int function(int a,int b)
  
  調(diào)用時只要用result = function(1,2)這樣的方式就可以使用這個函數(shù)。但是,當高級語言被編譯成計算機可以識別的機器碼時,有一個問題就凸現(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ù)傳遞中,有兩個很重要的問題必須得到明確說明:
  
  當參數(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è)計語言,其語法嚴謹,使用的函數(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)用者負責(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ù)奈恢茫虼水敳捎貌欢▊€數(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)致嚴重問題,下面是兩種常見的問題:

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

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

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

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

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

1、數(shù)據(jù)寄存器

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

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

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

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


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

2、變址寄存器

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

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

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

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

3、指針寄存器

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

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

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

它們主要用于訪問堆棧內(nèi)的存儲單元,并且規(guī)定:

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

4、段寄存器

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

CPU內(nèi)部的段寄存器:

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

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

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

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

5、指令指針寄存器

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

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

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

6、標志寄存器

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2、嵌套任務(wù)標志NT(Nested Task)
嵌套任務(wù)標志NT用來控制中斷返回指令I(lǐng)RET的執(zhí)行。具體規(guī)定如下:

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

(2)、當NT=1,通過任務(wù)轉(zhuǎn)換實現(xiàn)中斷返回。

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

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

一、常用指令 

1. 通用數(shù)據(jù)傳送指令

   MOV 傳送字或字節(jié)

   MOVSX 先符號擴展,再傳送

   MOVZX 先零擴展,再傳送

   PUSH 把字壓入堆棧

   POP 把字彈出堆棧

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

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

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

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

   BSWAP 交換32位寄存器里字節(jié)的順序 

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

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

   XADD 先交換再累加.( 結(jié)果在第一個操作數(shù)里

   XLAT 字節(jié)查表轉(zhuǎn)換

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

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

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

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

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

3. 目的地址傳送指令

   LEA 裝入有效地址

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

   LDS 傳送目標指針,把指針內(nèi)容裝入DS. 

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

   LES 傳送目標指針,把指針內(nèi)容裝入ES. 

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

   LFS 傳送目標指針,把指針內(nèi)容裝入FS. 

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

   LGS 傳送目標指針,把指針內(nèi)容裝入GS. 

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

   LSS 傳送目標指針,把指針內(nèi)容裝入SS. 

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

4. 標志傳送指令

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

   SAHF 標志寄存器傳送,AH內(nèi)容裝入標志寄存器

   PUSHF 標志入棧

   POPF 標志出棧

   PUSHD 32位標志入棧

   POPD 32位標志出棧

二、算術(shù)運算指令 

   ADD 加法

   ADC 帶進位加法

   INC 1. 

   AAA 加法的ASCII碼調(diào)整

   DAA 加法的十進制調(diào)整

   SUB 減法

   SBB 帶借位減法

   DEC 1. 

   NEC 求反( 0 減之). 

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

   AAS 減法的ASCII碼調(diào)整

   DAS 減法的十進制調(diào)整

   MUL 無符號乘法

  IMUL 整數(shù)乘法

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

   AAM 乘法的ASCII碼調(diào)整

   DIV 無符號除法

   IDIV 整數(shù)除法

     以上兩條,結(jié)果回送

     商回送AL,余數(shù)回送AH, (字節(jié)運算); 

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

   AAD 除法的ASCII碼調(diào)整

   CBW 字節(jié)轉(zhuǎn)換為字. (AL中字節(jié)的符號擴展到AH中去

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

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

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

三、邏輯運算指令 

   AND 與運算

   OR 或運算

   XOR 異或運算

   NOT 取反

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

   SHL 邏輯左移

   SAL 算術(shù)左移.(=SHL) 

   SHR 邏輯右移

   SAR 算術(shù)右移.(=SHR) 

   ROL 循環(huán)左移

   ROR 循環(huán)右移

   RCL 通過進位的循環(huán)左移

   RCR 通過進位的循環(huán)右移

     以上八種移位指令,其移位次數(shù)可達255

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

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

      MOV CL,04 

         SHL AX,CL 

四、串指令 

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

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

   CX 重復(fù)次數(shù)計數(shù)器

   AL/AX 掃描值

   D標志 0表示重復(fù)操作中SIDI應(yīng)自動增量; 1表示應(yīng)自動減量

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

   MOVS 串傳送

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

   CMPS 串比較

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

   SCAS 串掃描

     ALAX的內(nèi)容與目標串作比較,比較結(jié)果反映在標志位

   LODS 裝入串

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

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

   STOS 保存串

   LODS的逆過程

   REP CX/ECX<>0時重復(fù)

   REPE/REPZ ZF=1或比較結(jié)果相等,CX/ECX<>0時重復(fù)

   REPNE/REPNZ ZF=0或比較結(jié)果不相等,CX/ECX<>0時重復(fù)

  REPC CF=1CX/ECX<>0時重復(fù)

   REPNC CF=0CX/ECX<>0時重復(fù)

五、程序轉(zhuǎn)移指令 

1>無條件轉(zhuǎn)移指令 (長轉(zhuǎn)移

   JMP 無條件轉(zhuǎn)移指令 

   CALL 過程調(diào)用 

   RET/RETF過程返回

2>條件轉(zhuǎn)移指令 (短轉(zhuǎn)移,-128+127的距離內(nèi)

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

   JA/JNBE <

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

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


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

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

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


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

Solution:

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

僅列出標題
共7頁: 1 2 3 4 5 6 7 
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            小黄鸭视频精品导航| 亚洲最黄网站| 日韩一区二区久久| 亚洲国内欧美| 亚洲日本一区二区三区| 亚洲精品黄色| 久久精品二区亚洲w码| 亚洲欧美日韩综合| 午夜在线视频观看日韩17c| 欧美在线亚洲一区| 男人的天堂亚洲| 欧美精品一区二区精品网| 欧美日韩在线三区| 国产精品尤物福利片在线观看| 国产伦精品一区二区三区免费 | 亚洲视频欧美视频| 亚洲在线一区二区三区| 欧美一区二区视频在线观看2020| 久久久精品动漫| 91久久精品美女| 亚洲日韩视频| 亚洲欧美国产不卡| 欧美第十八页| 国产精品美女在线| 亚洲精品免费观看| 久久精品九九| 亚洲免费成人av电影| 欧美在线免费视频| 欧美色另类天堂2015| 国内精品一区二区| 一区二区三区毛片| 久久在线免费观看| 中国成人亚色综合网站| 你懂的视频欧美| 国产欧美日韩亚洲一区二区三区 | 免费国产自线拍一欧美视频| 欧美三区在线视频| 亚洲国产视频一区| 久久不射2019中文字幕| 亚洲国产高清在线| 午夜欧美不卡精品aaaaa| 欧美成人蜜桃| 韩日欧美一区二区| 亚洲欧美视频一区| 亚洲毛片在线免费观看| 久久五月天婷婷| 国产裸体写真av一区二区| 91久久视频| 欧美日韩成人一区二区三区| 欧美亚洲免费电影| 欧美日韩一区二区三区四区五区 | 亚洲视频久久| 免费短视频成人日韩| 国语自产精品视频在线看一大j8| 亚洲一区二区视频| 亚洲理论电影网| 免费欧美在线视频| 亚洲国产毛片完整版| 久久久久久久久岛国免费| 在线亚洲精品| 欧美午夜一区| 亚洲男人第一av网站| 日韩性生活视频| 欧美日韩一区二区三区在线视频| 亚洲精品社区| 亚洲日韩第九十九页| 欧美激情2020午夜免费观看| 亚洲韩国青草视频| 欧美国产精品劲爆| 女仆av观看一区| 亚洲麻豆av| av成人免费在线观看| 欧美视频中文在线看| 亚洲综合导航| 午夜天堂精品久久久久| 国产欧美丝祙| 久久久久久久久久久久久9999| 欧美一级一区| 亚洲第一区中文99精品| 亚洲国产精品久久人人爱蜜臀 | 在线亚洲自拍| 亚洲深夜福利视频| 国产日韩精品一区二区| 久久综合久色欧美综合狠狠| 久久亚洲综合色| 亚洲毛片在线观看| 亚洲小少妇裸体bbw| 国产原创一区二区| 亚洲国产精品尤物yw在线观看| 欧美日韩精品高清| 欧美中文在线观看| 久久精品视频在线播放| 亚洲精品欧美日韩专区| 亚洲作爱视频| 狠狠久久五月精品中文字幕| 亚洲国产天堂久久综合网| 国产精品久久久久久久久久免费看 | 狂野欧美性猛交xxxx巴西| 99精品99久久久久久宅男| 中文久久乱码一区二区| 亚洲人成高清| 亚洲视频一起| 1000精品久久久久久久久| 99pao成人国产永久免费视频| 国产精品美女久久久免费| 欧美成人一品| 国产手机视频一区二区| 亚洲靠逼com| 狠狠入ady亚洲精品| 亚洲图片你懂的| 亚洲人成亚洲人成在线观看图片| 亚洲少妇自拍| 亚洲精品字幕| 久久都是精品| 亚洲综合色在线| 免费在线日韩av| 欧美在线亚洲综合一区| 欧美日韩高清免费| 亚洲福利视频专区| 国产日韩欧美精品一区| 99re视频这里只有精品| 亚洲狠狠丁香婷婷综合久久久| 亚洲免费综合| 亚洲一区欧美| 欧美乱在线观看| 欧美国产91| 怡红院精品视频| 欧美一区三区二区在线观看| 亚洲免费综合| 国产精品wwwwww| 亚洲精品国产精品国自产观看浪潮| 狠狠色伊人亚洲综合网站色| 亚洲欧美国内爽妇网| 亚洲一区二区在线观看视频| 欧美日本簧片| 亚洲欧洲一二三| 亚洲人成网站影音先锋播放| 久久久久久有精品国产| 久久久久久国产精品一区| 国产视频欧美| 午夜亚洲影视| 久久久国产亚洲精品| 国产日韩精品入口| 午夜一区二区三视频在线观看| 亚洲免费综合| 亚洲美女精品一区| av成人免费观看| 欧美激情一区在线观看| 亚洲国产一区在线| 99国产精品99久久久久久粉嫩| 一区二区三区|亚洲午夜| 亚洲主播在线| 国产色综合网| 久久精品国产一区二区三区| 久久久久在线观看| 在线播放国产一区中文字幕剧情欧美| 久久丁香综合五月国产三级网站| 亚洲激情第一区| 免费av成人在线| 午夜精品视频网站| 国产九九精品| 久久久精品免费视频| 欧美91福利在线观看| 亚洲欧洲一区二区在线观看| 欧美成黄导航| 久久久久一本一区二区青青蜜月| 国产私拍一区| 亚洲精品123区| 欧美国产激情| 久久久国产精品亚洲一区| 久久中文字幕导航| 亚洲精品网站在线播放gif| 欧美日韩国产一区精品一区| 99视频在线精品国自产拍免费观看| 亚洲一区视频| 国产综合久久久久久鬼色| 欧美 日韩 国产 一区| 9色精品在线| 麻豆亚洲精品| 亚洲欧美怡红院| 亚洲日本va午夜在线电影| 国产精品一区一区| 欧美成人一区二区三区在线观看 | 久久久精品日韩欧美| 亚洲精品之草原avav久久| 亚洲综合导航| 日韩午夜激情| 激情小说另类小说亚洲欧美| 欧美三级乱人伦电影| 久久免费黄色| 亚洲欧美日韩综合| 亚洲卡通欧美制服中文| 欧美chengren| 久久九九国产| 午夜亚洲伦理| 亚洲精品午夜精品| 国内久久视频| 国产精品久久一级| 欧美另类高清视频在线| 久久欧美肥婆一二区|