簡體   English   中英

我可以通過指針訪問受限的 memory 空間嗎

[英]Can I access restricted memory space by pointers

所以當我了解指針時,我有一個瘋狂的想法:如果我打印一個指針,它給出了 memory 中的地址,如果是這樣,計算機的 memory 的哪一部分實際上被讀取了? 例如

#include <iostream>
using namespace std;
main()
{
   int a=1;
   int* ptr=&a;
   int i=0;
   while(1)
   {
      cout<<(ptr+i)<<"\t"<<*(ptr+i)<<"\n";
      i++;
   }
}

當我運行這個程序時(考慮到沒有錯誤),它給了我來自&a ++ 的地址。 所以它會遍歷 memory 中的地址,並顯示那里存儲的信息。 所以我的問題是,正在讀取 memory 的哪一部分? 我是否可以訪問每個 memory 位置(存儲在每個位置中的值)? 我可以像這樣刪除我電腦上的文件(這聽起來很愚蠢)嗎

ptr=nullptr;
while(1)
{
   *(ptr+i)=0;
   i++;
}

我不知道我的語法是否正確,盡管你明白了。 我不敢在我的電腦上運行這個...

在運行 C 或 C++ 程序時,地址可分為以下幾類:

  1. 那些已通過報告 const 限定對象地址的語言構造提供給 C 或 C++ 程序的那些。

  2. 那些已通過語言構造提供給 C 或 C++ 程序的那些,但不是從 const 限定的對象派生的。

  3. Null 地址。

  4. 那些語言實現已被環境授予獨占使用但尚未通過語言構造提供給程序的那些。

  5. 語言實現一無所知的那些。

如果代碼嘗試使用除前三個類別之外的任何地址執行任何操作,嘗試訪問前兩個類別之外的任何地址的存儲,或嘗試在任何不屬於第二類。

然而,被設計為適合低級編程任務的實現將定義超出標准規定的行為。 最值得注意的是,此類實現將“以環境的[已記錄] 時尚特征”處理類型#5 的訪問,其行為將在環境記錄它時以任何方式記錄。

例如,如果一個人正在運行針對 Commodore 64 的 C 實現,並且一個人執行*(char volatile*)0xD020 = 2; ,這將導致屏幕邊框變為紅色,因為 Commodore 64 記錄了寫入該特定地址的效果。 C 編譯器對“屏幕”、“邊框”或“紅色”等概念一無所知,但它不需要知道這些東西。 它只會對地址 0xD020 執行寫入操作,硬件將通過更改顏色控制鎖存器中的值來響應,這些鎖存器在光柵掃描處於邊界區域時進行采樣。 此類代碼僅在定義將值 2 存儲到地址 0xD020 的效果的平台上才有意義,但旨在用於低級編程的實現不應期望知道,因此也不應該關心此類構造何時會或何時會沒有用,因為程序員通常比編譯器編寫者更了解目標環境。

順便說一句,UB 可以擦除存儲介質的想法可能與以下事實有關:在 Apple //c 等某些機器上,或任何 Apple Ii 系列機器上,在插槽 6(通常位置)有軟盤 controller,訪問(甚至讀)地址 0xC0EF 軟盤驅動器運行時(包括軟盤驅動器訪問的第一秒左右內的任何時間)將導致驅動器開始覆蓋當前磁道上的數據,直到下一次訪問 0xC0EE。 盡管大多數讀取在大多數平台上都沒有副作用,但雜散讀取可能產生災難性后果的平台不僅僅是理論上的。

如果我打印一個指針,它給出了 memory 中的地址,如果是這樣,計算機的 memory 的哪一部分實際上被讀取?

解釋可執行文件如何工作的最簡單方法是,首先將可執行文件從 ROM(HHD 和/或 SSD)加載到 RAM,然后由 CPU 執行。 當您引用有效變量的指針時,它顯示的是該變量在 RAM 中的 memory 地址。

當我運行這個程序時(考慮到沒有錯誤),它給了我來自 &a ++ 的地址。 所以它會遍歷 memory 中的地址,並顯示那里存儲的信息。 所以我的問題是,正在讀取 memory 的哪一部分?

它顯示了可執行文件可能不擁有的 memory 位置。 您不應該訪問不允許的 memory:通常它會觸發運行時錯誤,說明: Access Violation Reading Location 0x...

我是否可以訪問每個 memory 位置(存儲在每個位置中的值)?

不,你不能也不應該。 那是因為操作系統不允許您讀取您不擁有的 memory,這是一個安全威脅。 如果要訪問 memory 的塊,請先分配。

