指針、句柄、引用的區別(2)

那是什麼是句柄?他和指針有什麼異同?下面是我在網上找的專業文章,介紹指針和句柄的異同。這也是我經常分不清楚的概念。我一直認爲當某個句柄被申請時,就是分配了某個對象在內存中的某種形式,用戶拿着這個句柄可以訪問和操作這塊內存,操作方法取決於句柄類型。感覺很高深,所以也就沒有去深究。
句柄和指針的區別

當把硬盤上的資源調入內存以後,將有一個句柄指向它,但是句柄只能指向一個資源。而且句柄知道所指的內存有多大。還有指針,指針指向地址,它不知道分配的內存有多大。

但是如果你定義一個句柄,然後在VC裏面右擊鼠標,選擇”go to definition of handle”,你會發現它的本質就是一個指針,但是它的作用不同於指針。它和通常意義上的指針是有區別的。句柄借用了指針的思想,有它的邏輯特點,但沒有它的物理功能。句柄是WINDOWS分配給窗口等資源的唯一標識,是一個整數。

一、書上定義:

<<Microsoft Windows 3 Developer”s Workshop>>(Microsoft Press,by Richard Wilton)

在Windows環境中,句柄是用來標識項目的,這些項目包括:模塊(module)、任務(task)、實例?(instance)、文件(file)、內存塊(block of memory)、菜單(menu)、控制(control)、字體(font)、資源(resource),包括圖標(icon),光標?(cursor),字符串(string)等、GDI對象(GDI object),包括位圖(bitmap),畫刷(brush),元文件(metafile),調色板(palette),畫筆(pen),區域?(region),以及設備描述表(device context)。

<<WINDOWS編程短平快>>(南京大學出版社):

句柄是WONDOWS用來標識被應用程序所建立或使用的對象的唯一整數,WINDOWS使用各種各樣的句柄標識諸如應用程序實例,窗口,控制,位圖,GDI對象等等。WINDOWS句柄有點象C語言中的文件句柄。

二、MFC源代碼

#ifdef STRICT
typedef void *HANDLE;
#define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
#else
typedef PVOID HANDLE;
#define DECLARE_HANDLE(name) typedef HANDLE name
#endif

DECLARE_HANDLE(HMODULE);
DECLARE_HANDLE(HINSTANCE);
DECLARE_HANDLE(HLOCAL);
DECLARE_HANDLE(HGLOBAL);
DECLARE_HANDLE(HDC);
DECLARE_HANDLE(HRGN);
DECLARE_HANDLE(HWND);
DECLARE_HANDLE(HMENU);
DECLARE_HANDLE(HACCEL);
DECLARE_HANDLE(HTASK);

三、理解:

句柄是一個32位的整數,實際上是windows在內存中維護的一個對象(窗口等)內存物理地址列表的整數索引。因爲windows的內存管理經常會將當前空閒對象的內存釋放掉,當需要時訪問再重新提交到物理存儲,所以對象的物理地址是變化的,不允許程序直接通過物理地址來訪問對象。程序將想訪問的對象的句柄傳遞給系統,系統根據句柄檢索自己維護的對象列表就能知道程序想訪問的對象及其物理地址了。句柄是一種指向指針的指針。我們知道,所謂指針是一種內存地址。應用程序啓動後,組成這個程序的各個對象是駐留在內存的。如果簡單地理解,似乎我們只要獲知這個內存的首地址,那麼就可以隨時用這個地址訪問對象了。但是,如果真這麼認爲,那麼就大錯特錯了。我們知道windows是一個虛擬內存爲基礎的操作系統。在這種情況下,windows內存管理器經常在內存中來回移動對象,以此來滿足各種應用程序的內存需要,對象被移動意味着它的地址變化了。如果地址總是如此的變化,我們應該去那裏找對象呢?爲了解決這個問題,windows操作系統爲各個應用程序騰出一些內存地址,用來專門登記各個應用對象在內存中的地址變化,而這個地址(存儲單元的位置)本身是不變的。windows內存管理器移動對象在內存中的位置後,把對象新的地址告知這個句柄地址來保存。這樣我們只需要記住這個句柄地址就可以間接地知道對象具體在內存中哪個位置了。這個地址是在對象裝載(load)時由系統分配的,當系統卸載時又釋放給系統。句並地址(穩定)—–>記載着對象在內存中的地址——–>對象在內存中的地址(不穩定)—–>實際對象。但是必須注意,程序每次重新啓動,系統不保證分配跟這個程序的句柄還是原來哪個句柄,而絕大多數情況下的確不一樣。假如我們把進入電影院看電影看成是一個應用程序的啓動運行,那麼系統給應用程序分配的句柄總是不一樣,這和每次電影院給我們的門票總是不同的座位是一個道理。

