第一、瞭解記憶體空間 本文章文字有點多,會有點枯燥,配合圖文一起看可以緩解枯燥,耐心閱讀哦!!! 先瞭解記憶體地址,才更好的理解指針! 我們可以把記憶體想象為成一列很長很長的貨運火車,有很多大小相同的車廂,而每個車廂正好相當於在記憶體中表示一個位元組。這些車廂裝著不同的貨物,就像我們的記憶體要存著各式各樣的數據 ...
第一、瞭解記憶體空間
本文章文字有點多,會有點枯燥,配合圖文一起看可以緩解枯燥,耐心閱讀哦!!!
先瞭解記憶體地址,才更好的理解指針!
我們可以把記憶體想象為成一列很長很長的貨運火車,有很多大小相同的車廂,而每個車廂正好相當於在記憶體中表示一個位元組
。這些車廂裝著不同的貨物,就像我們的記憶體要存著各式各樣的數據。
多啰嗦一下
我們平時在電腦上能夠聽音樂、看視頻和文章,其實看到的這些東西就是記憶體中每個“車廂”裡面的數據,這些數據最終還是由二進位0/1演變而成。
雖然視頻、文章、音樂等這些信息在我們眼裡是不同的,但對於電腦來說它們在記憶體中都是以二進位的形式來表示。
因為我們要知道去哪存或取數據,所以記憶體中每個位元組都有對應的編號,就像火車上的車廂編號一樣。而這個記憶體中每個位元組的編號就是我們常說的記憶體地址,是按一個位元組接著一個位元組的次序進行編址。如下圖所示:
凡事多問幾個為什麼?
1. 為什麼記憶體地址都有0x開頭?
0x 開頭代表以十六進位來表示的意思。
2. 為什麼我們平時看到記憶體地址是這樣的呢?如圖:
因為記憶體容量很大,容量大位元組數自然也多了,所以需要更多位來編址記憶體地址。上圖的(0x00 ...)記憶體地址這裡只是便於理解!
3. 為什麼我那麼菜呢?
哈哈哈......你心裡沒點*數嗎?
關於記憶體位元組
- 1個記憶體地址只存1個位元組 (Byte);
- 1個位元組等於8位二進位,每一位二進位的0或1,叫“比特”(bit);
- 比特是最小單位,位元組是比特的集合,也是一個單位;
記憶體給數據類型地址分配如下:
- char:占一個位元組分配一個地址;
- int: 占四個位元組分配四個地址;
- 還有long、float、double等類型,等著你來動手測試。
可以使用sizeof進行驗證:
#include<stdio.h>
int main () {
printf("sizeof(char)=%u\n",sizeof(char));
printf("sizeof(int)=%u\n",sizeof(int));
return 0;
}
結果如下:
第二、理解指針
不要把指針想得太複雜,指針的實質就是記憶體“地址”,可以說指針就是地址,其實指針就是保存地址的變數。
拿普通變數跟指針變數做比較:
char a; // 定義一個變數a,用於保存char類型的數據;
char *b; // 定義一個指針變數b,用於保存一個記憶體地址,這個記憶體地址上的數據必須是char類型的。
舉個例子,給指針變數進行賦值:
#include<stdio.h>
int main () {
char a = 5; // char 類型占一個位元組;
char *b = &a; // “&”是取變數的地址,取出a在記憶體中的地址;
// 賦值給b指針,此時b變數存儲的就是a地址。
printf("我是a變數的值:%d\n",*b); // *b表示輸出b裡面存儲的地址上的數據;
// 證明b上存儲的是a的地址;
printf("我是a的地址:%p\n",&a);
printf("我是b變數的值:%p\n",b);
return 0;
}
輸出結果為:
我是a變數的值:5
我是a的地址:000000000062FE17
我是b變數的值:000000000062FE17
通過畫圖來理解:
通過指針間接性修改變數的值
char a = 5;
char *b = &a;
printf("初始值:a=%d,*b=%d\n",a,*b);
*b = 12; // 其實操作的就是變數a本身的值;
printf("修改後:a=%d,*b=%d\n",a,*b);
------------------------------------------
輸出結果為:
初始值:a=5,*b=5
修改後:a=12,*b=12
指針類型的概念
我們知道char類型的數據只占一個位元組,有很多類型是需要多個位元組來存儲的,像int類型的數據就需要四個位元組來存儲(根據平臺不同,長度也有可能不一致)。
對於int類型的指針從當前位元組(地址)開始共四個位元組(地址)都是屬於該變數的值, 而對於char類型則只表示當前位元組(地址)。代碼如下:
int a = 259;
int * p1 = &a;
char * p2 = (char *)&a; // 這裡需要強制轉換一下類型
printf("*p1=%d,*p2=%d\n",*p1,*p2);
-----------------------
輸出:*p1=259,*p2=3
通過畫圖來便於理解:
通過上文我們已經對int類型指針有所瞭解了,*p1的輸出是在我們預算範圍之內的,但是為什麼*p2輸出的值是3呢?
重點,敲黑板!!!
因為電腦是使用二進位來表示數字的,上面(259)十進位轉換二進位是 [100000011],由於一個int類型變數占用四個位元組,8位二進位為一個位元組,補齊高位的0後,則 [00000000 00000000 00000001 00000011],每8位二進位(一個位元組)換算為十進位,則 [0 0 1 3]。
此時你應該差不多明白*p2為什麼輸出的值為3了吧,但是記憶體地址中有個概念叫"大小端模式",就會有兩種不同的排序:[0 0 1 3] or [3 1 0 0]。
由於電腦讀取*p2的地址是0x00,所以直接輸出這個地址上的數據,你也可以試著改一下,把259換成258/257等,看看是否正如所說。
驗證它們存儲地址,代碼如下:
int a = 259;
int * p1 = &a;
char * p2 = (char *)&a;
printf("*p1=%d,*p2=%d\n",*p1,*p2);
printf("&a=0x%p\n",&a);
printf("p1=0x%p\n",p1);
printf("p2=0x%p\n",p2);
輸出結果正如我們預想的:
當你看到這裡的時候,你只是剛剛認識指針而已,以上是我們俗稱的一級指針,一級指針是比較簡單的,還有二級指針和多級指針,更繞、更難理解,接下來介紹一下二級指針。
在講二級指針前,我們是否有疑問:什麼是一級指針?什麼是二級指針呢?兩者有什麼區別呢?
- 一級指針存儲變數的地址,通過這個地址"直接獲取"變數的數據。
- 二級指針存儲一級指針的地址,二級指針通過一級指針"間接獲取"獲取變數的數據。
- 多級指針以此類推,個人理解,講的不對歡迎指正。
再堅持一下,精彩在"下麵"!!![/滑稽]
二級指針
“指針的指針”也就是我們俗稱的二級指針。
什麼是“指針的指針”,例如下麵代碼:
char a = 5;
char * p1 = &a;
char ** p2= &p1;
printf("*p=%d,**p2=%d\n",*p1,**p2); // 輸出:*p1=5,**p2=5
通過畫圖來理解:
多級指針也就是指針的指針的指針.....,以此類推即可。
第三、指針運算問題
指針運算是根據指針的類型不同而進行運算的,因類型的不同,在加1/減1操作時,記憶體分配的空間也不同。
又拿int類型和char類型來作比較,代碼如下:
char類型+1:從輸出結果可以看出地址是遞增1的,正符合char類型占一個位元組的說法。
char c = 'h';
char *a = &c;
for (int i=0;i<3;i++){
printf("a+1=0x%p\n",a + i);
}
--------------------------------
輸出結果:
a+1=0x000000000062FE0F
a+1=0x000000000062FE10
a+1=0x000000000062FE11
int類型+1:輸出的地址之間相差為4,正是int類型占據空間。
int c = 259;
int *a = &c;
for (int i=0;i<3;i++){
printf("a+1=0x%p\n",a + i);
}
--------------------------------
輸出結果:
a+1=0x000000000062FE0C
a+1=0x000000000062FE10
a+1=0x000000000062FE14
char類型和int類型分別+1在記憶體中地址分配,如圖:
指針就介紹到這裡,這隻是指針的基礎,還有數組指針、指針數組、null指針、void指針等等知識,還需要學習,後續繼續更新。
以上有不恰當或者講得不對的地方,希望各位留言指正,謝謝!
站在巨人的肩膀上!