Cpp:指針

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

p1p2 之間相差3個對象舅踪,所以 n = 3n 是標(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.


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市属桦,隨后出現(xiàn)的幾起案子熊痴,更是在濱河造成了極大的恐慌,老刑警劉巖聂宾,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件果善,死亡現(xiàn)場離奇詭異,居然都是意外死亡系谐,警方通過查閱死者的電腦和手機(jī)巾陕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門讨跟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人鄙煤,你說我怎么就攤上這事晾匠。” “怎么了梯刚?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵凉馆,是天一觀的道長。 經(jīng)常有香客問我亡资,道長澜共,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任锥腻,我火速辦了婚禮嗦董,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瘦黑。我一直安慰自己京革,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布幸斥。 她就那樣靜靜地躺著匹摇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪甲葬。 梳的紋絲不亂的頭發(fā)上来惧,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機(jī)與錄音演顾,去河邊找鬼。 笑死隅居,一個胖子當(dāng)著我的面吹牛钠至,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播胎源,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼棉钧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涕蚤?” 一聲冷哼從身側(cè)響起宪卿,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎万栅,沒想到半個月后佑钾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡烦粒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年休溶,在試婚紗的時候發(fā)現(xiàn)自己被綠了代赁。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡兽掰,死狀恐怖芭碍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情孽尽,我是刑警寧澤窖壕,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站杉女,受9級特大地震影響瞻讽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宠纯,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一卸夕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧婆瓜,春花似錦快集、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至猴蹂,卻和暖如春院溺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背磅轻。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工珍逸, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人聋溜。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓谆膳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親撮躁。 傳聞我的和親對象是個殘疾皇子漱病,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,516評論 1 51
  • 307、setValue:forKey和setObject:forKey的區(qū)別是什么把曼? 答:1, setObjec...
    AlanGe閱讀 1,547評論 0 1
  • 話題:正確本身的價值 1+1=2杨帽,E=mc2兩個式子同樣都是正確的∴途可我們思考“正確”本身的價值時注盈,第一個式子正確...
    卷卷皮閱讀 254評論 1 3
  • 光澤照亮的地方背后暗黑色系蔓延的地方就會越多当凡,我們唯有堅定自己的內(nèi)心往前沖山害,往前走,不回頭沿量。 我們都知道努力的意義...
    阿俊xi閱讀 373評論 0 1
  • 與其浪費時間在想有的沒的朴则,不如想方設(shè)法的去賺錢权纤,去賺大錢!遠(yuǎn)離一切讓人墮落乌妒、腐爛的東西P谙搿! 在賺錢的過程中撤蚊,想要的...
    小Yan林閱讀 119評論 0 0