因此,句柄和指針其實是兩個截然不同的概念。windows系統用句並標記系統資源,用句並隱藏系統信息。你只需要知道有這個東西,然後去調用它就行了,它是32bit的uint。指針則標記某個物理內存的地址,是不同的概念。

(摘自:歐立奇,劉洋,段韜?編著的《程序員面試寶典?》)

指針對應着一個數據在內存中的地址,得到了指針就可以自由地修改該數據。Windows並不希望一般程序修改其內部數據結構,因爲這樣太不安全。所以Windows給每個使用GlobalAlloc等函數聲明的內存區域指定一個句柄(本質上仍是一個指針,但不要直接操作它),平時你只是在調用API函數時利用這個句柄來說明要操作哪段內存。當你需要對某個內存進行直接操作時,可以使用GlobalLock鎖住這段內存並獲得指針來直接進行操作。

句柄是指針的“指針”,使用句柄主要是爲了利於windows在進程內存地址空間移動分配的內存塊,以防止進程的內存空間被撕的四分五裂而存在過多的碎片。

句柄是一些表的索引也就是指向指針的指針。間接的引用對象,windows可以修改對象的”物理”地址和?描述器的值,但是句柄的值是不變的。

句柄可以在獲得窗口的時候使用,指針可以進行調用窗口,兩個使用的地方不一樣.一個括號外,一個括號內.

從窗口指針獲取窗口句柄:GetSafeHwnd();
從窗口句柄獲取臨時窗口指針:FromHandle();
從窗口句柄獲取永久窗口指針:?FromHandlePermanent();

其實兩者被沒有關係,實際上是MFC在創建窗口的時候用鉤子函數溝住HCBT_CREATEWND消息,
然後通過CWnd::Attach()函數把二者捆綁在一起。
以後就可以用GetSafeHwnd(),FromHandle(),FromHandlePermanent()這三個函數可以互相得到了。

MFC之所以要這樣做,主要是爲了使原來的SDK面向過程的編程遍成面向對象的編程,所有的MFC的窗口都共用一窗口過程函數,在窗口過程函數裏,通過窗口句柄(HWND)找到窗口對象指針(CWnd *)從而把消息分發到窗口對象中,這樣以後就可以在窗口類中實行消息響應編程處理了。

附註:獲得窗口句柄三種方法

1.HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName)

HWND FindWindowEx(HWND hwndParent, HWND hwndChildAfter,LPCTSTR lpClassName, LPCTSTR lpWindowName)

2.HWND WindowFromPoint(POINT& Point)//獲得當前鼠標光標位置的窗口HWND

3.BOOL CALLBACK EnumChildProc(HWND hwnd,LPARAM lParam)

BOOL CALLBACK EnumChildWindows(HWND hWndParent, WNDENUMPROC lpEnumFunc,LPARAM lParam)

BOOL CALLBACK EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam)

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)

一、書上定義:

<<Microsoft Windows 3 Developer”s Workshop>>(Microsoft Press,by Richard Wilton)

在Windows環境中,句柄是用來標識項目的,這些項目包括:模塊(module)、任務(task)、實例 (instance)、文件(file)、內存塊(block of memory)、菜單(menu)、控制(control)、字體(font)、資源(resource),包括圖標(icon),光標 (cursor),字符串(string)等、GDI對象(GDI object),包括位圖(bitmap),畫刷(brush),元文件(metafile),調色板(palette),畫筆(pen),區域 (region),以及設備描述表(device context)。

<<WINDOWS編程短平快>>(南京大學出版社)

句柄和實例

句柄是WONDOWS用來標識被應用程序所建立或使用的對象的唯一整數,WINDOWS使用各種各樣的句柄標識諸如應用程序實例,窗口,控制,位圖,GDI對象等等。

