酒店房間和 C++ 局部變量的作用域

本文由 伯樂在線 - 菜鳥浮出水 翻譯自 StackOverflow。歡迎加入 技術翻譯小組。轉載請參見文章末尾處的要求。

問題:Can a local variable’s memory be accessed outside its scope? 有一段局部變量的內存,可以從其范圍之外訪問它么?

如下代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
int *foo()
{
????int a = 5;
????return &a;
}
int main()
{
????int *p = foo();
????cout << *p;
????*p = 8;
????cout << *p;
}

這樣的代碼可以正常執行,而且沒有任何運行時的異常!

輸出是 5 8

這是怎么回事?難道局部變量在函數外也可以被訪問嗎?

?

來自微軟資深軟件工程師?Eric Lippert?的最佳答案(3200+贊):

你在酒店里租了一間房。你把一本書放進了桌子的第一個抽屜里,然后就去睡覺了。當你第二天早上醒來時,你假裝忘記去還鑰匙了。你偷了房間的鑰匙!

一周之后,你回到了酒店,但沒有入住,你用偷來的鑰匙溜進了你上次入住的房間,并查看了那個抽屜。你的書還在那里。是不是很令人吃驚!

這是怎么回事呢?難道一個酒店房間的抽屜不是應該無法被一個沒有入住這個房間的人看到嗎?

好吧,明顯的是,這種情況在真實世界中當然會發生。在你不入住這個房間的時候,這里面沒有任何神秘的力量把你的書弄消失掉,也沒有魔法能夠阻止你用偷來的鑰匙進入房間。

酒店的管理規章里沒有要求拿走你的書。你也沒有跟他們說如果你落下了一本書,他們可以幫你撕毀它。如果你用偷來的鑰匙非法進入了你上次的房間,并且沒有被酒店的安保系統發現。你也沒有跟他們說如果你之后嘗試溜進房間,他們應該阻止你。不過事實上,你確實簽了一份協議規定你保證不會偷偷溜回房間。只不過你打破了協議。

在這種情況下任何事情都有可能發生。如果你運氣好的話,那本書可能還在那里。其他人的書也可能在那個抽屜里而你的書則被丟進了酒店的火爐里。也可能當你溜進去的時候正好有個人在把你的書撕成碎片。酒店可能把那張桌子連帶你的書都移走了,而把一個衣柜放在那里。這家酒店也可能正好要被拆除,換成一個足球場,在你溜來溜去的時候。你可能會在一場爆破中死去。

當你離開酒店而偷了房間的鑰匙的時候,你不知道將會發生什么。你放棄了去生活在一個可靠的,安全的世界里,因為你選擇去打破系統的規則。

C++不是一門安全的語言。你可以非常輕松就打破這個系統的規則。如果你嘗試去做一些非法并且愚蠢的事情,比如你回到那個你已經不入住的房間,并想要去查看那張也許已經不存在的桌子。C++不會阻止你的。比C++更加安全的語言通過限制你的能力來解決這個問題,比如通過更加嚴格的控制房間鑰匙。

【更新】:

我的老天。這個答案獲得了這么多的關注。(我不知道為什么,我只是覺得這樣比喻比較有趣, 不過管他呢。)

我認為在經過了更加技術性的思考之后更新一下這個答案是必要的。

編譯器的工作是生成代碼來管理這個程序數據擁有的內存。有很多方式來生成管理內存的代碼,但是這么多年來有兩個基本的技術是必須要知道的。

第一個是擁有一片長期存在的區域,這片存儲區域里的每一個字節,他們的生命周期比較長。生命周期的意思就是它們能夠被程序訪問的時期。這類內存沒辦法提前進行預估。編譯器生成一種叫堆管理器的代碼,它知道如何在需要的時候動態的分配內存,當內存不再被需要的時候釋放掉他們。

第二個是擁有一片短期存在的區域,這片存儲區域里的每一個字節都可以提前進行預估。而且比較特殊的是,這片區域的生命周期遵循一種嵌套模式。也就是說,在這片區域中擁有最長生命周期的變量,它所分配的內存地址被它之后分配的那些生命周期較短的變量所重用。

局部變量就是第二種情況。當調用一個函數時,它的局部變量便被生成了。當這個函數調用另外一個函數時,新函數的局部變量也被生成了。這些變量會在第一個函數的局部變量之前被釋放掉。這些局部變量的內存地址的開始和結束可以提前被計算出來。

因為這個原因,局部變量經常被分配到棧數據結構里,因為一個棧的特點是第一個入棧的元素將會最后一個出棧。

這就好像酒店決定只能按照順序進行房間的出租。你沒辦法離開,除非你之前所有房間號比你大的人都走了。

所以,讓我們來想一下棧的操作過程。在很多操作系統中,每一個線程都有一個棧,并且棧的大小是一個可變的確定大小。當你調用一個函數的時候,相關的內容被壓入棧內。當你把一個這個棧的指針傳出這個函數時,就像上面的提問者所干的一樣。那個指針只是指向全部有效的數百萬個字節內存塊的中間。在我們的類比中,當你離開酒店的時候,你只是離開了當前被占用的數字最大的房間。如果沒有人在你之后入住,你又非法地回到了這個房間。你所有的東西肯定都還在這個酒店的房間里。

我們用棧作為臨時存儲因為它們非常廉價并且容易實現。C++的實現沒有規定一定要用棧來存儲局部變量,你可以使用堆來存儲它們,不過沒有人這么干,因為那樣做會使得程序變得很慢。

C++也沒有規定在你離開棧之后需要清掉棧里的內容,所以你可以在之后非法地回到棧里找到你之前的內容。當然編譯器如果生成代碼,一旦你不再使用了就把棧里的所有內容都清零,這是完全合法的。不需要再解釋為什么了,因為這樣做代價非常高。

C++沒有規定要確保當棧變小時,之前有效的內存地址依然有效。C++的實現也允許告訴操作系統“我們已經不再需要棧的個內存頁了。除非我說,否則當有任何人要訪問這個之前有效的棧的內存頁的時候拋出一個異常并結束程序”。再次,一般的實現也沒有這么做,因為這么說使程序變慢而且沒有必要。

相反,大多數時候,一般的C++實現允許你犯錯然后避免它。直到有一天,一些真正非常令人恐怖的錯誤出現了然后把整個程序弄崩潰了。

這樣做是有問題的。C++里有如此多的規則而又如此輕易就可以打破它們。我自己就有好多次這樣的經歷。更糟的是,這種問題往往是表面的,當你發現內存地址沖突了之后去檢查內存,卻發現它們在很長時間內又是正確的。所以你很難知道到底是哪個地方出錯了。

那些內存安全的語言通過限制你的能力來解決這個問題。在規范的C#里,沒有任何辦法去獲取一個局部變量的內存地址,然后返回它或者是存儲它等以后再用。你可以獲取一個局部變量的內存地址,但是語言被很好的設計了,你不可能在局部變量生命周期之后還能夠使用它。為了取得局部變量的內存地址并把它返回,你必須要把編譯器設置為一個特殊的不安全的模式,并且在你的程序里寫上“unsafe”關鍵字。這可以幫助提醒你,你正在做一些不安全的可能會打破規則的事情。

更進一步閱讀:
當C#返回引用時做了些什么?

http://blogs.msdn.com/b/ericlippert/archive/2011/06/23/ref-returns-and-ref-locals.aspx

為什么我們用棧來管理內存?C#里值的類型是否一直存儲在棧里?虛擬內存是如何工作的?以及更多的關于C#內存管理是如何工作的。這里許多文章都對C++程序員有幫助。

http://blogs.msdn.com/b/ericlippert/archive/tags/memory+management/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/447555.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/447555.shtml
英文地址,請注明出處:http://en.pswp.cn/news/447555.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

解決:Access denied for user ‘root’@‘localhost’(using password: YES)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 我報這個錯的原因很簡單&#xff1a;數據庫連接時密碼輸入錯誤。把密碼確認對了&#xff0c;就連接成功了。

【歷史回顧】Linux發展一覽

我們周圍到處都有Linux的身影&#xff0c;在家中、公司里、大學、實驗室&#xff0c;太空空間站。現在的Linux已經從一個個人愛好發展成了一場計算機革命。在這篇文章里&#xff0c;我們向你展示最完整的Linux 23年來發展的歷史年表。我們周圍到處都有Linux的身影&#xff0c;在…

專訪聯想謝政維:功耗和價格是天蝎項目最大障礙!

摘要&#xff1a;與國外OpenStack和OCP&#xff08;開放計算項目&#xff09;頻頻見諸報端不同&#xff0c;中國的天蝎計劃看起來神神秘秘&#xff0c;今天專訪聯想謝政維&#xff0c;他幫我們解密了目前天蝎項目的一些基本情況&#xff0c;以及他對未來天蝎發展的觀點。 服務…

解決:Chrome 插件安裝時提示 程序包無效:“CRX_HEADER_INVALID“

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 今天在添加谷歌插件的時候&#xff0c;卻發現谷歌瀏覽器顯示 程序包無效&#xff1a;"CRX_HEADER_INVALID"&#xff0c;現整理…

65種GPU性能測試,AMD開源驅動領先!

摘要&#xff1a;近日&#xff0c;Phoronix測試了65 種不同的GPU使用開源驅動的OpenGL性能&#xff0c;測試的GPU型號包括Intel HD Graphics、AMD Radeon、AMD FirePro和NVIDIA GeForce系列。結果顯示&#xff0c;相比之下&#xff0c;AMD開源驅動領先于NVIDIA。 【編者按】20…

1分鐘看懂:java 項目中 VO 、DTO、Entity,各自是在什么情況下應用的

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1、entity 里的每一個字段&#xff0c;與數據庫相對應&#xff0c; 2、vo 里的每一個字段&#xff0c;是和你前臺 html 頁面相對應&…

