----------------------------
--1-- 為什麼要使用指針1.1 指針的基本概念1.2 使用指針的好處1.3 變量的存儲方式--2-- 指針變量2.1 指針變量2.2 定義一個指針變量2.3 指針變量的初始化方法2.4 使用*獲取指針對應存儲區域的內容--3-- 存儲細節3.1 變量及指針變量--4-- 常見的應用場景4.1 指針常見的應用場景--5-- 多級指針介紹5.1 二級指針5.2 多級指針介紹----------------------------
【寫在開頭:】
『生活中的指針:
沒錯,就是一個門牌號。
指針是C語言中最重要的內容之一。
為什麼要使用指針?
如果你想要查找一篇資料,給你一本厚重的百科全書,而你只需要其中第3001頁的內容。
那麼,3001頁,就是指針。如果沒有這個指針...好吧,我去旁邊哭會兒...
內存單元的編號也叫做地址,根據內存單元的編號或地址就就可以找到所需的內存單元。
所以通常也把這個地址稱為指針
1)為函數提供修改調用變量的靈活手段
2)讓函數有多個返回值
3)可以改善程序的效率
在數據傳遞時,如果數據塊較大(比如數據緩沖區或比較大的結構),這時就可以使用指針傳遞地址而不是實際數據,既提高傳輸速度,又節省了大量的內存
4)為動態數據結構(如二叉數,鏈表等)提供支持
變量存取的方式有兩種:直接存取和間接存取
1)直接存取:變量的賦值和取值
2)間接存取:通過指針(地址)間接操作完成
指針最大的作用之一就是通過地址來操作(存取)變量、數組等
在C語言中,允許用一個變量來存放指針,這個變量稱為指針變量。
指針變量存放的是一個地址,是一個變量
指針變量的定義包括三個內容:
1)指針類型說明,即定義變量為一個指針變量
2)指針變量名
3)變量值(地址)
一般形式:
類型說明符 *變量名 = 地址;
int *p; //定義了一個指針變量,變量名是p int表示p指向的是一個int類型的變量空間 char *p1; //變量名是p1 char表示p1指向的是一個char類型的變量空間
其中
類型說明符表示本指針變量所指向的變量的數據類型
*表示這是一個指針變量
變量名即為定義的指針變量名
注意:
1)在定義指針時,*表示定義的變量是一個指針變量(如果沒有*就和普通變量沒有區別了)
2)指針變量能用來存放數組或字符之類?-->不能(存儲的是地址)
3)一個類型的指針只能指向同類型的變量,不能指向其他類型的變量
4)指針變量,歸根結底還是一個變量,也有全局和局部之分
指針變量應該用地址初始化
兩種初始化的方式:
定義的同時初始化、先定義後初始化
1)定義的同時初始化
//完全初始化 int a = 3; int b = 4; int *P = &a; //用a的地址初始化p這個指針變量(也稱為p指向了a)) int *p1 = &a, *p2 = &a; //p1,p2都指向了a
2)先定義後初始化
//部分初始化 int *p3 = &b, *p4; //定義了兩個指針變量 p3和p4 p4未初始化 p4 = &b;
指針變量同普通變量一樣,使用之前不僅要定義說明,而且必須賦予其具體的值。
未經賦值的指針變量不能使用,否則將造成系統混亂,甚至死機(野指針)。指針變量的賦值只能賦予地址,不能賦予任何其他的數據,否則將引起錯誤。
在C語言中,變量的地址是由編譯系統分配的,對用戶完全透明,用戶事先並不知道變量的具體地址(但可以知道區域位置)。
兩個相關的運算符:
&:取地址運算符;
*:指針運算符(或稱“間接訪問”運算符);
C語言提供了地址運算符"&"來表示變量的地址,一般形式為:&變量名;
獲取指針變量指向的存儲空間的內容:*指針變量名
*運算符和&運算符恰好相反。&運算符接收一個數據,然後告訴你這個數據保存在哪裡;*運算符接收一個地址,然後告訴你這個地址中保存的是什麼數據。
因為指針有時也叫引用,所以*運算符也可以描述成對指針進行解引用。
int a = 3; int b = 4; int *p1 = &a; int *p2 = &b; //*指針變量 作用:獲取指針變量指向的內存空間的內容(值)再賦值給value int value = *p1; //3 *p1 = 100; //改變空間的值 value = *p2; //4 printf("*p1 = %d\n", *p1); //100 printf("value = %d\n",value ); //4
思考
定義如下指針
float *pointer_3; char *pointer_4; int a,b; int *pointer_1 = &a, *pointer_2;
判斷下面的操作是否合法
*pointer_1 = &a; pointer_3 = 2000; pointer_3 = &a; pointer_1 = &a; pointer_2 = pointer_4;
分析:
*pointer_1 = &a; //錯; *pointer_1是一個解引用,是一個值,而非地址 pointer_3 = 2000; //錯;pointer_3指針並未初始化,且需賦值的是一個地址 pointer_3 = &a; //錯; 應該賦值一個float類型的空間地址給pointer_3 pointer_1 = &a; //合法 pointer_2 = pointer_4; //錯; 類型不同
每當聲明一個變量,計算機都會在存儲器中某個地方為它創建空間。如果在函數(例如main()函數)中聲明變量,計算機會把它保存在一個叫棧(Stack)的存儲器區段中;如果你在函數以外的地方聲明變量,計算機則會把它保存在存儲器的全局量段(Globals)。
int y = 1; //位於全局量段 地址:1000 000 int main(int argc, const char * argv[]) { @autoreleasepool { int x = 4; //位於棧區 地址:4100 000 } return 0; }
如有如下變量
int i = 200;
int *p = &i;
此時p指向了變量i的首地址&i
這樣的一種場景很常見,參數在函數間進行傳遞時,要通過被調函數改變主調函數中實參變量的值
如:交換兩個變量的函數,如果不使用指針,則一般用的是臨時變量等方法
int main(int argc, const char * argv[]) { @autoreleasepool { int x = 3, y = 4, temp; //臨時變量交換兩個變量的值 temp = x; x = y; y = temp; printf("x = %d, y = %d\n", x, y); } return 0; }
可是上面這樣的寫法是不好的,需要將其封裝(原諒我這裡的說法不准確,“封裝”是面向對象語言的特點)成自定義方法才好。
那麼如果封裝(習慣這樣叫了...)成自定義方法之後,如果實參傳遞的是一個變量,那麼主調函數中變量的值不會改變,因為形參實際上是又開辟了一個新的內存空間,在自定義函數中並不能修改實參的值:
int main(int argc, const char * argv[]) { @autoreleasepool { void swap(int x, int y); //函數聲明 int x = 3, y = 4; swap(x, y); //變量作為實參並不能修改實參空間中的值 printf("x = %d, y = %d\n", x, y); //未改變 x = 3, y = 4 } return 0; } /** * 測試方法,交換變量的值 * * @param x 變量1 * @param y 變量2 */ void swap(int x, int y){ //臨時變量交換兩個變量的值 int temp = x; x = y; y = temp; }
所以要實現上面設定情景,就只能使用指針地址傳遞:
int main(int argc, const char * argv[]) { @autoreleasepool { void swap(int *x, int *y); //函數聲明 int x = 3, y = 4; swap(&x, &y); //將地址作為形參就可以交換此處x和y的值 printf("x = %d, y = %d\n", x, y); //x = 4, y = 3 } return 0; } /** * 交換兩個變量的值 * * @param x 地址1 * @param y 地址2 */ void swap(int *x, int *y){ //臨時變量交換兩個變量的值 int temp = *x; *x = *y; *y = temp; }
如果一個指針變量存放的又是另一個指針變量的地址,則稱這個指針變量為指向指針的指針變量,也稱為“二級指針”
通過指針訪問變量稱為間接訪問,由於指針變量直接指向變量,所以稱為“一級指針”。而如果通過指向指針的指針變量來訪問變量則構成了“二級指針”
int a = 5; int *p = &a; printf("%p\n",&a); //a的地址 printf("%p\n",p); //指針p中存儲的是a的地址 printf("-----\n"); //****二級指針 int **p1 = &p; //p1中存儲的是一個指針 printf("p = %p\n", &p); //指針p的地址 printf("p1 --> %p\n", p1); //p1中存儲的地址 printf("----------------------\n"); printf("*p = %d\n", *p); //指針p指向的地址中存儲的值 printf("*p1 = %p\n", *p1); //p1指向的地址中存儲的指針地址 printf("**p1 = %d\n", **p1); //p1中存儲的指針解引用
同理,定義的時候有多少個*,一般也就稱為多少級的指針
【寫在結尾:】
『每個人小的時候,都應該會有一個夢想,或者,就稱為夢吧』