句柄是一些表的索引也就是指向指針的指針,句柄和指針都是地址,句柄是Windows編程的一個關鍵性的概念,編寫Windows應用程序總是要和各種句柄打交道。所謂句柄,就是一個唯一的數,用以標識許多不同的對象類型,如窗口、菜單、內存、畫筆、畫刷、電話線路等。在Win32裏,句柄是指向一個“無類型對象”(void*)的指針,也就是一個4字節長的數據。無論它的本質是什麼,句柄並不是一個真正意義上的指針。

從構造上看,句柄是一個指針,儘管它沒有指向用於存儲某個對象的內存位置。事實上,句柄指向一個包含了對該對象進行的引用的位置。句柄的聲明是這樣的:typedef void *HANDLE,由於Windows是一個多任務操作系統,它可以同時運行多個程序或一個程序的多個副本。這些運行的程序稱爲一個實例。爲了對同一程序的多個副本進行管理,Windows引入了實例句柄。Windows爲每個應用程序建立一張表,實例句柄就好象是這張表的一個索引。

不同在於:

1、句柄所指的可以是一個很複雜的結構,並且很有可以是與系統有關的,比如說上面所說的線程的句柄,它指向的就是一個很類或者結構,他和系統有很密切的關係,當一個線程由於不可預料的原因,而終止時在系統就可以回它所佔用的資料,如CPU,內存等等,反過來想可以知道,這個句柄中的某一些項,是與系統進行交互的。由於Windows系統,是一個多任務的系統,它隨時都可能要分配內存,回收內存,重組內存。

2、指針它也可以指向一個複雜的結構,但是通常是用戶定義的,所以的必需的工作都要用戶完成,特別是在刪除的時候。 但在VC++6.0中也有一些指針,它們都是處理一些小問題才用的,如最常見的字符的指針,它也是要用戶處理的如果你動態分配了內存;但是Cstring就不要用戶處理了,它其實是VC++中的一個類,所以的操作都由成員函數完成,產生(分配)由構造函數,刪除(回收)由析構函數完成。

二、MFC源代碼:

#ifdef STRICT
typedef void *HANDLE;
#define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
#else
typedef PVOID HANDLE;
#define DECLARE_HANDLE(name) typedef HANDLE name
#endif

DECLARE_HANDLE(HMODULE);
DECLARE_HANDLE(HINSTANCE);
DECLARE_HANDLE(HLOCAL);
DECLARE_HANDLE(HGLOBAL);
DECLARE_HANDLE(HDC);
DECLARE_HANDLE(HRGN);
DECLARE_HANDLE(HWND);
DECLARE_HANDLE(HMENU);
DECLARE_HANDLE(HACCEL);
DECLARE_HANDLE(HTASK);

知道本質了吧~~~~

WINDOWS程序中並不是用物理地址來標識一個內存塊,文件,任務或動態裝入模塊的,相反的,WINDOWS API給這些項目分配確定的句柄,並將句柄返回給應用程序,然後通過句柄來進行操作。

三、理解:

句柄是一個32位的整數,實際上是windows在內存中維護的一個對象(窗口等)內存物理地址列表的整數索引。

因爲windows的內存管理經常會將當前空閒對象的內存釋放掉,當需要時訪問再重新提交到物理存儲,所以對象的物理地址是變化的,不允許程序直接通過物理地址來訪問對象。程序將想訪問的對象的句柄傳遞給系統,系統根據句柄檢索自己維護的對象列表就能知道程序想訪問的對象及其物理地址了。

句柄是一種指向指針的指針。我們知道,所謂指針是一種內存地址。應用程序啓動後,組成這個程序的各個對象是駐留在內存的。如果簡單地理解,似乎我們只要獲知這個內存的首地址,那麼就可以隨時用這個地址訪問對象了。但是,如果真這麼認爲,那麼就大錯特錯了。我們知道windows是一個虛擬內存爲基礎的操作系統。在這種情況下,windows內存管理器經常在內存中來回移動對象,以此來滿足各種應用程序的內存需要,對象被移動意味着它的地址變化了。如果地址總是如此的變化,我們應該去那裏找對象呢?爲了解決這個問題,windows操作系統爲各個應用程序騰出一些內存地址,用來專門登記各個應用對象在內存中的地址變化,而這個地址(存儲單元的位置)本身是不變的。windows內存管理器移動對象在內存中的位置後,把對象新的地址告知這個句柄地址來保存。這樣我們只需要記住這個句柄地址就可以間接地知道對象具體在內存中哪個位置了。這個地址是在對象裝載(load)時由系統分配的,當系統卸載時又釋放給系統。

