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

Where there is a dream ,there is hope

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

常用鏈接

留言簿(1)

我參與的團(tuán)隊(duì)

搜索

  •  

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

#

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

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

大家都懂的,DLL就是動(dòng)態(tài)鏈接庫(kù),LIB是靜態(tài)鏈接庫(kù)。DLL其實(shí)就是EXE,只不過(guò)沒(méi)main。

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

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

 

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

DLL是程序在運(yùn)行階段才需要的文件。

LIB是程序編譯時(shí)需要鏈接的文件。

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

LIB是有兩種。若只生成LIB的話,則這個(gè)LIB是靜態(tài)編譯出來(lái)的,它內(nèi)部包含了函數(shù)索引以及實(shí)現(xiàn),這個(gè)LIB會(huì)比較大。若生成DLL的話,則也會(huì)生成一個(gè)LIB,這個(gè)LIB和剛才那個(gè)LIB不同,它是只有函數(shù)索引,沒(méi)有實(shí)現(xiàn)的,它很小。但是這倆LIB依然遵循上個(gè)原則,是在編譯時(shí)候是需要被鏈接的。若不鏈接第一個(gè)LIB的話,在程序運(yùn)行時(shí)會(huì)無(wú)法找到函數(shù)實(shí)現(xiàn),當(dāng)?shù)?。若不鏈接第二個(gè)LIB的話,在程序運(yùn)行時(shí)依然會(huì)無(wú)法找到函數(shù)實(shí)現(xiàn)。但第二種LIB有一種替代方式,就是在程序里,使用LoadLibrary,GetProcAddress替代第二個(gè)LIB的功能。第一種LIB生成的EXE文件會(huì)很大,因?yàn)長(zhǎng)IB所有信息被靜態(tài)鏈接進(jìn)EXE里了。第二種LIB生成的EXE文件會(huì)比較小,因?yàn)楹瘮?shù)過(guò)程實(shí)現(xiàn)依舊在DLL內(nèi)。

(啰嗦了一堆,某志希望大家能夠明白兩個(gè)LIB的區(qū)別。要再不行的話,我們可以將靜態(tài)編譯的LIB稱(chēng)為 靜態(tài)鏈接庫(kù)。但動(dòng)態(tài)編譯的LIB稱(chēng)為 引入庫(kù)??赡軙?huì)比較好一些。)

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

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

 

3:為什么編譯DLL時(shí)總會(huì)同時(shí)生成一個(gè)LIB?這個(gè)LIB有用嗎?

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

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

 

4:DLL意義。

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

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

3:DLL被多個(gè)進(jìn)程使用,因?yàn)橛袃?nèi)存映射機(jī)制,無(wú)需占用更多內(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的三種類(lèi)型就不解釋了,依舊那句話:百度一下你就知道。)

每個(gè)應(yīng)用程序必須有一個(gè)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:   // 進(jìn)程被調(diào)用
 case DLL_THREAD_ATTACH:     // 線程被調(diào)用
 case DLL_THREAD_DETACH:   // 線程被停止
 case DLL_PROCESS_DETACH:  // 進(jìn)程被停止
  break;
 }
 return TRUE;
}

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

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

但是有查看過(guò)DLL代碼的人員都會(huì)經(jīng)常見(jiàn)到這么一段代碼

#ifdef FK_DLL_EXPORTS

#define FK_DLL __declspec( dllexport )

#else

#define FK_DLL __declspec( dllimport )

#endif

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

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

使用時(shí)候也很簡(jiǎn)單。

class FK_DLL CMyDllClass{} ;

則整個(gè)類(lèi)被導(dǎo)出。

FK_DLL void MyTestFun( int a );

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

但是有時(shí)我們可以見(jiàn)到這樣的代碼

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

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

 

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

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

FKDll.def里加入以下代碼

LIBRARY FKDll

EXPORTS

MyTestFun@1

MyTestFun2@2

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

 

6:使用DLL

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

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

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

‍例如:

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

}

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

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

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

 

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

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

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

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

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

例如:

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

{

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

}

 

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

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

