2015/11/13 16:16更新:
為什么得到的喜歡數(shù)少于關(guān)注的人數(shù)呢晌坤?我分析了一下原因画恰,難道是因為“喜歡”按鈕在文章的最底部财喳?侄榴!希望 @簡叔 改進(jìn)呀 =_=
下面是原文遭贸,戈咳,
指針跟迭代器類似,也可以對指針進(jìn)行 解引用(*
) 和 自增(++
) 操作壕吹,其含義和迭代器類似著蛙。
指針用于指向?qū)ο螅c迭代器類似耳贬,指針提供對其所指對象的間接訪問踏堡。不同在于:指針指向單個對象,而迭代器只能訪問容器內(nèi)的元素咒劲。具體來說顷蟆,指針保存的是另一個對象的地址:
string s("hello");
string *p = &s; //指針 p 保存 s 的地址
&
是取地址符號,該符號只能用于左值腐魂。只有變量作為左值時帐偎,才能取其地址。
1挤渔、指針的定義和初始化
vector<int> *vp; //指向 vector<int> 對象的指針
int *ip; //指向 int 對象的指針
string *sp; //指向 string 對象的指針
另一種風(fēng)格的指針
int* ip;
但容易引起誤解肮街,會認(rèn)為 int*
是一種類型。但下例只有 ip1
是指針判导,ip2
是普通的整型變量
int* ip1, ip2;
指針的取值
int val = 1024;
int *ip1 = 0; //ip1 不指向任何對象
int *ip2 = &val; //ip2 指向val
int *ip3; //ip3 未初始化
ip1 = ip2; //ip1 指向 val
ip2 = 0; //ip2 不指向任何對象
避免使用未初始化的指針
初始化的約束
對指針初始化或賦值只能使用下列四種類型的值:
- 0常量表達(dá)式(編譯時能獲得0值得const對象或字面值常量0)嫉父;
- 類型匹配的對象的地址沛硅;
- 另一對象之后的下一地址;
- 同一類型的另一有效指針绕辖;
舉例:
int ival = 1;
double dval = 1.5;
int zero = 0;
const int czero = 0;
int *ip;
double *dp;
ip = ival; //error
ip = zero; //error
ip = czero; //ok:編譯時可以獲得 0
ip = 0摇肌; //ok:字面值常量 0
ip = &ival; //ok
dp = ip; //error:類型不匹配
當(dāng)然 0 值還可以使用從C語言繼承下來的預(yù)處理器變量 NULL
,它在 cstdlib 頭文件中定義仪际,其值為 0围小。
int *ip = NULL; //相當(dāng)于 int *ip = 0;
NULL
不是標(biāo)準(zhǔn)庫中定義的,所以不需要std::
树碱。
void*指針
void* 指針可以保存任何類型對象的地址:
double dval = 3.14;
double *dp = &dval;
void *vp = &dval; //ok
vp = dp; //ok
但 void* 指針只允許有限的操作:
- 與另一指針比較肯适;
- 向函數(shù)傳遞 void* 指針,或函數(shù)返回 void* 指針成榜;
- 給另一個 void* 指針賦值框舔。
不允許 void* 指針操作所指向的對象。
3赎婚、指針的操作
*操作符
string s1("hello");
string s2("world")刘绣;
string *sp = &s1;
cout << *sp << endl; //解引用
sp = &s2; //改變指針?biāo)笇ο?cout << *sp << endl; //解引用
*sp = "hello world"; //改變所指的內(nèi)容
cout << *sp << endl; //解引用
輸出結(jié)果:
hello
world
hello world
對 sp
的解引用可以獲得 s
的值,因為 sp
指向 s
挣输,所以給 *sp
賦值可以改變 s
的值纬凤。
指針和引用的比較
雖然引用(reference)和指針都可以間接訪問另一個值,但有區(qū)別:
- 定義引用時沒有初始化時錯誤的撩嚼;
- 賦值行為的差異:給引用賦值修改的是該引用所關(guān)聯(lián)的對象的值停士,并不是與另一個對象關(guān)聯(lián);
- 引用一經(jīng)初始化绢馍,就始終指向同一個特定的對象
指針的例子
int ival1 = 1024, ival2 = 2048;
int *ip1 = &ival1, *ip2 = &ival2;
ip1 = ip2; //ip1 此時指向 ival2
而引用的例子
int &r1 = ival1; int &r2 = ival2;
r1 = r2; //將 ival2 賦給 ival1
上面的修改只會修改引用所關(guān)聯(lián)的對象向瓷,并不會改變改變引用本身。并且修改后舰涌,兩個引用還是指向原來關(guān)聯(lián)的對象猖任。
指向指針的指針
指針本身也是需要占內(nèi)存的對象,所以指針也可以被指針訪問瓷耙。
int ival = 1024;
int *ip = &ival;
int **ipp = &ip;
cout << ival << endl;
cout << *ip << endl;
cout << **ipp << endl;
結(jié)果
1024
1024
1024
可以用三種方式輸出ival的值朱躺。
最后舉一個例子
int i = 10, j =10;
int *p1 = &i, *p2 = &j;
cout << *p1 << endl;
cout << *p2 << endl;
*p2 = *p1 * *p2; //改變 p2 所指的內(nèi)容
cout << *p1 << endl;
cout << *p2 << endl;
*p1 *= *p1; //改變 p1 所指的內(nèi)容
cout << *p1 << endl;
cout << *p2 << endl;
結(jié)果
10
10
10
100
100
100
4、使用指針訪問數(shù)組
指針與數(shù)組密切相關(guān)搁痛。特別是在表達(dá)式中使用數(shù)組名時长搀,改名字會自動轉(zhuǎn)換為指向數(shù)組第一個元素的指針:
int val[] = {0, 1, 2, 3};
int *p = val; //p 指向 val[0]
p = &val[3]; //p 指向 val[3]
指針的算術(shù)運算
上面的 p = &val[3];
使用下標(biāo)操作,也可以通過 指針的算術(shù)操作(pointer arithmetic) 來獲取指定的地址:
p = val; //p 指向 val[0]
int *p2 = p + 3; //p2 指向 val[3]
指針的算術(shù)操作只有在計算過后的新指針還是指向同一數(shù)組的元素才算合法鸡典,且不能越界源请,比如上面
int *p2 = p + 3;
改成int *p2 = p + 4;
就會出錯,因為數(shù)組val
的大小為 4,最大的下標(biāo)為 3谁尸。
兩個指針之間還可以做減法
ptrdiff_t n = p2 - p1; //n = 3
p1
和 p2
之間相差3個對象舅踪,所以 n = 3
。 n
是標(biāo)準(zhǔn)庫類型(library type) ptrdiff_t 類型良蛮。與 size_t 類型一樣抽碌,ptrdiff_t 也是一種與機(jī)器相關(guān)類型,在cstddef頭文件中定義决瞳。
允許在指針上加減 0货徙,使指針保持不變。
解引用和指針?biāo)阈g(shù)操作之間的相互作用
在指針上加上一個整數(shù)值皮胡,其結(jié)果仍是指針痴颊。允許在這個結(jié)果上直接進(jìn)行解引用操作,而不必先把它賦給一個新的指針:
int last = *(val + 3); //相當(dāng)于 val[3]
需要寫括號胸囱,如果寫成 int last = *val + 3;
則相當(dāng)于 val[0] + 3
祷舀。
下標(biāo)和指針
使用下標(biāo)訪問數(shù)組時,它實際上是使用下標(biāo)訪問指針:
int val[] = {0,1,2,3,4};
int i = val[0] //val 指向數(shù)組 val[] 的第一個元素
另一個例子
int *p = &val[2]; //ok: p 指向第二個元素
int j = p[1]; //ok: p[1] 相當(dāng)于 *(p + 1), j = val[3]
int k = p[-2]; //ok: p[-2] 相當(dāng)于 val[0]
計算數(shù)組的超出末端指針
vector 類型提供的end操作將返回指向超出 vector 末端位置的一個迭代器烹笔。類似的,可以計算數(shù)組的超出末端指針的值:
const size_t arr_size = 5;
int arr[arr_size] = {1,2,3,4,5};
int *p = arr;
int *p2 = p + arr_size; //ok:超出末端的指針
C++允許計算數(shù)組或?qū)ο蟮某瞿┒说牡刂放桌觯辉试S對此地址進(jìn)行解引用操作谤职。而計算數(shù)組超出末端之后或數(shù)組首地址之前的地址都是不合法的。
p2
不能解引用操作亿鲜,但能與其他指針比較允蜈,或者用作指針?biāo)阈g(shù)表達(dá)式的操作數(shù)。
輸出數(shù)組元素
const size_t arr_size = 5;
int arr[arr_size] = {1,2,3,4,5};
for (int *begin = arr, *end = arr + arr_size; begin != end; ++begin){
cout << *begin << "," << endl;
}
指針是數(shù)組的迭代器蒿柳。上面的程序與迭代器程序非常相似饶套,事實上,內(nèi)置類型具有標(biāo)準(zhǔn)庫容器的許多性質(zhì)垒探,指針就是數(shù)組的迭代器妓蛮。
5、指針與const限定符
兩種類型:
- 指向const對象的指針圾叼;
- const指針蛤克;
指向const對象的指針
如果指針指向的是const對象,則不再能夠使用指針來修改對象的內(nèi)容夷蚊。為了保證這個特性构挤,C++語言強(qiáng)制要求指向const對象的指針也必須具有const特性:
const double *cdp;
cdp
是指向一個double類型const對象的指針,const限定了 cdp
指針?biāo)赶虻膶ο筇韫模⒎?cdp
本身筋现。即 cdp
不是const類型,在定義時不一定需要給它初始化;如果有需要的話矾飞,允許給 cdp
重新賦值一膨,使其指向另一個const對象,但不能通過 cdp
修改所指對象的值:
*cdp = 2; //error: *cdp might be const
把一個const對象的地址賦給一個普通的凰慈、非const對象的指針也會導(dǎo)致編譯錯誤:
const double pi = 3.14;
double *dp = π //error: dp is a plain pointer
const double *cdp = π //ok: cdp is a pointer to const
不能使用 void* 指針保存const對象的地址汞幢,而必須使用const void*指針保存:
const int val = 2;
const void *cvp = &val; //ok: cvp is const
void *vp = &val; //error: val is const
允許將非const對象賦給指向const對象的指針,例如:
double dval = 3.14;
const double *cdp = &dval; //ok: 但是不能通過指針 cdp 改變 dval 的值
盡管 dval 不是 const 對象微谓,但任何企圖通過指針 cdp
修改其值得行為都會導(dǎo)致錯誤森篷。
事實上,也有辦法通過指向const對象指針改變所指的非const對象的值:
double dval = 3.14;
const double *cdp = &dval; //ok: 但是不能通過指針 cdp 改變 dval 的值
*cdp = 3.14159; //error: 不能通過 cdp 改變所指對象的值
double *dp = &dval; //ok:dp 可以指向非const對象
*dp = 3.14159; //ok
cout << *cdp << endl; //此時會輸出:3.14159
可以這樣理解指向const對象的指針:自以為指向const對象的指針豺型。但并不能保證所指向的對象一定是const對象仲智。
const指針
這種指針本身不能修改:
int ival = 0;
int *const icp = &ival; //icp 是const指針
這樣理解:icp
是指向int對象的const的指針。跟其他const對象類似姻氨,const指針的值不能修改钓辆,意思就是不能使 icp
指向其他對象。任何企圖給const指針賦值的行為都會出錯(即使是賦它本身的值也一樣):
icp = icp; //error: icp is const
并且 const指針在定義時必須初始化肴焊。
const指針?biāo)笇ο蟮闹的芊癖辉撝羔樞薷耐耆Q于該對象的類型前联,例如 icp
指向一個普通的非 const int 型的對象,則可以使用 icp
修改該對象的值:
*icp = 1;
指向const對象的const指針
const double pi = 3.14159;
const double *const cdcp = π
上面的意思是既不能修改 pi
的值娶眷,也不能修改 cdcp
所指的對象似嗤。
指針和typedef
在typedef中使用指針往往會帶來意外的結(jié)果,下面是一個幾乎所有初學(xué)者都會搞錯的問題:請問 cstr
變量是什么類型届宠?
typedef string *sp;
const sp cstr;
簡單的回答是:const sp 類型的指針烁落。進(jìn)一步:const sp 所表示的真實類型是什么?可能會認(rèn)為是
const string *cstr; //error
但這是錯誤的豌注,原因是:聲明const sp時伤塌,const修飾的是 sp 類型,而 sp 是一個指針轧铁。所以等價于
string *const cstr;
理解const聲明:
//s1 和 s2 都是const
string const s1;
const string s2;
用typedef寫const類型定義時每聪,const限定符加在類型前面容易引起誤解
string s;
typedef string *sp;
//下面三種定義時等價的
const sp cstr1 = &s; //容易誤解
sp const cstr2 = &s;
string *const cstr3 = &s;
舉例
int i = -1;
const int ic = i; //ok
const int *pic = ? //ok
int *const cpi = ? //error
const int *const cpic = ? //ok
END.