指針與句柄的區(qū)別?
句柄是一個32位的整數(shù),實際上是Windows在內(nèi)存中維護的一個對象內(nèi)存物理地址列表的整數(shù)索引。
因為Windows的內(nèi)存管理經(jīng)常會將空閑對象的內(nèi)存釋放掉,當需要訪問時再重新提交到物理內(nèi)存,所以對象的物理地址是變化的,不允許程序直接通過物理地址來訪問對象。
程序?qū)⑾朐L問的對象的句柄傳遞給系統(tǒng),系統(tǒng)根據(jù)句柄檢索自己維護的對象列表就能知道程序想訪問的對象及物理地址了。
句柄是一種指向指針的指針。我們知道,所謂指針是一種內(nèi)存地址。應(yīng)用程序啟動后,組成這個程序的各對象是駐留在內(nèi)存的。如果簡單地理解,似乎我們只要獲知這個內(nèi)存的首地址,那么就可以隨時用這個地址訪問對象。
但是,Windos是一個以虛擬內(nèi)存為基礎(chǔ)的操作系統(tǒng)。在這種系統(tǒng)環(huán)境下,Windows內(nèi)存管理器經(jīng)常在內(nèi)存中來回移動對象,以此來滿足各種應(yīng)用程序的內(nèi)存需要。
對象被移動意味著它的地址變化了。如果內(nèi)存總是如此變化,我們該到哪里去找該對象呢?為了解決這個問題,Windows操作系統(tǒng)為各應(yīng)用程序騰出一些內(nèi)存地址,用來專門登記各應(yīng)用對象在內(nèi)存中的地址變化,而這個地址本身是不會變的。
Windows內(nèi)存管理器移動對象在內(nèi)存中的位置后,把對象新的地址告知這個句柄地址來保存。
這樣我們只需要記住這個句柄地址就可以間接地知道對象具體在內(nèi)存中的哪個位置。
這個地址是在對象裝載時由系統(tǒng)分配的,當系統(tǒng)卸載時又釋放給系統(tǒng)。但是,必須注意的是,程序每次重新啟動,系統(tǒng)不能保證分配給這個程序的句柄還是原來的那個句柄。
而且絕大多數(shù)情況下的確不一樣。
假如我們把進入電影院看電影看成是一個應(yīng)用程序的啟動運行,那么系統(tǒng)給應(yīng)用程序分配的句柄總是不一樣,這和每次電影院售給我們的門票總是不同的座位是一樣的道理。
句柄實際上是一種指向某種資源的指針,但與指針又有所不同:指針對應(yīng)著一個數(shù)據(jù)在內(nèi)存中的地址,得到了指針就可以自由地修改該數(shù)據(jù)。
Windows并不希望一般程序修改其內(nèi)部數(shù)據(jù)結(jié)構(gòu),因為這樣太不安全。
所以Windows給每個使用GlobalAlloc等函數(shù)聲明的內(nèi)存區(qū)域指定一個句柄(本質(zhì)上仍是一個指針,但不要直接操作它,如果要操作該段內(nèi)存,對于GlobalAlloc創(chuàng)建的可移動的內(nèi)存,需要配合使用GlobalLock、GlobalUnlock),平時你只是在調(diào)用API函數(shù)時利用這個句柄來說明要操作哪段內(nèi)存。
HDC 是設(shè)備描述表句柄CDC 是設(shè)備描述表類你使用CreateThead后函數(shù)會返回一個句柄,它代表這個線程。
你可能會調(diào)用SetThreadPriority去修改線程的優(yōu)先級,使用ResumeThread去重新開始一個線程的運行,在調(diào)用這些函數(shù)時你都需要告訴系統(tǒng)你到底要操作哪個線程,而剛才返回的句柄派上用處了,這些函數(shù)的第一個參數(shù)就是線程的句柄。