我們說(shuō)下使用導(dǎo)出類(lèi)。

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

例如:

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

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

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

{

       ....

}

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

void main

{

     CMyDllClass* pClass = new CMyDllClass ();

}

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

為解決這個(gè)問(wèn)題,我們可以給出兩個(gè)接口函數(shù)對(duì)DLL導(dǎo)出類(lèi)進(jìn)行創(chuàng)建銷(xiāo)毀支持,就可以使內(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; }

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

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

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

但是若僅僅提供上面兩個(gè)接口函數(shù)以及類(lèi)內(nèi)全部函數(shù),的確功能可以實(shí)現(xiàn),卻無(wú)法進(jìn)行類(lèi)繼承了。若這個(gè)類(lèi)繼承很重要,必須開(kāi)放,那么就需要使用新的內(nèi)存跟蹤程序替換應(yīng)用程序內(nèi)的原有內(nèi)存跟蹤程序?;蛘呤褂孟旅娴囊粋€(gè)方法。(見(jiàn)模塊9:復(fù)雜問(wèn)題)

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

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

 

9:復(fù)雜問(wèn)題。

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

class CMyDllClass

{

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

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

}

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

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

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

{

       ....

}


posted @ 2011-01-17 11:55 IT菜鳥(niǎo) 閱讀(595) | 評(píng)論 (0)編輯 收藏

 

 見(jiàn)到這兩個(gè)符號(hào)的很多不同的用法,整理在一起

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

一、一般用法

我們使用#把宏參數(shù)變?yōu)橐粋€(gè)字符串,##把兩個(gè)宏參數(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;

}

 

二、當(dāng)宏參數(shù)是另一個(gè)宏的時(shí)候

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

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

#define TOW     (2)

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

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

這行的宏會(huì)被展開(kāi)為:

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

MUL里的參數(shù)TOW會(huì)被展開(kāi)為(2).

2, 當(dāng)有'#''##'的時(shí)候

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

這行會(huì)被展開(kāi)為:

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

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

這一行則是:

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

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

