引言
初入c++,肯定會對指針這個概念非常熟悉。但是為什么c/c++要使用指針?
其實每一種編程語言都使用指針,指針並不只是C/C++的獨有特性。C++將指針暴露給了用戶(程序員),而Java和C#等語言則將指針隱藏起來了。不光如此,指針還有很多妙用,后面會着重展開詳解。
一,指針(*)的概念分析
指針是一個變量,其值為另一個變量的地址,即,內存位置的直接地址。就像其他變量或常量一樣,您必須在使用指針存儲其他變量地址之前,對其進行聲明。指針變量聲明的一般形式為:
指針的類型 *指針變量的名稱;
eg: int *p
要搞清一個指針需要搞清指針的四方面的內容:
- 指針的類型
- 指針所指向的類型
- 指針的值或者叫指針所指向的內存區
- 指針本身所占據的內區。
相關鏈接:一文讓你不再害怕指針之C指針詳解(經典,非常詳細) - 知乎 (zhihu.com)
指針的類型(即指針本身的類型)和指針所指向的類型是兩個概念。必須加以區分!!!
首先,先來看幾個復雜類型的指針聲明(有難入易更容易理解)
int p; //這是一個普通的整型變量 int *p; //首先從P 處開始,先與*結合,所以說明P 是一個指針,然后再與int 結合,說明指針所指向的內容的類型為int 型.所以P是一個返回整型數據的指針 int p[3]; //首先從P 處開始,先與[]結合,說明P 是一個數組,然后與int 結合,說明數組里的元素是整型的,所以P 是一個由整型數據組成的數組 int *p[3]; //首先從P 處開始,先與[]結合,因為其優先級比*高,所以P 是一個數組,然后再與*結合,說明數組里的元素是指針類型,然后再與int 結合,說明指針所指向的內容的類型是整型的,所以P 是一個由返回整型數據的指針所組成的數組 int (*p)[3]; //首先從P 處開始,先與*結合,說明P 是一個指針然后再與[]結合(與"()"這步可以忽略,只是為了改變優先級),說明指針所指向的內容是一個數組,然后再與int 結合,說明數組里的元素是整型的.所以P 是一個指向由整型數據組成的數組的指針 int **p; //首先從P 開始,先與*結合,說是P 是一個指針,然后再與*結合,說明指針所指向的元素是指針,然后再與int 結合,說明該指針所指向的元素是整型數據.由於二級指針以及更高級的指針極少用在復雜的類型中,所以后面更復雜的類型我們就不考慮多級指針了,最多只考慮一級指針. int p(int); //從P 處起,先與()結合,說明P 是一個函數,然后進入()里分析,說明該函數有一個整型變量的參數,然后再與外面的int 結合,說明函數的返回值是一個整型數據 Int (*p)(int); //從P 處開始,先與指針結合,說明P 是一個指針,然后與()結合,說明指針指向的是一個函數,然后再與()里的int 結合,說明函數有一個int 型的參數,再與最外層的int 結合,說明函數的返回類型是整型,所以P 是一個指向有一個整型參數且返回類型為整型的函數的指針
二,指針的算術運算
指針是一個用數值表示的地址。因此,您可以對指針執行算術運算。可以對指針進行四種算術運算:++、--、+、-。
假設 ptr 是一個指向地址 1000 的整型(int)指針,是一個 32 位的整數,讓我們對該指針執行下列的算術運算:
ptr++
在執行完上述的運算之后,ptr 將指向位置 1004。如果 ptr 指向一個地址為 1000 的字符(char),上面的運算會導致指針指向位置 1001,因為下一個字符位置是在 1001。
因為int類型在32位程序中占4個字節,字符char類型占1個字節
遞增一個指針
我們喜歡在程序中使用指針代替數組,因為變量指針可以遞增,而數組不能遞增,因為數組是一個常量指針。下面的程序:
例如,一個指向數組開頭的指針,可以通過使用指針的算術運算或數組索引來訪問數組。請看下面的程序:(遞增變量指針(ptr++),以便順序訪問數組中的每一個元素)
#include <iostream> using namespace std; const int MAX = 3; int main() { int var[MAX] = { 10, 100, 200 }; int* ptr; // 指針中的數組地址(指針指向數組開頭) ptr = var; for (int i = 0; i < MAX; i++) { cout << "Address of var[" << i << "] = "; cout << ptr << endl; cout << "Value of var[" << i << "] = "; cout << *ptr << endl; // 移動到下一個位置 ptr++; } return 0; }
三,運算符&和*
C++ 提供了兩種指針運算符
- 取地址運算符 & :& 是一元運算符,返回操作數的內存地址。例如,如果 var 是一個整型變量,則 &var 是它的地址。&var 的運算結果是一個指針
- 間接尋址運算符 * :間接尋址運算符 *,它是 & 運算符的補充。* 是一元運算符,返回操作數所指定地址的變量的值。
int main () { int var = 3000; int *ptr; int val;
// 獲取 var 的地址
ptr = &var; //輸出prt=0xbff64494(即3000的地址)
// 獲取 ptr 的值
val = *ptr; //輸出val=3000
}
四,數組和指針的關系
數組的數組名其實可以看作一個指針。看下例:
int array[10]={0,1,2,3,4,5,6,7,8,9},value; ... ... value=array[0];//也可寫成:value=*array; value=array[3];//也可寫成:value=*(array+3); value=array[4];//也可寫成:value=*(array+4);
上例中,一般而言數組名array代表數組本身,類型是int [10],但如果把array看做指針的話,它指向數組的第0個單元,類型是int *,所指向的類型是數組單元的類型即int。因此*array等於0就一點也不奇怪了。同理,array+3是一個指向數組第3個單元的指針,所以*(array+3)等於3。其它依此類推。
五,指針和結構類型的關系
可以聲明一個指向結構類型對象的指針。
struct MyStruct { int a; int b; int c; } MyStruct ss={20,30,40};//聲明了結構對象ss,並把ss的三個成員初始化為20,30和40。 MyStruct *ptr=&ss;//聲明了一個指向結構對象ss的指針。它的類型是MyStruct*,它指向的類型是MyStruct。
怎樣通過指針ptr來訪問ss的三個成員變量?
ptr->a; ptr->b; ptr->c;
六,傳遞指針給函數
C++ 允許傳遞指針給函數,只需要簡單地聲明函數參數為指針類型即可。
下面的實例中,我們傳遞一個無符號的 long 型指針給函數,並在函數內改變這個值:
#include <iostream> #include <ctime> using namespace std; // 在寫函數時應習慣性的先聲明函數,然后在定義函數 //定義一個函數(參數為指針) void getSeconds(unsigned long* par); int main() { unsigned long sec; getSeconds(&sec);//獲取sec的地址 // 輸出實際值 cout << "Number of seconds :" << sec << endl; return 0; } void getSeconds(unsigned long* par) { // 獲取當前的秒數 *par = time(NULL); return; }
七,從函數返回指針
C++中函數是不能直接返回一個數組的,但是數組其實就是指針,所以可以讓函數返回指針來實現:
另外,C++ 不支持在函數外返回局部變量的地址,除非定義局部變量為 static 變量。
要想返回一個數組,使用智能指針之類的東西才是正途。(后續總結智能指針。。。)