句柄地址(穩定)—>記載着對象在內存中的地址—>對象在內存中的地址(不穩定)—>實際對象。

但是必須注意,程序每次重新啓動,系統不保證分配跟這個程序的句柄還是原來哪個句柄,而絕大多數情況下的確不一樣。假如我們把進入電影院看電影看成是一個應用程序的啓動運行,那麼系統給應用程序分配的句柄總是不一樣,這和每次電影院給我們的門票總是不同的座位是一個道理。

因此,句柄和指針其實是兩個截然不同的概念。windows系統用句柄標記系統資源,用句並隱藏系統信息。你只需要知道有這個東西,然後去調用它就行了,它是32bit的uint。指針則標記某個物理內存的地址,是不同的概念。

(摘自:歐立奇,劉洋,段韜 編著的《程序員面試寶典 》)

爲什麼我們需要句柄?
更準確地說,是windows需要句柄。windows需要向程序員提供必要地編程接口,在這些接口中,允許程序員訪問、創建和銷燬對象。但是,出於封裝地考慮,windows並不想向程序員返回指針。指針包含了太多的信息。首先指針給出了對象存儲的確切位置;其次,要操作一個指針,程序員必須知道指針所指對象的內部結構特徵,也即,windows必須向程序員暴露相應的數據結構,而這些數據結構也許是操作系統想向程序員隱藏的。
如果說COM技術向用戶隱藏了數據,只暴露了接口並只允許按接口定義的方法操作數據的話,句柄這種方式則允許你按自己的方式直接操作數據,但windows又不向你直接暴露數據。直接操作數據是程序員需要的,不暴露數據是windows所需要的,句柄封裝方式實現了各取所需。

句柄和指針

句柄與指針確實是完全不同的兩個概念。句柄僅僅是一個32位整數,WIN32中用於標記某個系統或進程的對象,可以理解爲對象索引(由於MS未完全公開相關技術,在一定程度上只能如此理解),這個索引更像是一種映射關係(從句柄到對象指針的映射)。
句柄可以理解爲用於指向或標識內存的一塊“資源”,這些資源如:文件(file)、內存塊(block of memory)、菜單(menu)等等。操作系統通過句柄來定位核心對象和系統資源。
指針即爲指向內存的“數據或指令”某一單元。說的確切一點,句柄實際上是一種指向某種資源的指針,但與指針又有所不同:指針對應着一個數據在內存中的地址,得到了指針就可以自由地修改該數據。
指針對應着一個數據在內存中的地址,得到了指針就可以自由地修改該數據。Windows並不希望一般程序修改其內部數據結構,因爲這樣太不安全。所以Windows給每個使用GlobalAlloc等函數聲明的內存區域指定一個句柄(本質上仍是一個指針,但不要直接操作它),平時你只是在調用API函數時利用這個句柄來說明要操作哪段內存。當你需要對某個內存進行直接操作時,可以使用GlobalLock鎖住這段內存並獲得指針來直接進行操作。
句柄是指針的“指針”,使用句柄主要是爲了利於windows在進程內存地址空間移動分配的內存塊,以防止進程的內存空間被撕的四分五裂而存在過多的碎片。
句柄是一些表的索引也就是指向指針的指針。間接的引用對象,windows可以修改對象的”物理”地址和描述器的值,但是句柄的值是不變的。
句柄可以在獲得窗口的時候使用,指針可以進行調用窗口,兩個使用的地方不一樣.一個括號外,一個括號內.

 

從窗口指針獲取窗口句柄:GetSafeHwnd();
從窗口句柄獲取臨時窗口指針:FromHandle();
從窗口句柄獲取永久窗口指針: FromHandlePermanent();

其實兩者被沒有關係,實際上是MFC在創建窗口的時候用鉤子函數溝住HCBT_CREATEWND消息,
然後通過CWnd::Attach()函數把二者捆綁在一起。
以後就可以用GetSafeHwnd(),FromHandle(),FromHandlePermanent()這三個函數可以互相得到了。