加這層宏的用意是把所有宏的參數(shù)在這層里全部展開(kāi), 那么在轉(zhuǎn)換宏里的那一個(gè)宏(_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型的最大值,為一個(gè)變量 #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表示該行行號(hào);

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

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

第三層:                 --> static int _anonymous70;

即每次只能解開(kāi)當(dāng)前層的宏,所以__LINE__在第二層才能被解開(kāi);

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

相當(dāng)于:

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、得到一個(gè)數(shù)值類(lèi)型所對(duì)應(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"];

這里相當(dāng)于:

char buf[11];

posted @ 2010-12-17 14:34 IT菜鳥(niǎo) 閱讀(405) | 評(píng)論 (0)編輯 收藏

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

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

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

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

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

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

參考代碼:

首先我們定義二元查找樹(shù)結(jié)點(diǎn)的數(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
    };

思路一對(duì)應(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);
}

思路二對(duì)應(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菜鳥(niǎo) 閱讀(349) | 評(píng)論 (0)編輯 收藏

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

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

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

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

  stdcall 
  cdecl 
  fastcall 
  thiscall 
  naked call

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

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

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

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

  而對(duì)于函數(shù)自身,則可以翻譯為: 
  push ebp       保存ebp寄存器,該寄存器將用來(lái)保存堆棧的棧頂指針,可以在函數(shù)退出時(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í),這個(gè)函數(shù)的名字被翻譯成_function@8

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

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

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

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

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

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

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

  MSDN中說(shuō),該修飾自動(dòng)在函數(shù)名前加前導(dǎo)的下劃線,因此函數(shù)名在符號(hào)表中被記錄為_(kāi)function,但是我在編譯時(shí)似乎沒(méi)有看到這種變化。

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

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

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

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

  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沒(méi)有被入棧

  //函數(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
  
  可見(jiàn),對(duì)于參數(shù)個(gè)數(shù)固定情況下,它類(lèi)似于stdcall,不定時(shí)則類(lèi)似cdecl

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

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

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

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

  注意這個(gè)修飾是和__stdcall及cdecl結(jié)合使用的,前面是它和cdecl結(jié)合使用的代碼,對(duì)于和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)致的常見(jiàn)問(wèn)題
  如果定義的約定和使用的約定不一致,則將導(dǎo)致堆棧被破壞,導(dǎo)致嚴(yán)重問(wèn)題,下面是兩種常見(jiàn)的問(wèn)題:

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

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

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

  DLLFUNC func = (DLLFUNC)GetProcAddress(...)//這里修改了調(diào)用約定
  result = func(1,2);//導(dǎo)致錯(cuò)誤

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

posted @ 2010-12-15 15:42 IT菜鳥(niǎo) 閱讀(198) | 評(píng)論 (0)編輯 收藏

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

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

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

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

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

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

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

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


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

2、變址寄存器

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

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

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

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

3、指針寄存器

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

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

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

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

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

4、段寄存器

段寄存器是根據(jù)內(nèi)存分段的管理模式而設(shè)置的。內(nèi)存單元的物理地址由段寄存器的值和一個(gè)偏移量組合而成
的,這樣可用兩個(gè)較少位數(shù)的值組合成一個(gè)可訪問(wèn)較大物理空間的內(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個(gè)段寄存器,所以,程序在任何時(shí)刻至多有4個(gè)正在使用的段可直接訪問(wèn);在32位
微機(jī)系統(tǒng)中,它有6個(gè)段寄存器,所以,在此環(huán)境下開(kāi)發(fā)的程序最多可同時(shí)訪問(wèn)6個(gè)段。

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

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

5、指令指針寄存器

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

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

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

6、標(biāo)志寄存器

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

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

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

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

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

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

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

4、零標(biāo)志ZF(Zero Flag)
零標(biāo)志ZF用來(lái)反映運(yùn)算結(jié)果是否為0。如果運(yùn)算結(jié)果為0,則其值為1,否則其值為0。在判斷運(yùn)算結(jié)果是否為0時(shí),可使用此標(biāo)志位。

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

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

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

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

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

指令系統(tǒng)中沒(méi)有專(zhuān)門(mén)的指令來(lái)改變標(biāo)志位TF的值,但程序員可用其它辦法來(lái)改變其值。

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

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

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

CPU的指令系統(tǒng)中也有專(zhuān)門(mén)的指令來(lái)改變標(biāo)志位IF的值。

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

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

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

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

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

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

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

一、常用指令 

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

   MOV 傳送字或字節(jié)

   MOVSX 先符號(hào)擴(kuò)展,再傳送

   MOVZX 先零擴(kuò)展,再傳送

   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é).( 至少有一個(gè)操作數(shù)為寄存器,段寄存器不可作為操作數(shù)

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

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

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

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

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

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

   OUT I/O端口輸出. ( 語(yǔ)法: OUT {端口號(hào)│DX},累加器

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

3. 目的地址傳送指令

   LEA 裝入有效地址

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

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

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

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

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

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

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

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

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

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

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

4. 標(biāo)志傳送指令

   LAHF 標(biāo)志寄存器傳送,把標(biāo)志裝入AH. 

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

   PUSHF 標(biāo)志入棧

   POPF 標(biāo)志出棧

   PUSHD 32位標(biāo)志入棧

   POPD 32位標(biāo)志出棧

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

   ADD 加法

   ADC 帶進(jìn)位加法

   INC 1. 

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

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

   SUB 減法

   SBB 帶借位減法

   DEC 1. 

   NEC 求反( 0 減之). 

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

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

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

   MUL 無(wú)符號(hào)乘法

  IMUL 整數(shù)乘法

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

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

   DIV 無(wú)符號(hào)除法

   IDIV 整數(shù)除法

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

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

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

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

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

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

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

   CDQ 雙字?jǐn)U展. (EAX中的字的符號(hào)擴(kuò)展到EDX中去

三、邏輯運(yùn)算指令 

   AND 與運(yùn)算

   OR 或運(yùn)算

   XOR 異或運(yùn)算

   NOT 取反

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

   SHL 邏輯左移

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

   SHR 邏輯右移

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

   ROL 循環(huán)左移

   ROR 循環(huán)右移

   RCL 通過(guò)進(jìn)位的循環(huán)左移

   RCR 通過(guò)進(jìn)位的循環(huán)右移

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

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

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

      MOV CL,04 

         SHL AX,CL 

四、串指令 

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

   ES I 目標(biāo)串段寄存器:目標(biāo)串變址

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

   AL/AX 掃描值

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

Z標(biāo)志 用來(lái)控制掃描或比較操作的結(jié)束

   MOVS 串傳送

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

   CMPS 串比較

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

   SCAS 串掃描

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

   LODS 裝入串

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

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

   STOS 保存串

   LODS的逆過(guò)程

   REP 當(dāng)CX/ECX<>0時(shí)重復(fù)

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

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

  REPC 當(dāng)CF=1CX/ECX<>0時(shí)重復(fù)

   REPNC 當(dāng)CF=0CX/ECX<>0時(shí)重復(fù)

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

1>無(wú)條件轉(zhuǎn)移指令 (長(zhǎng)轉(zhuǎn)移

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

   CALL 過(guò)程調(diào)用 

   RET/RETF過(guò)程返回

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

   ( 當(dāng)且僅當(dāng)(SF XOR OF)=1時(shí),OP1<OP2 ) 

   JA/JNBE <

posted @ 2010-12-15 11:01 IT菜鳥(niǎo) 閱讀(311) | 評(píng)論 (0)編輯 收藏

今天寫(xiě)一個(gè)小程序出現(xiàn)了這種問(wè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) : 參見(jiàn)“AF_IPX”的前一個(gè)定義
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) : 參見(jiàn)“AF_MAX”的前一個(gè)定義
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) : 參見(jiàn)“SO_DONTLINGER”的前一個(gè)定義
1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(206) : error C2011: “sockaddr”: “struct”類(lèi)型重定義
1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(485) : 參見(jiàn)“sockaddr”的聲明
1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2143: 語(yǔ)法錯(cuò)誤 : 缺少“}”(在“常量”的前面)
1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2143: 語(yǔ)法錯(cuò)誤 : 缺少“;”(在“常量”的前面)
1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2059: 語(yǔ)法錯(cuò)誤 : “常量”
1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C2143: 語(yǔ)法錯(cuò)誤 : 缺少“;”(在“}”的前面)
1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C4430: 缺少類(lèi)型說(shuō)明符 - 假定為 int。注意: C++ 不支持默認(rèn) int
1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C4430: 缺少類(lèi)型說(shuō)明符 - 假定為 int。注意: C++ 不支持默認(rèn) 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) : 參見(jiàn)“IN_CLASSA”的前一個(gè)定義
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) : 參見(jiàn)“IN_CLASSB”的前一個(gè)定義
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) : 參見(jiàn)“IN_CLASSC”的前一個(gè)定義
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) : 參見(jiàn)“INADDR_ANY”的前一個(gè)定義
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) : 參見(jiàn)“INADDR_BROADCAST”的前一個(gè)定義
1>c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(577) : error C2011: “sockaddr_in”: “struct”類(lèi)型重定義
1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(312) : 參見(jiàn)“sockaddr_in”的聲明
1>c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(132) : error C2011: “fd_set”: “struct”類(lèi)型重定義
1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(68) : 參見(jiàn)“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) : 參見(jiàn)“FD_SET”的前一個(gè)定義
1>c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(176) : error C2011: “timeval”: “struct”類(lèi)型重定義
1>        c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(111) : 參見(jiàn)“timeval”的聲明


