1 為什麼要有指標?

2021-08-16 14:09:05 字數 2035 閱讀 1241

(0)史前

早期的cpu(也許並沒有真正的實現)並不如今天的強大,

記憶體讀寫的指令可能只有:

「從*常數*0x1234位址處讀入1位元組到暫存器a」,

或者「把暫存器b的值寫入*常數*位址0x5678這個地方」。

那個時候沒有變數這一說,所有的記憶體讀寫都得指定好常數,也就是得把具體的數字(也稱為字面量,literal)寫死在程式裡。

換言之,你如果想清空100位元組的記憶體,而每條指令只能對記憶體中某乙個位元組進行寫入,那就得寫100條指令。隨著要處理的資料的膨脹,程式也得跟著膨脹,而這是不可接受的。

(1)流程跳轉

為了解決清空大量記憶體的問題,人們發明了控制流程的

指令

比如「如果暫存器c的值為0,則略過下一條指令」和「無條件跳轉到首位址為0x9012」的地方,從那個地方繼續執行」。

注意,指令本身也是編碼成位元組存在記憶體裡的,這就是馮諾伊曼機器最優雅的地方。這時候我們可以寫出迴圈了

(2)自修改程式

問題並沒有解決,因為就算有了迴圈,

整個程式還是只能寫常量,所以如果想要對不同的記憶體塊清零,只能寫不同的指令,對應常量值寫指令。

但是人是很聰明的。

利用「指令本身也是編碼成位元組存在記憶體裡」這個特徵,我們可以通過修改記憶體來修改指令,繼而修改行為。

舉例來說,在0x3454處存了一條指令,這條指令叫做「把0x5678開始的一位元組清零」。

比方說這條指令占用了三個位元組,第乙個位元組告訴cpu這是一條清零指令,後兩個位元組(0x3455和0x3456)存了乙個表示位址的整數,告訴cpu到底要把哪個位元組清零。顯然,這個整數會和0x5678有關,而且大多情況就是0x5678。

現在,要是cpu在某處執行了一句「把起始位址為0x3455的那個2位元組數自增1」,那實際上0x3454處的指令就變成了「把0x5679開始的一位元組清零」!

看到希望了麼,我們可以寫乙個指令,然後不斷修改這個指令,再加上流程控制的指令產生乙個迴圈,就能用一段固定長度(注意,**內容隨著執行並不固定)的**清零任意指定的一段記憶體塊!

這叫做「自修改程式」。

3)間接定址

後來的事情就很簡單了。編寫這種自修改程式極容易出錯,因為稍微改錯乙個地方指令就全改亂了,比如在上例中如果把0x3454中的數字給改了,就完全變成了另一條指令,

所以人們再次發明新的工具,叫間接定址。

所以有了這樣的指令「把暫存器a存的數字當成位址,取出該位址處的位元組放到暫存器b裡」和「把暫存器a存的數字當成位址,把暫存器b的位元組寫入到該位址處」。

回到自修改程式的那個例子裡,我們可以把「把0x5678開始的一位元組清零」換成:

「把暫存器b設為0」

「把0x5678這個數字寫到0x1234處」

「讀入0x1234的數字到暫存器a」

把暫存器a存的數字(此處即0x5678)當成位址,把暫存器b(此處為0)的位元組寫入到該位址處」

這樣實現雖然看起來費事,但是總算得到了等價的功能。另外,我們以後只需要讀寫0x1234這個記憶體就能達到改變行為的目的,而不要冒險去修改指令本身了。換言之,指令本身被固化,其行為更加穩定

今天,**雖然也在記憶體裡,也編碼成了乙個個位元組,但是一般不和資料放在一起,而且一般執行的時候是唯讀的。

假設程式設計師無限聰明(當然這種好事從來就沒有發生過:),寫的**從來不出錯,那麼間接定址是沒有必要的,因為直接寫自修改程式就行了。間接定址沒有增加任何新的功能,這點不像跳轉指令。

4)指標

哦,現在解釋指標就很簡單了,指標就是間接定址例子裡面那個0x1234的記憶體塊。它存了一片位址,而指標解引用(比如*p)

就對應的是間接定址讀寫的指令了

所以說到最後,「為什麼要有指標的」就可以化成「為什麼要有間接定址」,問題基本等價於「為什麼不直接使用自修改程式」了。

希望我說得比較明白。 

為什麼要有cgroup

linux系統中經常有個需求就是希望能限制某個或者某些程序的分配資源。也就是能完成一組容器的概念,在這個容器中,有分配好的特定比例的cpu時間,io時間,可用記憶體大小等。於是就出現了cgroup的概念,cgroup就是controller group,最初由google的工程師提出,後來被整合進l...

為什麼要有多型?

include include using namespace std 岳不群 class yuebuqun virtual void fight virtual表7示修飾的乙個成員方法時乙個虛函式,和虛繼承含義不同 string kongfu 林平之類 class linpingzhi publi...

為什麼要有 hashCode

當你把物件加入 hashset 時,hashset 會先計算物件的 hashcode 值來判斷物件加入的位 置,同時也會與其他已經加入的物件的 hashcode 值作比較,如果沒有相符的hashcode,hashset會假設物件沒有重複出現。但是如果發現有相同 hashcode 值的物件,這時會呼叫...