指尖上的藝術——如何運用代碼發揮無限創意

摘要&#xff1a;計算機為我們的生活帶來了不計其數的變化&#xff0c;現在&#xff0c;我們不妨也把它與藝術結合起來&#xff0c;去從機器的角度來為生活增添活力與色彩&#xff0c;開拓一個無限可能的藝術創作新境界。 寫在前面 生成的藝術&#xff0c;指尖上的藝術&#…

解決:XXX類存在 卻報錯 Unkonwn class:‘XXX‘ 、@Autoeired 注入失敗

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 我的情況如圖&#xff0c;有一個CuxxxxManager 類明明是就在本工程中的&#xff0c;我想注入到對應 Service 中&#xff0c;卻直接報…

精益設計:“紙上談兵”的說服力

摘要&#xff1a;當你和團隊通宵達旦地輸出大量完整而精美的方案時&#xff0c;可客戶卻根本不買帳。這究竟是溝通出了問題&#xff0c;還是我們犯了錯&#xff1f;如何提高設計效率、提升溝通及如何設計出更好的用戶體驗是每個互聯網從業者都值得關注的問題。 【編者按】此前…

《西線無戰事》:合上書的那一刻:只想痛哭

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 昨晚看完此書&#xff0c;看的時候&#xff0c;我一直以為他是在回憶&#xff0c;然而&#xff0c;最終卻沒有一個人活下來&#xff0c;…

如何兼職創業并避免風險

大多數人開始創建第一家公司時&#xff0c;仍然有一份正常工作。 這是有道理的&#xff1a;你不需要貸款&#xff0c;并且你并不需要資金。如果你“失敗”了&#xff0c;你失去僅僅是時間&#xff0c;但考慮到你得到的樂趣&#xff0c;經歷&#xff0c;以及一切你已經學會的東西…

得當前時間的工具類

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 Date datenew Date();DateFormat formatnew SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String timeformat.format(date);不同…

8款最受歡迎的HTML5/CSS3應用及代碼

新的一周開始&#xff0c;小編也將繼續為大家分享精彩的HTML5應用&#xff0c;還有CSS3和jQuery方面的東西。今天給大家帶來的是8款最受歡迎的HTML5/CSS3應用及代碼&#xff0c;一起來看看吧。 1、基于HTML5 Canvas的圖表插件Chart.js chart.js是一款基于HTML5 Canvas的圖表插…

Java 8: LocalDate、LocalTime 、LocalDateTime 處理日期時間

JDK8中&#xff0c;新增了三個類&#xff0c;用以處理時間。 LocalDate專門處理日期&#xff0c;LocalTime專門處理時間&#xff0c;LocalDateTime包含了日期和時間&#xff0c;而且對于很多復雜的問題&#xff0c;都提供了現成的方法&#xff0c;比如&#xff1a;獲取2017年1…

超負荷寫代碼 = 慢性自殺

本文是html5tricks原創翻譯&#xff0c;轉載請看清文末的轉載要求&#xff0c;謝謝合作&#xff01; 也許你會認為我是個故弄玄虛的標題黨&#xff0c;不過我需要澄清一下。首先&#xff0c;這是真的&#xff0c;超負荷寫代碼的確意味著慢性自殺。然后問題就來了&#xff0c;“…

Dubbo + RestEasy 實現文件上傳與下載

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 DubboRestEasy實現文件上傳與下載 DubboRestEasy實現文件上傳與下載 Maven依賴 注解 代碼實現 1、 上傳 2、 下載 3、 上傳、下載請求參…

程序員必須知道的10大基礎實用算法及其講解

算法一&#xff1a;快速排序算法 快速排序是由東尼霍爾所發展的一種排序算法。在平均狀況下&#xff0c;排序 n 個項目要Ο(n log n)次比較。在最壞狀況下則需要Ο(n2)次比較&#xff0c;但這種狀況并不常見。事實上&#xff0c;快速排序通常明顯比其他Ο(n log n) 算法更快&a…

《 雙城記 》:無數的平民擁有的只是和她同樣的眼睛 ...

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 PS&#xff1a;此書我沒有買&#xff0c;是在喜瑪拉雅上聽完的&#xff0c;主播&#xff1a;高樂高 。 1、卡頓&#xff1a; 一個才華斐…

Git遠程操作詳解

摘要&#xff1a;Git是目前最流行的版本管理系統&#xff0c;學會Git幾乎成了開發者的必備技能。本文詳細介紹5個Git命令&#xff0c;它們的概念和用法&#xff0c;理解了這些內容&#xff0c;你就會完全掌握Git遠程操作。 【編者按】Git是一個分布式版本控制&#xff0f;軟件…

《千只鶴》:面對愛,我們永遠還行走在成長的路上 ...

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 PS&#xff1a;之前我一直以為書名是 千紙鶴&#xff0c;直到真正拿到書才注意到&#xff0c;原來書名是《千只鶴》... 包括 2 部分&am…