上學的時候學習C語言,最煩的就是里面指針,可是指針也恰恰是C語言的靈魂。
最近在重溫數據結構的內容,因為大多數據結構的教材都是用C語言描述的,而數據結構中也大量的用到了指針的內容,所以我就在這篇筆記中記錄一下我這周復習C語言的心得。
先看看百科上對指針的描述。
在計算機科學中,指針(Pointer)是編程語言中的一個對象,利用地址,它的值直接指向(points to)存在計算機存儲器中另一個地方的值。由於通過地址能找到所需的變量單元,可以說,地址指向該變量單元。因此,將地址形象化的稱為“指針”。意思是通過它能找到以它為地址的內存單元。
作個比喻,假設將計算機存儲器當成一本書,一張內容記錄了某個頁碼加上行號的便利貼,可以被當成是一個指向特定頁面的指針;根據便利粘貼面的頁碼與行號,翻到那個頁面,把那個頁面的那一行文字讀出來,這就是指針的作用。
下面將通過一些代碼說明指針在C語言中的表現形式。
int main(){
int a ;
a = 10;
int *p;
p = &a;
}
如果用圖片描述這段代碼,就是下面這個樣子。
怎么來理解呢?首先這段代碼里通過int a
和 int *p
定義了兩個變量:分別是p和a,p變量與a變量的定義方式有一些不同,a變量就是C語言中一個很普通的int型變量,通過a=10
將10這個整型賦值給了a。
而p變量的定義前面有一個 * ,這個 * 表明了p變量是一個指針變量,指針變量里面只能存放地址,這個地址是內存中的某個位置,在上面的代碼中我們在p變量里面存放的是0x2C406B24這個地址,這個地址里面存放的值必須是int值,在我們這里,p變量里面存放的地址是a變量的地址,a變量在定義時就是一個int,所以是符合要求的。
這樣一來,我們就說p變量指向了a變量,p = &a
這句代碼完成了p指向a的這個操作。這里沒有寫p = a
,那是因為p變量需要的是一個地址,而不是a變量里面存放的值,所以&這個操作符就是取地址的意思,通過&a取到a變量在內存中的地址,將地址賦值給p指針變量,就使p指向了a。
既然p是一個指針變量,那它就可以賦值給另外一個指針變量,如下:
int main(){
int a ;
a = 10;
int *p;
p = &a;
int *q;
q = p;
}
新定義了一個指針變量q,將p變量里的值0x2C406B24這個地址,賦值給q,這樣q變量與p變量里都保存了同樣的地址,就是說他們都指向同一個值。
int main(){
int a ;
a = 10;
int *p;
p = &a;
int *q;
q = p;
*p = 5; //
*q = 0; //
}
介紹完指針,那這個東西有什么作用呢?如果要修改a變量里面的值,可以執行a=5
,這是沒介紹指針之前的做法。學習完指針后,通過指針也能達到修改a變量里的值的目的*p=5
。
這是很有用的,舉個例子,我們來看下面這段代碼。
void swap(int a,int b);
int main(){
int a = 5;
int b = 6;
swap(a, b);
printf("a=%d b=%d",a,b);
return 0;
}
void swap(int a, int b){
int temp;
temp = a;
a = b;
b = temp;
}
上面這段代碼,這段代碼能夠達到交換a和b的值的目的嗎?答案是不能,因為C語言在調用函數時,永遠只能時傳值給函數。
在C語言中每個函數都有自己的變量空間,函數的參數也位於這個獨立的空間中,與其它函數沒有關系,上面的代碼中有兩個函數,一個是main函數,另一個是swap函數,所以這兩個函數里的a,b變量是不同空間中的變量,他們之間毫無關系可言。
所以對swap函數中的a,b做交換,完全不能改變main函數中a,b變量的值。
函數在每次運行的時候,會產生一個獨立的變量空間,在這個空間中的變量,是函數這次調用時所獨有的,稱為本地變量。
定義在函數內部的變量就是本地變量,參數也是本地變量。
變量有 生存期 和 作用域 這兩個屬性。
- 生存期:什么時候變量開始出現,到這個變量消亡
- 作用域:在代碼的什么范圍內可以訪問這個變量(這個變量可以起作用)
對於本地變量而言,生存期與作用域都是在本地變量所在的大括號(塊)內。
既然都說到本地變量了,那就總結一下本地變量的一些規則。
本地變量的規則:
- 本地變量定義在塊內
- 本地變量只存在於運行塊內語句的期間
- 在塊外面定義的變量,塊里仍然有效
- 本地變量不會默認初始化
- 參數這樣的本地變量在進入函數時就被初始化了
- 列表內容
又說回上面的交換函數,那么怎樣才能達到交換main函數里a,b兩個變量值的目的呢?
void swap(int a,int b);
int main(){
int a = 5;
int b = 6;
swap(&a, &b);
printf("a=%d b=%d",a,b);
return 0;
}
void swap(int *pa, int *pb){
int temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}
利用上面所學習的指針,改寫成上面這樣,才能達到交換的目的。
始終記住:C語言在調用函數時,永遠只能時傳值給函數。在上面的代碼中,自然也是傳值進的swap函數,只不過在使用指針時,這個值指的是地址,地址當然也是一個值。
將main函數中a和b變量的地址傳給了swap函數中的指針變量pa和pb,在swap函數中通過 *pa 和 *pb 操作到了main函數中的a和b,從而達到了交換的目的。
這只是指針的作用之一,用好了指針,才能體現出C語言更多強大的地方。
數組
這是一篇總結指針的文章,那么我為什么要提到數組呢?
因為數組和指針又太多的相似之處。看下面一段代碼。
int test(int a[], int numOfa){
int i;
for(i = 0; i<numOfa; i++){
printf("%d",a[i])
}
}
其實數組中的[ ],與 * ,. ,& 等運算符一樣,也是一種運算符。
上面test函數里的a數組變量,本身就是一個地址,所以我們在調用這個函數的時候,需要寫成這樣test( &a, i)
,需要傳入一個地址,這個地址所在的變量保存的就是一個數組。
例如:
int a[10]; int *p = a;
// 不需要用&取地址,數組變量本身就表達地址a == &a[0]
但是數組的單元表示的是變量,需要用&對變量取地址。數組變量第一個單元的地址就是數組變量的地址。p[0] == a[0]
[ ] 運算符可以對數組做,也可以對指針做*a = 9
將數組的第一個單元賦值為9,說明 * 運算符既可以對數組做,也可以對指針做
最后在總結一下const關鍵字在指針中的規則:
- const int *p = &i :const在前,表示不能通過指針去修改變量
- int * const p = &i :const在后,表示p這個指針變量不能再保存別的地址值
上面就是我在復習C語言指針時的一些總結,今后有需要添加的也會陸續補充。