技術(shù)交流QQ群:1027579432何址,歡迎你的加入!
1.Cpp中的指針
- 每個(gè)變量都有一個(gè)內(nèi)存位置进胯,每一個(gè)內(nèi)存位置都定義了可使用&地址運(yùn)算符訪問(wèn)的地址用爪,它表示了變量在內(nèi)存中的一個(gè)地址。
int var1; int var2[10]; cout << "變量var1的地址:" << &var1 << endl; // 訪問(wèn)變量var1的在內(nèi)存中的位置胁镐,使用&地址運(yùn)算符偎血,獲得變量在內(nèi)存中的地址 cout << "變量var2的地址:" << &var2 << endl;
2.什么是指針?
-
指針也是一個(gè)變量希停,它存放的值是另一個(gè)變量的地址,即變量在內(nèi)存中的位置烁巫,即內(nèi)存中的直接地址。與其他變量或常量一樣宠能,在使用指針存儲(chǔ)其他變量地址之前,必須對(duì)指針進(jìn)行聲明磁餐,指針變量聲明的一般形式是:
數(shù)據(jù)類型 *指針變量名;
- 在上述指針的指針變量聲明過(guò)程中违崇,數(shù)據(jù)類型必須是一個(gè)有效的C++數(shù)據(jù)類型阿弃,用來(lái)聲明指針變量的星號(hào)*與乘法中使用的星號(hào)是相同的。但是羞延,在上面的聲明語(yǔ)句中渣淳,星號(hào)是用來(lái)指定一個(gè)變量是指針,有效的指針變量的聲明是:
int *p; float *p1; double *ptr; char *p2;
- 由于所有指針變量中存放的都是另一個(gè)變量在內(nèi)存中的地址伴箩,所以不管指針指向的是int,float,char還是其他數(shù)據(jù)類型入愧,指針變量的值實(shí)際數(shù)據(jù)類型都是一樣的!都是一個(gè)代表內(nèi)存地址的十六進(jìn)制數(shù)嗤谚。不同數(shù)據(jù)類型的指針變量之間唯一的區(qū)別是:指針?biāo)赶虻淖兞炕虺A康臄?shù)據(jù)類型不同棺蛛。32位機(jī)系統(tǒng)中,指針變量所占的字節(jié)數(shù)是sizeof(*p) = 4個(gè)字節(jié)巩步。
3.C++中使用指針
- 指針變量使用時(shí)旁赊,會(huì)頻繁使用以下幾個(gè)操作:定義一個(gè)指針變量,把另外一個(gè)變量的地址賦值個(gè)指針椅野,訪問(wèn)指針變量中可用地址的值终畅,這些操作都是通過(guò)*運(yùn)算符來(lái)返回位于操作數(shù)所指定地址的變量的值。
int var = 20; // 普通變量的初始化 int *ip; // 指針變量的聲明 ip = &var; // 用指針變量存儲(chǔ)普通變量var的地址 cout << "var的值是:" << var << endl; cout << "普通變量var在內(nèi)存中的地址: " << &var << endl; cout << "指針變量ip在內(nèi)存中的地址: " << &ip << endl; cout << "通過(guò)訪問(wèn)指針變量ip中存放的普通變量的地址來(lái)得到普通變量var的值: " << *ip << endl;
4.Cpp中指針詳解
-
C++中與指針有關(guān)的概念如下表:
4.1 C++中的NULL指針
-
在指針變量聲明時(shí)竟闪,如果沒(méi)有明確的地址可以賦值時(shí)离福,為指針變量賦值一個(gè)NULL值是一個(gè)很好的習(xí)慣。NULL指針是一個(gè)定義在標(biāo)準(zhǔn)庫(kù)中的值為0的常量炼蛤。如下面的程序:
int *ptr = NULL; cout << "指針變量ptr在內(nèi)存中的地址:: " << &ptr << endl; cout << "ptr的值是: " << ptr << endl;
- 在大多數(shù)的操作系統(tǒng)上妖爷,程序不允許訪問(wèn)地址為0的內(nèi)存,因?yàn)樵搩?nèi)存是操作系統(tǒng)保留的鲸湃。然而赠涮,內(nèi)存地址0有特別重要的意義,它表明該指針不指向一個(gè)可訪問(wèn)的內(nèi)存位置暗挑。但按照慣例笋除,如果指針包含空值(零值),則假定它不指向任何東西炸裆。如果檢查一個(gè)空指針垃它,需要使用if語(yǔ)句:
if(ptr) // 如果ptr非空,則完成 if(!ptr) // 如果ptr為空烹看,則完成
- 因此国拇,如果所有未使用的指針都被賦予空值,同時(shí)避免使用空指針惯殊,就可以防止誤用一個(gè)未初始化的指針酱吝。很多時(shí)候,未初始化的變量存有一些垃圾值土思,導(dǎo)致程序難以調(diào)試务热。
4.2 指針的算術(shù)運(yùn)算
- 指針變量中存放的是一個(gè)用數(shù)值表示另一個(gè)變量的地址忆嗜,可以對(duì)指針變量執(zhí)行算術(shù)運(yùn)算。對(duì)指針進(jìn)行四個(gè)算術(shù)運(yùn)算:++ -- - +崎岂,假設(shè)ptr是一個(gè)指向地址是1000的整型指針變量捆毫,是一個(gè)32位整數(shù),對(duì)該指針執(zhí)行算術(shù)運(yùn)算ptr++后冲甘,ptr的指向位置是1004绩卤。因?yàn)閜tr每增加一次,它都指向下一個(gè)整數(shù)位置江醇,即當(dāng)前位置往后移動(dòng)4個(gè)字節(jié)濒憋。這個(gè)運(yùn)算在不影響內(nèi)存位置中實(shí)際值的情況下,移動(dòng)指針到下一個(gè)內(nèi)存位置嫁审。如果ptr執(zhí)向的是一個(gè)地址是1000的char型字符跋炕,上面的運(yùn)算會(huì)導(dǎo)致指針指向位置1001,因?yàn)橄乱粋€(gè)字符位置是1001。
- 遞增一個(gè)指針:在程序中使用指針代替數(shù)組律适,因?yàn)橹羔樧兞靠梢赃f增辐烂,而數(shù)組不能遞增,因?yàn)閿?shù)組名是一個(gè)指針常量捂贿,如下面的例子:
int a[MAX] = {10, 100, 200}; int *pp; pp = a; for (int i = 0; i<MAX; i++){ cout << "變量a[" << i << "]的地址是: " << &a[i] << endl; cout << "變量a[" << i << "]的地址是: " << pp << endl; cout << "變量a[" << i << "]的值是: " << a[i] << endl; cout << "通過(guò)訪問(wèn)指針變量pp中存放的普通變量的地址來(lái)得到數(shù)組元素的值: " << *pp << endl; cout << "------------------------------------------------\n"; pp++; // 移動(dòng)到下一個(gè)位置 }
- 遞減一個(gè)指針:對(duì)指針進(jìn)行遞減運(yùn)算纠修,即把值減去其指向的數(shù)據(jù)類型的字節(jié)數(shù),如下所示:
pt = &a[MAX - 1]; for (int i = MAX - 1; i >= 0; i--){ cout << "變量a[" << i << "]的地址是: " << &a[i] << endl; cout << "變量a[" << i << "]的地址是: " << pt << endl; cout << "變量a[" << i << "]的值是: " << a[i] << endl; cout << "通過(guò)訪問(wèn)指針變量pt中存放的普通變量的地址來(lái)得到數(shù)組元素的值: " << *pt << endl; cout << "------------------------------------------------\n"; pt--; // 移動(dòng)到下一個(gè)位置 }
- 指針的比較:指針可以用關(guān)系運(yùn)算符進(jìn)行比較厂僧,如果p1和p2指向兩個(gè)相關(guān)的變量扣草,如同一個(gè)數(shù)組中的不同元素,則可以對(duì)p1和p2進(jìn)行大小比較颜屠。
tt = a; int j = 0; while (tt <= &a[MAX - 1]){ cout << "變量a[" << j << "]的地址是: " << tt << endl; cout << "變量a[" << j << "]的值是: " << a[j] << endl; tt++; j++; }
4.3 指針vs數(shù)組
- 指針與數(shù)組是密切相關(guān)的辰妙,在很多情況下兩個(gè)是可以互換的。例如甫窟,通過(guò)一個(gè)指向數(shù)組開(kāi)頭的指針密浑,可以通過(guò)使用指針的算術(shù)運(yùn)算或數(shù)組的索引來(lái)訪問(wèn)數(shù)組,如下面例子:
int a[MAX] = {10, 100, 200}; int *pp; pp = a; for (int i = 0; i<MAX; i++){ cout << "變量a[" << i << "]的地址是: " << &a[i] << endl; cout << "變量a[" << i << "]的地址是: " << pp << endl; cout << "變量a[" << i << "]的值是: " << a[i] << endl; cout << "通過(guò)訪問(wèn)指針變量pp中存放的普通變量的地址來(lái)得到數(shù)組元素的值: " << *pp << endl; cout << "------------------------------------------------\n"; pp++; // 移動(dòng)到下一個(gè)位置 }
- 然而粗井,指針與數(shù)組并不是完全可以互換的尔破,如下面的例子:
int b[MAX] = {1, 11, 111}; cout << "b = " << b << endl; cout << "*b = " << *b << endl; for (int i = 0; i < MAX; i++){ *b = i; // 改變的是數(shù)組第一個(gè)元素的值 // b++; 數(shù)組名是一個(gè)指針常量,不能進(jìn)行修改 *(b + 2) = 555; } // 不是說(shuō)數(shù)組內(nèi)的值不能修改浇衬,而是只要b的值(地址)不變懒构,那么相應(yīng)位置的數(shù)值改變了,也不會(huì)影響該數(shù)組的首地址耘擂。
4.4 指針數(shù)組
- 引入:一個(gè)由3個(gè)整數(shù)組成的數(shù)組
int c[MAX] = {6, 66, 666}; for (int i = 0; i < MAX; i++){ cout << "c[" << i << "]的值是: " << c[i] << endl; }
- 可能有一種情況是:希望數(shù)組存儲(chǔ)的是指向int/char或者其他數(shù)據(jù)類型的指針胆剧,下面是一個(gè)指向整數(shù)的指針數(shù)組的聲明:
int * ptr[3];
- 把ptr聲明為一個(gè)數(shù)組,由3個(gè)整數(shù)指針組成醉冤。因此赞赖,ptr中的每個(gè)元素滚朵,都是指向int類型的指針冤灾,見(jiàn)下面的例子:
int cc[MAX] = {6, 66, 666}; int *pc[MAX]; // 定義一個(gè)指針數(shù)組 for (int i = 0; i < MAX; i++){ pc[i] = &cc[i]; // 由于pc數(shù)組中的每個(gè)元素都是指向int類型的指針前域,所以這里賦值為cc[i]的每個(gè)元素的地址 } for (int i = 0; i < MAX; i++){ cout << "cc[" << i << "]的地址是: " << pc[i] << endl; cout << "cc[" << i << "] = " << *pc[i] << endl; }
- 也可以用一個(gè)指向字符的指針數(shù)組來(lái)存儲(chǔ)一個(gè)字符串列表,如下面例子:
const char *names[MAX] = { "Curry", "Harden", "Durant", }; // const char * names[MAX]是指針數(shù)組韵吨,它的本質(zhì)是存儲(chǔ)指針的數(shù)組匿垄,存儲(chǔ)的元素是char類型的指針 for (int i = 0; i < MAX; i++){ cout << "names[" << i << "] = "<< names[i] << endl; // 輸出字符串的值 cout << "*names[" << i << "]的值是: " << *names[i] << endl; // 輸出指針?biāo)赶虻淖址氖椎刂返闹? cout << "names[" << i << "]的地址是: " << &names[i] << endl; // 輸出指針?biāo)赶虻淖址氖椎刂? }
4.5 指向指針的指針(多級(jí)間接尋址)
-
指向指針的指針是一種多級(jí)間接尋址的形式,或者說(shuō)是一個(gè)指針鏈归粉。通常椿疗,一個(gè)指針中存儲(chǔ)的是另一個(gè)變量的地址,當(dāng)再定義一個(gè)指向指針的指針時(shí)糠悼,第一個(gè)指針變量就包含了第二個(gè)指針變量的地址届榄,第二個(gè)指針變量存儲(chǔ)的是另一個(gè)變量的地址。
- 一個(gè)指向指針的指針變量必須進(jìn)行聲明倔喂,即在變量名前加上兩個(gè)**,下面是一個(gè)指向int型的指針的指針:
int val = 3000; int *p1 = &val; int **p2 = &p1; // 指向int類型的指針的指針 int ***p3 = &p2; cout << "val的地址是: " << &val << endl; cout << "指針p1的地址是: " << &p1 << endl; cout << "指向指針p1的指針p2的地址是: " << &p2 << endl; cout << "指針指針p2的指針p3的地址是: " << &p3 << endl; cout << "--------------------------------------------------------\n"; cout << "指針p3中存放的是指針p2的地址: " << p3 << endl; cout << "指針p2中存放的是指針p1的地址: " << p2 << endl; cout << "指針p1中存放的是變量val的地址: " << p1 << endl; cout << "*p3得到的是p2的值即p1的地址: " << *p3 << endl; cout << "*(*p3)得到的是p1的值即a的地址: " << *(*p3) << endl; cout << "*(*(*p3))得到的是a的值: " << *(*(*p3)) << endl;
4.6 傳遞指針給函數(shù)
- C++中允許傳遞指針給函數(shù)铝条,只需要簡(jiǎn)單地聲明函數(shù)參數(shù)為指針類即可,下面的實(shí)例是傳遞一個(gè)long型指針給函數(shù),并在函數(shù)內(nèi)改變這個(gè)值:
void getSeconds(unsigned long *par){ *par = time(NULL); // 獲取當(dāng)前秒數(shù) } // 傳遞指針給函數(shù) unsigned long sec; getSeconds(&sec); // 輸出實(shí)際值 cout << "number of seconds: " << sec << endl;
- 能接收指針作為參數(shù)的函數(shù)席噩,也可以接收數(shù)組作為參數(shù)班缰,如下所示:
double getAverage(int *arr, int size){ int i, sum = 0; double avg; for (i = 0; i < size; i++) sum += arr[i]; avg = double(sum) / size; return avg; } int balance[5] = {1, 2, 3, 4, 5}; double avg; // 傳遞一個(gè)指向數(shù)組的指針作為參數(shù) avg = getAverage(balance, 5); cout << "平均值: " << avg << endl;
4.7 從函數(shù)返回指針
- 從函數(shù)返回指針,需要聲明一個(gè)返回指針的函數(shù),如下所示:
int * myFunction(){ 語(yǔ)句; }
- 此外悼枢,C++不支持在函數(shù)外部返回局部變量的地址埠忘,除非定義的是局部變量static變量。
- 返回從函數(shù)返回指針的實(shí)例如下:
// 要生成和返回隨機(jī)數(shù)的函數(shù) int * getRandom(){ static int r[10]; // 設(shè)置種子 srand((unsigned)time(NULL)); for (int i=0; i<10;i++){ r[i] = rand(); cout << "r[" << i << "]=" << r[i] << endl; } return r; } // 從函數(shù)返回一個(gè)指針 int *ppg; ppg = getRandom(); // 調(diào)用子函數(shù)馒索,從子函數(shù)中返回一個(gè)指針 for (int i = 0; i < 10; i++) cout << "*(ppg + " << i << ") = " << *(ppg + i) << endl;
5.指針小結(jié)
- 指針的本質(zhì)是變量莹妒,可以是各種數(shù)據(jù)類型,定義一個(gè)指針"*ip"绰上,其中"ip"需要賦于一個(gè)地址(可以用&符號(hào)獲取其他變量的地址再賦值給 ip)旨怠,而"*ip"是一個(gè)具體的值,即讀取地址后獲得的值渔期;&符號(hào)的意思是取地址运吓,也就是返回一個(gè)對(duì)象在內(nèi)存中的地址, *符號(hào)的意思是取得一個(gè)指針?biāo)赶虻膶?duì)象,也就是如果一個(gè)指針保存著一個(gè)內(nèi)存地址,那么它就返回在那個(gè)地址的對(duì)象疯趟。
#include <iostream> using namespace std; int main() { int var = 20; int *ip; ip = &var; cout << "var的值:"; cout << var << endl; cout << "變量ip的儲(chǔ)存地址:"; cout << ip << endl; cout << "指針*ip的值:"; cout << *ip << endl; return 0; }
-
C++中允許聲明指向函數(shù)的指針拘哨,被稱為函數(shù)指針。函數(shù)指針的聲明類似于函數(shù)的聲明信峻,只不過(guò)將函數(shù)名變成了(*指針名)倦青,定義方式: int (*fp)(int a); // 定義了一個(gè)指向函數(shù)(這個(gè)函數(shù)參數(shù)僅僅為一個(gè)int類型,函數(shù)返回值是int類型)的指針fp
int func(int b); { cout << b; return ++b; } // 定義一個(gè)函數(shù)指針 int(*p)(int); p = func; // 通過(guò)函數(shù)指針來(lái)調(diào)用函數(shù) (*p)(5);