[英]Pointers to statically allocated objects
我試圖理解靜態分配對象的指針是如何工作的以及它們可能出錯的地方。
我寫了這段代碼:
int* pinf = NULL;
for (int i = 0; i<1;i++) {
int inf = 4;
pinf = &inf;
}
cout<<"inf"<< (*pinf)<<endl;
我感到很驚訝,因為我認為當程序離開塊並且指針指向不再存在的東西時, inf
會消失。 我想在嘗試訪問pinf
時出現分段錯誤。 在程序中的什么階段會inf
死嗎?
你的理解是正確的。 離開循環范圍時, inf
消失,因此訪問*pinf
產生未定義的行為。 未定義的行為意味着編譯器和/或程序可以執行任何操作,這可能是崩潰,或者在這種情況下可能只是簡單地進行操作。
這是因為inf
在堆棧上。 即使它超出范圍, pinf
仍然指向堆棧上的可用內存位置。 就運行時而言,堆棧地址很好,並且編譯器不會費心插入代碼來驗證您是否沒有訪問超出堆棧末尾的位置。 在為速度設計的語言中,這將是非常昂貴的。
因此,您必須非常小心,以避免未定義的行為。 C和C ++並不像Java或C#那樣非常好,非法操作幾乎總是會產生立即異常並導致程序崩潰。 程序員必須保持警惕,因為編譯器會錯過你所犯的各種基本錯誤。
你使用所謂的懸空指針 。 它將導致C ++標准的未定義行為。
它可能永遠不會死,因為pinf將指向堆棧上的某些東西 。
堆棧通常不會縮小。
修改它,你幾乎可以保證覆蓋。
如果你問這個問題:
int main() {
int* pinf = NULL;
for (int i = 0; i<1;i++){
int inf = 4;
pinf = &inf;
}
cout<<"inf"<< (*pinf)<<endl;
}
那么你所擁有的是未定義的行為。 自動分配(非非靜態)對象inf已超出范圍,並且當您通過指針訪問它時,概念上已被銷毀。 在這種情況下,任何事情都可能發生,包括它似乎“工作”。
您不一定會得到SIGSEGV(分段錯誤)。 inf
內存可能在堆棧中分配。 堆棧內存區域可能仍然分配給您的進程,因此,這可能就是您沒有遇到seg錯誤的原因。
行為是未定義的,但實際上,“破壞” int
是一個noop,因此大多數編譯器會將數字單獨留在堆棧上,直到其他東西出現以重用該特定的槽。
某些編譯器在調試模式下超出范圍時可能會將int設置為0xDEADBEEF
(或某些此類垃圾),但這不會使cout << ...
失敗; 它只會打印出荒謬的價值。
當它到達你的cout線時,內存可能會或可能不會包含4。 它可能嚴重意外地包含4。 :)
首先要做的事情是:您的操作系統只能檢測在頁面邊界上誤入歧途的內存訪問。 所以,如果你的距離是4k或8k或16k或更多。 (有一天在Linux系統上檢查/proc/self/maps
以查看進程的內存布局;允許列出范圍內的任何地址,不允許在列出的范圍之外的任何地址。受保護內存CPU上的每個現代操作系統將支持類似的機制,所以即使你對Linux不感興趣它也會很有啟發性。我只知道它在Linux上很容易。)因此,當你的數據太小時,操作系統無法幫助你。
另外,你的int inf = 4;
可能很好地存儲在程序的.rodata
, .data
或.text
段中。 靜態變量可以填充到這些部分中的任何一部分(我不知道編譯器/鏈接器如何決定;我認為它很神奇)因此它們在程序的整個持續時間內都是有效的。 在下次進入Unix系統時檢查size /bin/sh
,以了解將哪些數據放入哪些部分。 (並查看readelf(1)
以獲取太多信息readelf(1)
objdump(1)
如果您使用的是較舊的系統。)
如果你將inf = 4
改為inf = i
,那么存儲將被分配到堆棧中,你有更好的機會快速覆蓋它。
當您指向的內存頁面不再對該進程有效時,會發生保護錯誤。
幸運的是,大多數操作系統不會為每個整數的堆棧空間創建單獨的頁面。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.