目錄
指標和陣列
1. 指標
1.1 如何看待下面代碼中的a變數?
1.2. 什么是指標?
2.1. 指標 和 指標變數又有何不同?我們口語中的"定義一個指標"究竟是什么意思?我們該如何理解這種說法?
2.2.1. 代碼理解
2.2.2. 代碼理解*p進行解參考的時候,p對應的左值還是右值?
3. 為什么要有指標
3.1. 回答一個問題:為何每間宿舍都要有門牌號呢?
3.2. 那又有一個問題:CPU如何拿取資料?
4. 究竟該如何理解編址
5.1. 指標的記憶體布局
5.2. 指標的解參考
5.3. int *p = NULL 和*p=NULL的區別
5.4. 如何將數值存盤到指定的記憶體地址
5.5. 編譯器的bug
本章節文章是作者通過觀看《C語言深度剖析》等各種資料總結的精華,基礎部分省略了不少,是為了讓大家能夠更加深入了解C語言的魅力!因為為了避免與之前的文章發生贅述,所以就直接講作者認為的精華部分哈!現在正文開始!
誰都不能阻擋你成為更優秀的人,
指標和陣列
1. 指標
1.1 如何看待下面代碼中的a變數?
反匯編可以看一下:
重新理解變數:
定義一個變數,本質是在記憶體中根據型別來進行開辟空間,有了空間,就必須具有地址來標識空間,來方便 CPU 進行尋 址,有了空間,就可以把資料保存起來,所以,目前我們先討論變數的空間和內容這兩個概念
1.2. 什么是指標?
指標就是地址!那么地址本質是什么呢?地址是資料,那么資料可不可以被保存在變數空間里面呢?當然可以,
保存指標 ( 地址 ) 資料的變數就叫做指標變數
2.1. 指標 和 指標變數又有何不同?我們口語中的"定義一個指標"究竟是什么意思?我們該如何理解這種說法?
嚴格意義上,指標和指標變數是不同的,指標就是地址值,而指標變數是 C 中的變數,要在特定區域開辟空間,要用來保存地址資料,還可以被取地址,( 先分開 )但是,我們經常在口語化表達的時候,又經常將這兩個概念混合,具體原因無從考證,不過個人認為與最早的 C 資料 ( 書,檔案之類) 的翻譯有關,然后,書與書之間互相借鑒,形成了這樣的說法,同時,簡化說法,也更符合人的表達習慣,估計老外也是這么想的,( 在關聯 )那么我們以后怎么認為呢?我們分開理解,但是依舊關聯使用,自己使用的時候,混合使用可以,和別人討論,最好明確概念,
2.2.1. 代碼理解
所以說我們平時使用的指標,嚴格上講是指標變數(因為有左值右值),
個人認為最好區分指標和指標變數的方法就是:
有沒有用他的空間,或是說他有沒有開辟空間,有就是指標變數,
2.2.2. 代碼理解*p進行解參考的時候,p對應的左值還是右值?
int a=10;
int *p=&a;
*p=10;
int b=*p;
左邊是*p取的是a的空間(左值),是把10放了進去,
右邊的*p取的是a的值(右值),是把a放進了b的空間,
對指標解參考,代表的是指標所指向的目標,
結論:指標就是地址,指標變數是一個變數,變數內部保存指標(地址)資料,
3. 為什么要有指標
3.1. 回答一個問題:為何每間宿舍都要有門牌號呢?
如果要一個外人來宿舍找你,是不是有門牌號更好找?結論:提高查找效率,
CPU在記憶體中尋址的基本單位是多大?在32位機器下,最多能夠識別多大的物理記憶體?既然CPU尋址按照位元組尋址,但是記憶體又很大,所以,記憶體可以看做眾多位元組的集合
其中,每個記憶體位元組空間,相當于一個學生宿舍,位元組空間里面能放8個位元位,就好比同學們住的八人間,每個人是一個位元位,每間宿舍都有門牌號就等價于每個位元組空間對應的地址,即該空間對應的指標,那么,為何要存在指標呢?為了CPU尋址的效率,如果沒有,該怎么找在位元組空間中的資料呢?
3.2. 那又有一個問題:CPU如何拿取資料?
CPU首先要先知道拿取資料的地址,這個地址是記憶體通過地址總線傳輸給CPU的,然后CPU拿著這個地址又通過資料總線去記憶體讀取資料,
4. 究竟該如何理解編址
首先,必須理解,計算機內是有很多的硬體單元,而硬體單元是要互相協同作業的,所謂的協同,至少相互之間要能夠進行資料傳遞,但是硬體與硬體之間是互相獨立的,那么如何通信呢?答案很簡單,用"線"連起來,而CPU和記憶體之間也是有大量的資料互動的,所以,兩者必須也用線連起來,不過,我們今天關心一組線,叫做地址總線,CPU訪問記憶體中的某個位元組空間,必須知道這個位元組空間在記憶體的什么位置,而因為記憶體中位元組很多,所以需要給記憶體進行編址(就如同宿舍很多,需要給宿舍編號一樣)計算機中的編址,并不是把每個位元組的地址記錄下來,而是通過硬體設計完成的,鋼琴 吉他 上面沒有寫上“都瑞咪發嗦啦”這樣的資訊,但演奏者照樣能夠準確找到每一個琴弦的每一個位置,這是為何?因為制造商已經在樂器硬體層面上設計好了,并且所有的演奏者都知道,本質是一種約定出來的共識!硬體編址也是如此我們可以簡單理解,32位機器有32根地址總線,每根線只有兩態,表示0,1【電脈沖有無】,那么一根線,就能表示2中含義,2根線就能表示4中含義,依次類推,32根地址線,就能表示2^32中含義,每一種含義都代表一個地址,地址資訊被下達給記憶體,在記憶體內部,就可以找到改地址對應的資料,將資料在通過資料總線傳入CPU內暫存器,
地址總線上發出的地址資訊
5.1. 指標的記憶體布局
int a = 10;
int *p = &a;
1.兩個,一個整型變數a,一個指標變數p,
2.取的最低的地址,訪問就是從最低的地址連續訪問4個位元組,(int)
3.如下圖:

5.2. 指標的解參考
我們可以通過這些圖看理解代碼的意思:

那又有一個問題:*p用的是左值還是右值?
int a=10; int*p=&a;
*p:*是一個運算子,*p的那一個運算式,*p使用的是左值還是右值?

(看下圖)雖然我們把地址放到了變數p里面,但是在我們用的時候是直接訪問該變數里面的地址,是直接訪問目標地址的,所以右邊的式子是等于左邊的(上面的圖), 所以我們對指標解參考相當于使用的指標變數的右值(地址;內容),所以說對指標變數解參考的時候,是直接訪問該指標變數里面保存的地址所指向的變數就可以了,其實也就是之前說的:在同型別情況下,對指標解參考,代表指標所指向的目標(a的值,10),(當然這里說p也是直接訪問地址,那為什么還要p呢?其實就是為了方便書寫,額,可能還有好理解!)
這種情況也和上面是一樣的:

什么是直接訪問和間接訪問?
5.3. int *p = NULL 和*p=NULL的區別

p是變數,在這里充當左值也就是說把NULL放進p的空間,即p的值從01改為了全0,

此時的*p為左值,充當a的空間,
PS:


但是含義不同(型別不同),所以使用不準確可能會有報警,
5.4. 如何將數值存盤到指定的記憶體地址
知道了指標的本質就是地址,地址就是資料,那么我們可以直接通過地址資料對變數進行訪問嗎?題外話:大部分技術書,一定是落后于行業的,這本書也是,目前主流的編譯器和作業系統,為了安全,已經有了很多記憶體保護的機制,我們目前的win和Linux都有堆疊隨機化這樣的機制來方式黑客對用戶資料地址進行預測,當然,還有其他的堆疊保護機制,比如“金絲雀”技術之類的,經過試驗,目前vs2013和Centos7上,使用C語言定義的區域變數,在每次運行的時候,地址都是不同的,經過試驗發現, 定義全域變數,每次更改代碼,地址也會發生變化,所以這個實驗沒法正確做出來,但是程式崩潰,也能說明問題,//demo#include<stdio.h>#include <windows.h>int main (){int a = 10 ; // 假設 a 變數的地址是 0x12345678 ,那么訪問 a 變數,還可以直接通過指標方式進行訪問printf ( "%d\n" , * ( int* ) 0x12345678 ); // 本質是一種直接尋址的方式* ( int* ) 0x12345678 = 100 ; // 本質是一種直接尋址的方式// 所以, C 語言通過 int*p = &a; 這種指標變數的方式,訪問目標資料有什么好處呢?system ( "pause" );return 0 ;}

下面ok,在此我們就不需要關心a的地址是什么,所以指標的存在給我們堆疊隨機化這樣的技術存在了可能,我們只需要知道p指向a就可以了,所以用指標比直接用地址要好,
5.5. 編譯器的bug
三步除錯的結果:

再看:
p開始為0,p第一次指向自己,第二次把值改為10,第三次把值改為20,

今天的內容就到這里了哈!!!
要是認為作者有一點幫助你的話!
就來一個點贊加關注吧!!!當然訂閱是更是求之不得!
最后的最后謝謝大家的觀看!!!
你們的支持是作者寫作的最大動力!!!
下期見哈!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/386665.html
標籤:其他