好久沒(méi)寫(xiě)TCP的程序了,都忘記是怎么回事了,隱約記得解決方法很簡(jiǎn)單。搜索了一下,記錄下來(lái):

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

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


關(guān)于WINSOCK.H與winsock2.h中的重定義解決辦法分析
問(wèn)題描述:在 VC 6.0中使用socket相關(guān)的函數(shù)時(shí)沒(méi)有什么問(wèn)題,可是到了.net下就有以下類(lèi)似的錯(cuò)誤,
[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有時(shí)候我也遇到這個(gè)問(wèn)題,解決方法與之相同。一句話,在#include<windows.h>之前 #include <winsock2.h> 問(wèn)題就可以解決。

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

     error C2011: 'fd_set' : 'struct' type redefinition
     error C2011: 'timeval' : 'struct' type redefinition
                     ....
     error C2375: 'accept' : redefinition; different linkage
[原因分析]
   主要原因是因?yàn)?lt;windows.h>中包含了<winsock.h>頭文件,由于其版
本的不同,導(dǎo)致出
現(xiàn)上述的錯(cuò)誤。<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
[解決方案]
    由以上代碼可以看出如果在沒(méi)有定義WIN32_LEAN_AND_MEAN宏
的大前
提下windows.h有可能包含winsock.h 頭文件,因此我們得出一個(gè)很簡(jiǎn)單
的解決方
法就是在包含<windows.h>之前定義WIN32_LEAN_AND_MEAN宏,如
下所示:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