我可以像這樣刪除我電腦上的文件(這聽起來很愚蠢)嗎

不,文件存儲在 ROM 中,而您在該指針中指向的內容位於 RAM 中。

我不敢在我的電腦上運行這個...

是的,您不應該也不能這樣做,因為操作系統可能會阻止您這樣做(某些操作系統可能不會嚴格執行它,即:嵌入式系統)。

代碼是對程序編譯后應該做什么的抽象描述。 此抽象描述的基礎是標准中指定的 C++ 語言。 標准中沒有任何內容指定獲取任意指針、遞增它然后取消引用它的含義(除非所有這些都發生在數組內)。 官方術語是:未定義的行為。 如果你用未定義的行為編譯代碼,任何事情都可能發生。 這有點像強迫某人將“foofoo moomoo”從英語翻譯成另一種語言。

在存在未定義行為的情況下,我們可以使用我們對 C++ 的明確定義部分的了解,並嘗試推斷編譯和運行具有未定義行為的代碼時會發生什么。 然而,這是徒勞的,因為有時編譯器可以檢測到 UB 並采取相應的行動。 如果您要求編譯器編譯您的代碼,則在生成的可執行文件中根本不需要任何指針增量(因為您的指針增量和隨后的取消引用是未定義的)。

如果您仍然想知道生成的可執行文件實際上做了什么,您需要研究編譯器的 output,例如作為匯編。 但是,當代碼具有未定義的行為時,根據編譯器、其版本和設置的不同,結果差異很大,請不要感到驚訝。

不,如果你能做到這一點,那么“限制”將毫無用處。

在過去——想想:MS-DOS 和 Windows 3.1 天——你可以這樣做。 它會覆蓋屬於其他程序的 memory,並可能導致計算機崩潰。

現代操作系統使用一種稱為虛擬 memory的技術,其中操作系統控制您的地址,每當您訪問指針 0x12345678 時,CPU 都會在頁表中查找有關0x12345 的信息。

如果那是屬於您的程序的頁面,它會將頁面地址更改為頁表所說的 - 所以現在它是 0xabcde678 - 並從該 RAM 芯片獲取數據。 這允許操作系統在您的程序沒有注意到的情況下移動頁面。 前一時刻 0x12345678 表示 0xabcde678,下一時刻表示 0x54321678,你分不清。

如果它不是屬於您的程序的頁面,則頁表中不會有新地址。 在這種情況下,CPU會引發頁面錯誤異常——它調用操作系統中的特定 function 來處理頁面錯誤。 這個 function 會看到沒有很好的缺頁原因(因為有一些很好的原因)。 它會結束您的程序並彈出一個消息框,提示“此程序遇到問題,需要關閉”。

在一般意義上,動態分配的空間通常放置在稱為堆或空閑存儲的程序段中。 您必須使用new關鍵字讓計算機從該堆空間分配 memory。

當您用完堆空間時,例如當您繼續動態分配新指針但不刪除未使用的指針時,可能會發生這樣的錯誤std::bad_alloc

另一方面,如果您嘗試訪問未動態分配的 memory。 Segmentation Fault幾乎肯定會發生。 一個非常簡單的示例是當您嘗試讀取超出范圍的數組元素時。 由於 C++ 沒有邊界檢查。 您可以輕松冒險進入危險區域並禁止非法 memory 訪問。 在您的情況下,嘗試訪問您分配的 memory 旁邊但未分配給您的 memory 會導致分段錯誤。 當然,您絕對可以打印 memory。 但是訪問 memory 並實際更改其內容是完全不同的事情。

我在 Visual Studio Community 2019 中測試了您的程序。打印了許多 memory 位置,但幾秒鍾后,拋出異常:讀取訪問沖突。 程序中的所有變量都是在堆棧上創建的。 循環的第一次迭代顯示變量“a”(值 1)的內容,該變量位於堆棧上,但下一次迭代顯示 memory 的內容也屬於程序堆棧。 最后嘗試讀取出棧的一個memory position,所以拋出異常。 回到 MS-DOS 時代,嘗試在程序堆棧之外讀取 memory 不會有任何問題,因為操作系統沒有保護 memory 位置。

當我嘗試用 '*(ptr + i) = 0;' 寫入 memory 位置時而不是閱讀它們,拋出的是寫訪問沖突異常。 在 MS-DOS 時代,這會導致覆蓋程序堆棧之外的 memory 位置,這會影響其他正在運行的程序,可能計算機會掛起,您必須重新啟動它,這在出錯時很常見是在使用指針時制作的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM