一.指針
** 1.指針概念**:指針就是用來(lái)保存內(nèi)存地址的變量。
2.聲明指針的方式:int *p;與運(yùn)算符*結(jié)合,p就表示一個(gè)指針
為什么使用指針(指針的三大作用):由于指針可以通過(guò)內(nèi)存地址直接訪(fǎng)問(wèn)數(shù)據(jù),可避免在程序中復(fù)制大量的代碼,因此指針的效率最高,其三大作用如下
2.1 處理堆中存放的大型數(shù)據(jù)
2.2 快速訪(fǎng)問(wèn)類(lèi)的成員數(shù)據(jù)和函數(shù)
2.3 以別名的方式向函數(shù)傳遞參數(shù)
3.運(yùn)算符和&,以及運(yùn)算符->*
&是取地址運(yùn)算符(后面再講引用的時(shí)候這個(gè)就變成引用運(yùn)算符了)。 如
int a=5;
cout<<&a<<endl;//使用&獲取變量a的內(nèi)存地址
*是指針運(yùn)算符或間接引用運(yùn)算符(注意:如果*用于聲明指針,那么它就是指針說(shuō)明符,如下)
int a=1;
int *p=&a;//這里的*表明是指針說(shuō)明符
cout<<*p<<endl;//這里的*表示指針運(yùn)算符,使用*獲取指針變量p中保存的地址處的值,也就是1
->是成員指針運(yùn)算符或指向成員運(yùn)算符
4.復(fù)雜變量的解釋(要判斷是一個(gè)變量的類(lèi)型就看與他最先結(jié)合的運(yùn)算符是什么),舉例如下:
int p 變量p是一個(gè)普通的整形變量
int *p 由于變量p先與運(yùn)算符*結(jié)合,所以p本質(zhì)是一個(gè)指針,再與int結(jié)合,所以p就是一個(gè)指向整形數(shù)據(jù)的指針
int p[3] 由于變量p只與運(yùn)算符[]結(jié)合,所以p本質(zhì)是一個(gè)數(shù)組,再與int結(jié)合,所以p就是一個(gè)由整形數(shù)據(jù)組成的數(shù)組
int *p[3] 變量p先與[]結(jié)合,因?yàn)閇]的優(yōu)先級(jí)高于,所以p本質(zhì)上是一個(gè)數(shù)組,剩下的就是要知道p是一個(gè)什么數(shù)組,然后p再與結(jié)合,說(shuō)明是p是一個(gè)指針數(shù)組,然后再與int結(jié)合,說(shuō)明p是一個(gè)指向整形數(shù)據(jù)的指針?biāo)M成的數(shù)組
int (*p)[3] 由于()改變了優(yōu)先級(jí),所以變量p先與*結(jié)合,所以p本質(zhì)上是一個(gè)指針,那它是什么類(lèi)型的指針呢,然后p再與[]結(jié)合,說(shuō)明p是一個(gè)指向數(shù)組的指針,然后再與int結(jié)合,說(shuō)明p是一個(gè)指向整形數(shù)據(jù)組成的數(shù)組的指針
int **p 變量p先與結(jié)合,說(shuō)明p本質(zhì)是一個(gè)指針,那它是什么指針呢,然后再與結(jié)合,說(shuō)明p是一個(gè)指向指針的指針,然后再與int結(jié)合,說(shuō)明p是一個(gè)指向整形數(shù)據(jù)的指針的指針
int p(int a)p首先與()結(jié)合,說(shuō)明p是一個(gè)函數(shù),然后進(jìn)入()分析,發(fā)現(xiàn)函數(shù)有一個(gè)int型的參數(shù)a,然后再與int結(jié)合,說(shuō)明p是具有整型參數(shù)且返回類(lèi)型為整型的函數(shù)。
int (*p)(int a) 由于(p)改變了優(yōu)先級(jí),所以p先與結(jié)合,說(shuō)明p本質(zhì)是一個(gè)指針,然后再與(int a)結(jié)合,說(shuō)明p是一個(gè)函數(shù)指針,然后再看(int a)發(fā)現(xiàn)函數(shù)具有一個(gè)int型的參數(shù)a,然后再與int結(jié)合,說(shuō)明p是一個(gè)指向具有整型參數(shù)且返回類(lèi)型為整型的函數(shù)的指針
5.指針的四方面重要內(nèi)容
指針的類(lèi)型,指針?biāo)赶虻念?lèi)型,指針的值(指針?biāo)赶虻膬?nèi)存區(qū)),指針本身所占據(jù)的內(nèi)存區(qū)
5.1 判斷這四個(gè)方面的規(guī)則
指針的類(lèi)型:把指針聲明語(yǔ)句的指針名字去掉,剩下的部分就是指針的類(lèi)型
指針?biāo)赶虻念?lèi)型:把指針聲明語(yǔ)句里的指針名字和名字左邊的指針聲明符*去掉,就是指針?biāo)赶虻念?lèi)型
指針的值:在32位程序里,所有類(lèi)型的指針的值都是一個(gè)32位整數(shù),指針的值是指向的內(nèi)存區(qū)域的首地址
指針本身所占據(jù)的內(nèi)存區(qū):指針本身占據(jù)的內(nèi)存長(zhǎng)度可以使用sizeof(指針的類(lèi)型)測(cè)一下就知道,在32位平臺(tái)里,指針本身占據(jù)了4個(gè)字節(jié)的長(zhǎng)度
舉例說(shuō)明指針的類(lèi)型和指針?biāo)赶虻念?lèi)型:
| 舉例 | 指針(ptr)(本身)的類(lèi)型 | 指針?biāo)赶虻念?lèi)型 |
|---|---|---|
| int *ptr | int * | int |
| char *ptr | char * | char |
| int **ptr | int ** | int * |
| int (*ptr)[3] | int (*)[3] | int ()[3] |
| int (ptr)[4] | int ()[4] | int *()[4] |
6.指針與常量
| 聲明定義式 | 注釋 | |
|---|---|---|
| 常量指針 | int *const p; | 指針本身不可改變,指向的變量可變 |
| 指向常量的指針 | const int *p; | 指針本身可變,其指向的變量不可變 |
| 指向常量的常指針 | const int *const p; | 指針本身不可變,其指向的變量也不可變 |
** 7.指針的注意事項(xiàng):迷途指針**
定義一個(gè)指針之后,如果沒(méi)有給他賦初值,那么該指針就是一個(gè)迷途指針,它可以指向任何地址,并且如果對(duì)該指針進(jìn)行操作就會(huì)對(duì)位置區(qū)域的數(shù)據(jù)進(jìn)行修改或刪除,照成意想不到的后果,所以解決辦法是將定義的指針進(jìn)行初始化,如下
[cpp]
int *p=0;
這樣,這個(gè)指針就稱(chēng)為空指針。
不僅在初始化的時(shí)候,還有一種情況迷途指針也會(huì)造成危害,就是刪除指針delete p;之后,雖然指向的內(nèi)存空間釋放了,但是指針本身還存在,如果再次使用該指針也會(huì)造成很?chē)?yán)重的后果,所以再刪除一個(gè)指針之后,將該指針賦值為空。雖然空指針是非法的,容易是程序奔潰,但是我們寧愿程序崩潰,也不愿意調(diào)試起來(lái)很困難。如下:
int *p=new int;
delete p;
p=0;
[cpp] view plain copy print?
*p=23;
刪除指針p后,賦值為0,然后在使用該空指針,程序運(yùn)行的時(shí)候,運(yùn)行到*p=23就會(huì)報(bào)錯(cuò),這樣我們就知道我們使用了一個(gè)迷途指針,從而及時(shí)修改程序。要是我們將p=0這句話(huà)去掉,那么程序就不會(huì)報(bào)錯(cuò),但是何時(shí)崩潰就不知道了,這樣加重了我們查問(wèn)題的難度。
二.引用
1.引用的概念:引用就是別名,如
[cpp]
int &rnum=num;//這里的&是引用運(yùn)算符
rnum是整形變量num的別名,這樣,對(duì)rnum的操作實(shí)際就是對(duì)num的操作。
這里要注意,別名rnum前面的符號(hào)&不是取地址運(yùn)算符,而是引用運(yùn)算符。
2.引用的作用(為什么要用“引用”)
其實(shí)引用只是為變量另外起了一個(gè)名字,就像#define num rnum==(int &runm=num),將num定義成rnum,兩者在內(nèi)存中是同一個(gè)空間。引用它不像int rnum=num;rnum其實(shí)是在內(nèi)存中新分配了一個(gè)空間,所以rnum和num占據(jù)的是兩個(gè)不同的內(nèi)存空間。那么引用在程序中到底有什么用呢?
我們知道我們?cè)趥骱瘮?shù)的參數(shù)的時(shí)候,分兩種:按值傳遞和按地址傳遞。
按值傳遞:
void swap(int a,int b)
按值傳遞,編譯器會(huì)自動(dòng)在棧中創(chuàng)建a和b的副本,如果形參不是int類(lèi)型,而是類(lèi)類(lèi)型,那么副本就會(huì)很大,效率很低,這時(shí)候就要考慮按地址傳遞
按地址傳遞:
[cpp]
void swap(int *a,int *b)
{
int c;
c=*a;
*a=*b;
*b=c;
}
上面的功能是達(dá)到了,把指針作為函數(shù)的接受參數(shù)雖然能正常使用,但是它卻不易閱讀,而且很難使用。這時(shí)候引用作為形參就派上用場(chǎng)了:
如果引用作為參數(shù),在函數(shù)內(nèi)部可以修改a的值和b的值,這樣破壞了按值傳遞的保護(hù)機(jī)制,不過(guò)我們可以使用const來(lái)聲明一個(gè)不可修改值的引用,假設(shè)我們不想在函數(shù)內(nèi)修改a的值,那么上面的代碼修改如下:
[cpp]
<span style="font-size:14px;">void swap(const int &a,int &b)
{
int c;
c=a;
a=b;//編譯報(bào)錯(cuò):不能給常量a賦值
b=c;
}
通過(guò)上面的代碼,a就不能被賦值了,這樣a就被稱(chēng)為常引用
總結(jié):引用在使用中單純的給某個(gè)變量取個(gè)別名是沒(méi)有意義的,引用的主要目的是可以作為按地址的參數(shù)傳遞還可以作為函數(shù)的返回值(注意:局部變量是不能返回引用的,因?yàn)榫植孔兞吭诤瘮?shù)返回后會(huì)被銷(xiāo)毀)
3.引用的兩個(gè)特點(diǎn):
第一:定義引用的同時(shí)要對(duì)該引用進(jìn)行初始化,否則編譯不能通過(guò)。如下
[cpp]
//正確的定義引用
int a=0;
int &ra=a;
//錯(cuò)誤的定義引用ra,必須進(jìn)行初始化
int a=0;
int &ra;
ra=a;
第二:引用可以改變其指向地址的數(shù)據(jù),但是不能改變其自身的地址(也就是說(shuō)別名的地址是不會(huì)被改變的,但是別名的值會(huì)變),如
[cpp]
int a;
int &ra=a;
a=999;
cout<<"a="<<a<<" "<<"&a="<<&a<<endl;//a=999 &a=0012ff60
cout<<"ra="<<ra<<" " <<"&ra="<<&ra<<endl;//ra=999 &ra=0012ff60
int b=1000;
ra=b;
cout<<"a="<<a <<" "<<"&a="<<&a<<endl;//a=1000 &a=0012ff60
cout<<"ra="<<ra<<" " <<"&ra="<<&ra<<endl;//ra=1000 &ra=0012ff60
cout<<"b="<<b <<" "<<"&b="<<&b<<endl;//b=1000 &b=0012ff48
ra=1;
cout<<"a="<<a <<" "<<"&a="<<&a<<endl;//a=1 &a=0012ff60
cout<<"ra="<<ra<<" " <<"&ra="<<&ra<<endl;//ra=1 &ra=0012ff60
cout<<"b="<<b<<" " <<"&b="<<&b<<endl;//b=1000 &b=0012ff48
上面的例子中ra=b之后,查看ra的地址可以看出來(lái),ra的地址并沒(méi)有變化,也就是說(shuō)ra是a的別名,那么就不可能變成其他變量(b)的別名,對(duì)ra的操作還是在操作a,而不是b,所以最后在ra=1之后改變的還是a的值。
4.引用的注意事項(xiàng)
4.1 引用聲明的時(shí)候必須進(jìn)行初始化 int num=5;int &rnum=num;
4.2 不能建立數(shù)組的引用(int &a[5]),不能建立引用的引用(int &&a),不能建立引用的指針(int &*a),
4.3 可以建立指針的引用:
[cpp]
int *p;
int *&q=p;
上面的q與它最先結(jié)合的運(yùn)算符是&,所以他的本質(zhì)是一個(gè)引用,然后再與*結(jié)合,所以q是一個(gè)指針的引用,也就是指針p的別名。
4.4 引用在作為函數(shù)的返回值的時(shí)候,千萬(wàn)注意,局部變量是不能作為返回值的,因?yàn)榫植孔兞吭诤瘮?shù)返回的時(shí)候已經(jīng)被釋放了,如下:
[cpp]
class A
{
}
A &func()
{
A a;
return a;
}
上面的func函數(shù)返回的是局部變量類(lèi)A的對(duì)象a,如果外部使用了它會(huì)報(bào)錯(cuò),因?yàn)榫植孔兞縜在函數(shù)返回的時(shí)候已經(jīng)被釋放了。
三.指針和引用
1.首先對(duì)指針和引用的運(yùn)算符&和*進(jìn)行說(shuō)明:
運(yùn)算符&和*在聲明定義的時(shí)候(包括形參的聲明)稱(chēng)為引用運(yùn)算符(聲明變量是引用)和指針說(shuō)明符(聲明變量是指針);它們?cè)谑褂玫臅r(shí)候稱(chēng)為取地址運(yùn)算符(獲取變量的內(nèi)存地址)和指針運(yùn)算符(獲取指針指向的地址里的內(nèi)存數(shù)據(jù)),例如:
運(yùn)算符&:
[cpp]
int a=5;
int &a1=a;//引用運(yùn)算符
cout<<&a1<<endl;//取地址運(yùn)算符
int func(int &a,int &b);//引用運(yùn)算符
運(yùn)算符*
[cpp]
int b=6;
int *b1=&b;//指針說(shuō)明符;取地址運(yùn)算符
cout<<*b1<<endl;//指針運(yùn)算符
int func(int *a,int *b);//指針說(shuō)明符
2.常指針和常引用
它們的聲明方式相同,都是使用const來(lái)定義,但是由于引用本身是不可更改的,所以不用這樣聲明:int const &a;
3.指針和引用的區(qū)別
指針可以為空,引用不可以。
指針可以被賦值,引用只能被初始化,不能被賦值。
在堆中創(chuàng)建一塊內(nèi)存區(qū)域,必須使用指針來(lái)指向它,不能使用引用來(lái)指向它,如int &r=new int;這句話(huà)是錯(cuò)誤的。這時(shí)候你可以這樣int *&r=new int;r表示一個(gè)指針的引用,也就是指向new int所在的堆區(qū)的指針的別名。