posted @ 2010-12-07 15:30 IT菜鳥(niǎo) 閱讀(1174) | 評(píng)論 (0)編輯 收藏

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

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

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

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

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

用typename關(guān)鍵字來(lái)告訴編譯器Obj是個(gè)類(lèi)型。
posted @ 2010-11-26 09:30 IT菜鳥(niǎo) 閱讀(1141) | 評(píng)論 (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菜鳥(niǎo) 閱讀(1280) | 評(píng)論 (0)編輯 收藏

僅列出標(biāo)題
共7頁(yè): 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>
            国产精品久久97| 亚洲欧美日韩视频二区| 久久精选视频| 一区二区三区高清不卡| 久久在线91| 合欧美一区二区三区| 亚洲无限av看| 亚洲国产aⅴ天堂久久| 久久国产精品99国产精| 国产精品免费久久久久久| 99这里有精品| 亚洲国产小视频| 久久综合狠狠综合久久综合88| 国产日产欧产精品推荐色 | 亚洲国产成人av在线 | 欧美日韩视频| 亚洲精品一品区二品区三品区| 久热爱精品视频线路一| 亚洲综合色噜噜狠狠| 国产精品a级| 亚洲五月婷婷| 日韩午夜在线| 欧美日韩一级片在线观看| 99国产精品久久久久久久成人热| 美女诱惑黄网站一区| 欧美一区二区黄| 亚洲一区观看| 国产精品久久91| 亚洲欧美日韩人成在线播放| 一区二区三区高清在线观看| 欧美日韩精品久久久| 妖精视频成人观看www| 亚洲激情国产精品| 欧美精品系列| 一本色道久久综合亚洲精品不 | 亚洲电影在线播放| 免费h精品视频在线播放| 久久乐国产精品| 亚洲高清在线精品| 欧美福利视频一区| 免播放器亚洲一区| 亚洲精选中文字幕| 亚洲区第一页| 欧美婷婷久久| 性欧美大战久久久久久久久| 午夜精品区一区二区三| 国产日韩欧美自拍| 麻豆亚洲精品| 欧美激情片在线观看| 一区二区三欧美| 一区二区三区高清视频在线观看| 国产精品久久波多野结衣| 欧美一区成人| 久久久欧美一区二区| 亚洲黄色av| 日韩亚洲精品视频| 国产九色精品成人porny| 久久久视频精品| 猛男gaygay欧美视频| 一区二区三区精品| 亚洲图片欧美午夜| 国产一区二区三区久久 | 一区二区三区精密机械公司 | 国产亚洲成av人片在线观看桃| 久久免费视频在线观看| 久久在线免费观看视频| 一本久道久久久| 亚洲欧美文学| 91久久精品国产91久久性色tv| 亚洲欧洲精品一区二区| 国产精品卡一卡二| 蜜臀91精品一区二区三区| 欧美另类专区| 久久久国产91| 欧美极品在线观看| 久久aⅴ乱码一区二区三区| 久久男人av资源网站| 一区二区三区欧美亚洲| 午夜精品久久久久影视| 亚洲欧洲精品一区二区| 亚洲午夜视频在线观看| 亚洲国产精品成人综合| 亚洲午夜电影网| 在线免费观看视频一区| 夜夜爽www精品| 雨宫琴音一区二区在线| 99精品视频一区| 久久精品一区二区三区中文字幕 | 久久久一区二区三区| 在线一区二区三区四区| 久久精品国产999大香线蕉| 日韩写真在线| 久久精品国产一区二区电影| 正在播放亚洲一区| 久久躁日日躁aaaaxxxx| 午夜精品网站| 欧美成人精品高清在线播放| 欧美在线不卡视频| 欧美另类视频在线| 免费一区二区三区| 国产精品日韩欧美一区二区三区| 欧美黄色片免费观看| 国产视频观看一区| 亚洲免费观看高清完整版在线观看熊 | 亚洲国产精品久久91精品| 国产精品日韩欧美大师| 亚洲国产精品成人精品| 国内精品久久久久国产盗摄免费观看完整版 | 猛男gaygay欧美视频| 国产精品视频yy9099| 亚洲啪啪91| 一区免费在线| 亚洲一区二区三区精品动漫| 亚洲免费成人av电影| 久久久久久久999精品视频| 性久久久久久久| 欧美日韩人人澡狠狠躁视频| 欧美国产91| 永久91嫩草亚洲精品人人| 亚洲欧洲av一区二区| 亚洲免费在线| 欧美日韩亚洲一区| 亚洲激情成人| 亚洲欧洲另类| 久久综合给合| 免费欧美在线视频| 国产综合网站| 欧美在线亚洲一区| 欧美综合国产| 国产欧美一区二区精品性色| 亚洲系列中文字幕| 亚洲永久免费观看| 欧美日韩综合网| 日韩午夜电影在线观看| 一本色道久久综合亚洲精品不| 欧美成人国产va精品日本一级| 欧美va日韩va| 在线观看亚洲a| 久久久久免费| 嫩模写真一区二区三区三州| 伊人久久大香线| 久久久久免费观看| 欧美sm视频| 亚洲国产欧美在线| 欧美成人免费在线| 亚洲福利在线观看| 亚洲美女免费精品视频在线观看| 蜜臀久久久99精品久久久久久| 在线不卡a资源高清| 久久av一区二区| 久久亚洲精品中文字幕冲田杏梨 | 午夜精品亚洲一区二区三区嫩草| 国产精品成人一区二区艾草| 亚洲网站在线播放| 午夜欧美理论片| 国产欧美精品日韩精品| 午夜精品久久久久久99热| 久久久精品国产99久久精品芒果| 国产欧美一区二区三区久久人妖| 午夜精品一区二区三区四区 | 欧美视频二区| 亚洲视频网在线直播| 销魂美女一区二区三区视频在线| 国产欧美日韩综合| 欧美中文字幕不卡| 免费观看亚洲视频大全| 91久久久久久国产精品| 欧美日韩不卡在线| 亚洲午夜久久久久久久久电影网| 欧美一区二区三区四区在线观看 | 国产日韩欧美高清| 久久久综合激的五月天| 亚洲国产视频一区| 亚洲综合色激情五月| 国产日韩欧美在线| 另类成人小视频在线| 亚洲精品国偷自产在线99热| 亚洲欧美日韩成人高清在线一区| 国产无一区二区| 免费观看一区| 一区二区三区欧美亚洲| 久久久www成人免费精品| 亚洲国产欧美另类丝袜| 欧美日韩一区在线播放| 欧美一区二区三区久久精品| 欧美激情2020午夜免费观看| 在线视频欧美日韩精品| 国产欧美日韩亚州综合| 久热精品视频在线观看| 日韩亚洲欧美在线观看| 久久成人久久爱| 亚洲欧洲在线免费| 国产精品日韩一区二区| 麻豆国产精品va在线观看不卡| 亚洲精品美女在线观看播放| 欧美一区二区三区四区视频| 亚洲国产va精品久久久不卡综合| 国产精品v欧美精品v日韩| 久久精品日韩一区二区三区| 亚洲精品视频免费|