MFC之所以要這樣做,主要是爲了使原來的SDK面向過程的編程遍成面向對象的編程,所有的MFC的窗口都共用一窗口過程函數,在窗口過程函數裏,通過窗口句柄(HWND)找到窗口對象指針(CWnd *)從而把消息分發到窗口對象中,這樣以後就可以在窗口類中實行消息響應編程處理了。

四、隱喻:

CSDN上有人說過:牧童遙指杏花村。
牧童的手爲指針,杏花村的牌子爲句柄,杏花村酒店爲對象的實例.
句柄是資源在創建過程中由Windows賦予的,它就是代表這個資源的。

而指針實質上是某個變量或者對象所在內存位置的首地址,是指向對象的。

一個是指向,一個是代表,二者是不同的。

一個是直接找到對象(指針),一個是間接找到對象(句柄)。

例如,杏花村可以搬家(實際上程序運行過程中,資源在內存中的地址是變化的),那麼牧童的手的指向也就不同(指針)了,然而即使搬了家,“杏花村”這塊牌匾是不變的,通過打聽“杏花村”這個名稱,還是可以間接找到它的(地址)。
另外一個例子:

HANDLE的本意是把柄,把手的意思,是你與操作系統打交道的東東。

舉個通俗的例子,比如你考上了大學,入學後,學校(操作系統)會給你一個學生證號。注意,這個號碼是學校指定的,你無法自選。有了這個號碼(學生證,假設一證多用)享受學校提供的服務:如你就可以去圖書館借書,去食堂吃飯,去教室上課等等。但你不能到食堂裏買啤酒,因爲學校不允許這種服務。而在計算機中系統提供的服務就是API調用,你有了HANDLE,就可以理直氣壯地向系統提出調用API的服務。
而指針的權力就大多了,有了指針你可以到處去喝酒,打架,學校(操作系統)管不着,所以句柄和指針的區別在於句柄只能調用系統提供的服務。

而句柄雖然是一個能相互區別的號碼,但與我們普通的ID號又有區別,普通的ID號是可以由程序員自己定義的,而句柄不行,它是對象生成時系統指定的,是爲了區別系統中存在的各個對象,這個句柄不是由程序員符給的.

附註:獲得窗口句柄三種方法

1.HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName)

HWND FindWindowEx(HWND hwndParent, HWND hwndChildAfter,LPCTSTR lpClassName, LPCTSTR lpWindowName)

2.HWND WindowFromPoint(POINT& Point)//獲得當前鼠標光標位置的窗口HWND

3.BOOL CALLBACK EnumChildProc(HWND hwnd,LPARAM lParam)

BOOL CALLBACK EnumChildWindows(HWND hWndParent, WNDENUMPROC lpEnumFunc,LPARAM lParam)

BOOL CALLBACK EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam)

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)

附:句柄種類

HACCEL Handle to an accelerator table.
HANDLE Handle to an object.
HBITMAP Handle to a bitmap.
HBRUSH Handle to a brush.
HCONV Handle to a dynamic data exchange (DDE) conversation.
HCONVLIST Handle to a DDE conversation list.
HCURSOR Handle to a cursor.
HDC Handle to a device context (DC).
HDDEDATA Handle to DDE data.
HDESK Handle to a desktop.
HDROP Handle to an internal drop structure.
HDWP Handle to a deferred window position structure.
HENHMETAFILE Handle to an enhanced metafile.
HFILE Handle to a file opened by OpenFile, not CreateFile.
HFONT Handle to a font.
HGDIOBJ Handle to a GDI object.
HGLOBAL Handle to a global memory block.
HHOOK Handle to a hook.
HICON Handle to an icon.
HIMAGELIST Handle to an image list.
HIMC Handle to input context.
HINSTANCE Handle to an instance.
HKEY Handle to a registry key.
HKL Input locale identifier.
HLOCAL Handle to a local memory block.
HMENU Handle to a menu.
HMETAFILE Handle to a metafile.
HMODULE Handle to a module.
HMONITOR Handle to a display monitor.
HPALETTE Handle to a palette.
HPEN Handle to a pen.
HRGN Handle to a region.
HRSRC Handle to a resource.
HSZ Handle to a DDE string.
HWINSTA Handle to a window station.
HWND Handle to a window

Trackback:?http://tb.blog.csdn.net/TrackBack.aspx?PostId=2071949/

發佈了151 篇原創文章 · 獲贊 12 · 訪問量 54萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章