您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“C++中指針的示例分析”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“C++中指針的示例分析”這篇文章吧。
要搞明白什么是指針,首先我們得先了解一個概念:地址。
什么是地址呢?
我們知道,計算機的內存是一塊連續的大塊存儲空間,為了提高 CPU 查找數據的效率,我們將這塊連續的存儲空間以字節為單位進行編號,每個字節都一一對應了一個編號,這個編號就叫做地址。
舉個形象的例子:
如果我們把一座宿舍樓看做是內存,那么宿舍樓里的房間我們就可以看成是內存里的每一個字節,那么每個房間的門牌號就可以看做是地址。
為什么要有地址?
通過上面的例子,我們很容易了解到地址存在的意義。試想,如果宿舍樓里的房間都沒有門牌號,我們就很難描述一指定房間的具體位置。CPU 對內存的處理也是如此,如果沒有地址,CPU 就沒有辦法確定指定內存的具體位置,從而正確且快速的訪問到該內存空間。
指針就是地址
指針就是地址
指針就是地址
重要的事情說三遍,我們必須明確的知道一件事,指針就是地址,可以理解為,指針是地址在C語言里的一種特殊叫法。
#include <stdio.h> int main() { int a = 10; int *p = &a; // p是不是指針? printf("%p\n", p); // %p 可以格式化輸出地址。 return 0; }
既然如此,上述代碼中 p 是不是指針呢?
我們說過指針就是地址,而 p 是不是地址?當然不是!地址是一個單獨的數據,就像 int a = 10; 我們能說 a 就是 10 嗎?當然不能,10是一個整形常量,而 a 是一個整形變量,我們只能說 a 里面存放的值是 10,而不能簡單的說 a 就是 10。
同理,上述代碼中的 p 是一個變量,里面存放的是 a 的地址,我們稱它為指針變量。所以嚴格意義上來說,p 不是指針,而是指針變量。就像 a 不是整數10,而是整形變量一樣。
那么什么是指針呢,我們把這段代碼運行起來:
沒錯,嚴格意義上來說,這段數字才真正的叫做指針 (地址值在計算機中一般以16進制顯示)。
通過上面的講解,相信大家可以理解,指針是地址,是一個常量,而指針變量是一個變量,里面存放的是指針,這兩者之間有著本質區別。
看到這,相信有些同學就有疑問了:明明上課的時候,或者某些書上都將指針變量統稱為指針,就像 上例中的 p 平時都叫做指針,為什么現在又說 p 不是指針呢?
請大家注意,我說 p 不是指針是在嚴格意義上來說。
在日常使用中,我們經常把指針和指針變量混為一談。這似乎成為了一種習慣,甚至我在下面的表述中都可能將指針變量表述為指針。
結論是:我們在日常使用中,可以模糊指針與指針變量的概念,把指針變量統稱為指針,但是,我們心里必須清楚的意識到,指針和指針變量的本質區別,以及你叫的這個東西到底是指針還是指針變量。
經過上面的講解,相信這個問題就非常簡單了。指針就是地址,那么為什么要有地址?究其原因就是,為了提高查找效率。
試想如果沒有地址的存在,我們寫下 int a = 10; 的時候,系統會自動在內存中開辟一4字節的空間存放10這個數值。而我們 CPU 在訪問的時候,由于沒有地址的存在只能在內存中一塊一塊的遍歷對比查找,而有了地址,內存就可以把地址值告訴 CPU,CPU 就可以根據地址值直接定位到該內存,大大提高了 CPU 訪問內存的效率與準確性。
指針變量的定義格式較簡單,只要在類型和變量名之間加上 * 操作符,就可以定義該類型的指針變量。
int *a = NULL; //定義整形指針變量 double *d = NULL; //定義雙精度浮點型指針變量 struct stdnt *ps = NULL; //定義結構體指針變量
注: NULL 是指針變量初始化的時候常用的宏定義,稱其為空指針,本質為 0 值。
如果定義時不初始化,如:
int *a;
我們知道,變量如果不初始化,其中的值就是隨機值,也就是此時的指針變量 a 里面存放的是隨機值,如果此時訪問 a 變量就會以這個隨機值作為地址訪問對應的內存,這種操作是非法的。這種不初始化的指針我們稱之為野指針。
所以,為了避免野指針的出現,我們在定義指針變量時盡量對其進行初始化。
我們使用指針就是使用地址,那么地址從何而來?我們可不可以這么寫代碼:
int *a = 0x11223344;
我們把 0x11223344 看做是地址值交給整形指針變量 a 來存儲。語法上沒有問題,但是這樣寫毫無意義!因為 0x11223344 這個地址對于我們來說是未知的,我們并沒有讀取和訪問的權限,這時候的 a 變量就相當于一個野指針。
事實上,我們的地址是由 & 取地址符取出來的。
#include <stdio.h> int main() { int a = 10; int *p = &a; return 0; }
這里我們定義了整形變量 a,取地址符取出了 a 的地址并把它賦給整形指針變量 p。
這里就有一個疑問,a 是一個整形變量,占4字節的空間,而通過上面的介紹,我們知道內存的地址編號是以字節為單位的,這就意味著變量 a 其實存在4個地址,那么 &a 究竟取出的是哪一個地址呢?
上結論:
在C語言中,對任意類型的變量或者數組取地址,永遠取得是數值上最小的那個地址。
我們畫一下上面代碼的內存分布圖:
假設從左向右地址依次升高,那么 p 里面存放的就是 a 變量4字節中地址最低的那個字節的地址。
我們上面說到,對變量取地址取得是地址最低的字節即首字節地址,那么我們如何通過這一個字節的地址訪問到原變量里的數據呢,這里就需要使用 * 操作符:
#include <stdio.h> int main() { int a = 10; int *p = &a; *p = 20; // 等價于 a = 20 printf("%d\n", *p); printf("%d\n", a); return 0; }
看上面的代碼,我們把 a 變量的地址賦值給指針變量 p,通過 *p 即可訪問到該變量的內容。
那么 * 操作符到底是如何使用的呢,下面給出結論:
對指針解引用,就是指針指向的目標。
就像上例中 *p 是什么?*p 就是 a,*p 就是 a,*p就是a,重要的事情說三遍。所以,如果我改變 *p 的值,完全等價于直接改變 a 變量的值。
上面的代碼運行結果:
那么,我們看下面這段代碼:
int main() { int a = 0x11223344; int *p = &a; printf("0x%x\n", *(char*)p); return 0; }
注意,我們說 *p 就是 a,但是,這里的 *p 被強制類型轉化為 char* 類型之后再進行的解引用操作,此時的 p 就是一個字符類型的指針,因為字符 char 類型在C中只占一個字節,對其進行解引用只能訪問一個字節的內容。而我們說過 p 中存放的是 a 變量的首字節的地址,即 44 的地址 (至于為什么,請讀者自己了解大小端的內容),解引用就只訪問到了 44:
下面定義一個結構體:
typedef struct Student { int age; char name[20]; char sex[2]; }STD;
C語言中任何數據類型都有其對于的指針類型,結構體也不例外,如下:
int main() { STD s = { 18, "張三", "男" }; STD *ps = &s; // 定義結構體指針 printf("%s\n%d\n%s\n", s.name, s.age, s.sex); }
我們定義一個結構體變量并初始化,通過 . 操作符可以很容易訪問到結構體的內容。那么我們如何通過結構體指針變量 ps 來訪問結構體的內容呢?
我們說過對指針解引用,就是指針指向的目標,那么請讀者思考,*ps 是什么呢?沒錯 *ps 就是 s變量,既然如此,我們就可以這么訪問:
printf("%s\n%d\n%s\n", (*ps).name, (*ps).age, (*ps).sex); //注:因 . 操作符優先級高于 * 操作符,故 (*ps) 必須加括號
C語言可能認為這樣寫太麻煩,于是對于結構體指針我們可以使用 -> 操作符直接訪問到結構體的內容:
printf("%s\n%d\n%s\n", ps->name, ps->age, ps->sex);
ps->name 寫法完全等價于 (*ps).name 這樣的寫法。
注:-> 操作符只在結構體指針變量訪問結構體成員時使用。
首先問大家一個問題,指針變量是不是變量?是變量。既然是變量,那么就有地址,我們依然可以把指針變量的地址存入一個新的變量,此變量我們可以稱為二級指針變量:
int a = 10; int *pa = &a; int **ppa = &pa; // 定義二級指針變量 *ppa; //等價于 pa **ppa; //等價于 *pa,即等價于 a
二級指針大家不要看的多么神秘,所謂二級指針其實也就是一個指針變量,只不過里面存放的是另一個指針變量的地址而已。
既然如此,二級指針變量是不是變量呢?它能不能取地址存入另外一個三級指針變量呢?那么,三級指針變量是不是變量呢?… 如果你愿意,可以一直這么套娃下去~
之所以把這塊單獨分出來講,是因為對指針變量進行一個好的命名規范,不僅有利于提高代碼的可讀性,更有助于我們理解一些復雜的數據結構的代碼。
指針的命名習慣上在原變量名前加字母 p。 如定義整形變量 a ,其指針變量就命名為 pa, 定義結構體變量 std 其對應指針變量命名為 pstd,如果對 pstd 再次取地址存入二級指針變量,那么該二級指針變量就應該命名為 ppstd。
都說指針是C語言的靈魂,那么指針的魅力究竟在何處?
我們看這段交換兩個數的代碼:
#include <stdio.h> void Swap(int a, int b) { int t = a; a = b; b = t; } int main() { int a = 10; int b = 20; Swap(a, b); printf("a = %d\n", a); printf("b = %d\n", b); return 0; }
我們應該知道,這段代碼是無法完成 a, b 的交換的,因為 Swap() 里的 a, b 只不過是 main() 函數里 a, b 的一份拷貝。即,Swap() 函數里 a, b 的改變是不會影響 main() 函數的。這種傳參的方式我們稱之為傳值。
可是我們這么寫:
#include <stdio.h> void Swap(int *pa, int *pb) { int t = *pa; *pa = *pb; *pb = t; } int main() { int a = 10; int b = 20; Swap(&a, &b); printf("a = %d\n", a); printf("b = %d\n", b); return 0; }
main() 傳參時傳 a, b 的地址,Swap() 函數使用指針進行接收,內部使用解引用方式進行交換,我們就可以完成真正的交換功能。這種傳參的方式我們稱之為傳址。
我在一篇文章上看到這樣一個說法,可謂是對指針理解到了本質:
傳值就是傳值。
傳址就是傳值本身。
值和值本身兩個概念希望大家能夠深刻理解。
如同我們 Swap() 函數的第一種寫法,main() 函數僅僅是將 a, b 的值傳遞給了 Swap() 函數進行處理,可無論 Swap() 函數對其做什么樣的處理,也依然無法改變原來 a, b 的值。
而 Swap() 的第二種寫法,main() 函數傳的是 a, b 的地址,相當于傳的是 a, b 本身,這樣 Swap() 在進行處理時便可直接改變原來的 a, b 的值。
以上是“C++中指針的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。