詳解C++指針和引用

C++是在C語言的基礎(chǔ)上發(fā)展來的秸滴。C++除了有C語言的指針外,還增加一個新的概念——引用募判,初學者容易把引用和指針混淆一起荡含,面試或者筆試經(jīng)常被考到。

要弄清楚這兩個概念届垫,先從變量說起释液。

一:變量的形式

什么是變量呢?變量(variable)的定義在計算機科學中到底是如何定義的装处?然后variable到底是在內(nèi)存中如何存儲值的呢误债?那么跟著上面的問題,我們來一一的解答符衔。

首先最重要的找前,變量的定義,當你申明一個變量的時候判族,計算機會將指定的一塊內(nèi)存空間和變量名進行綁定躺盛;這個定義很簡單,但其實很抽象形帮,例如:int x = 5; 這是一句最簡單的變量賦值語句了槽惫, 我們常說“x等于5”,其實這種說法是錯誤的辩撑,x僅僅是變量的一個名字而已界斜,它本身不等于任何值的。這條語句的正確翻譯應(yīng)該是:“將5賦值于名字叫做x的內(nèi)存空間”合冀,其本質(zhì)是將值5賦值到一塊內(nèi)存空間各薇,而這個內(nèi)存空間名叫做x。切記:x只是簡單的一個別名而已,x不等于任何值峭判。其圖示如下:

?變量在內(nèi)存中的操作其實是需要經(jīng)過2個步驟的:

1)找出與變量名相對應(yīng)的內(nèi)存地址开缎。

2)根據(jù)找到的地址,取出該地址對應(yīng)的內(nèi)存空間里面的值進行操作林螃。

二:指針

首先介紹到底什么是指針奕删?指針變量和任何變量一樣,也有變量名疗认,和這個變量名對應(yīng)的內(nèi)存空間完残,只是指針的特殊之處在于:指針變量相對應(yīng)的內(nèi)存空間存儲的值恰好是某個內(nèi)存地址。這也是指針變量區(qū)別去其他變量的特征之一横漏。例如某個指針的定義如下:


intx=5;

int*ptr=&x;

ptr即是一個指正變量名谨设。通過指針獲取這個指針指向的內(nèi)存中的值稱為dereference,間接引用绊茧。

其相對于內(nèi)存空間的表示如下:


使用指針的優(yōu)點和必要性:

? ??指針能夠有效的表示數(shù)據(jù)結(jié)構(gòu)铝宵;

? ??能動態(tài)分配內(nèi)存,實現(xiàn)內(nèi)存的自由管理华畏;

? ??能較方便的使用字符串;

? ??便捷高效地使用數(shù)組

? ??指針直接與數(shù)據(jù)的儲存地址有關(guān)尊蚁,比如:值傳遞不如地址傳遞高效,因為值傳遞先從實參的地址中取出值,再賦值給形參代入函數(shù)計算缴渊;而指針則把形參的地址直接指向?qū)崊⒌刂范以铮褂脮r直接取出數(shù)據(jù),效率提高琴锭,特別在頻繁賦值等情況下(注意:形參的改變會影響實參的值N酢)

三:引用

引用是C++引入的新語言特性,是C++常用的一個重要內(nèi)容之一决帖。

引用(reference)在C++中也是經(jīng)常被用到厕九,尤其是在作為函數(shù)參數(shù)的時候,需要在函數(shù)內(nèi)部修改更新函數(shù)外部的值的時候地回,可以說是引用場景非常豐富扁远。正確、靈活地使用引用刻像,可以使程序簡潔畅买、高效。

我在工作中發(fā)現(xiàn)细睡,許多人使用它僅僅是想當然谷羞,只是知道怎么應(yīng)用而已,而不去具體分析這個reference溜徙。

在某些微妙的場合湃缎,很容易出錯犀填,究其原由,大多因為沒有搞清本源雁歌。

下面我就來簡單的分析一下這個reference宏浩。首先我們必須明確的一點就是:reference是一種特殊的pointer。從這可以看出reference在內(nèi)存中的存儲結(jié)構(gòu)應(yīng)該跟上面的指針是一樣的靠瞎,也是存儲的一塊內(nèi)存的地址比庄。例如reference的定義如下:

intx=5;

int&y=x;

引用就是某一變量(目標)的一個別名,對引用的操作與對變量直接操作完全一樣乏盐。

  引用的聲明方法:類型標識符 &引用名=目標變量名佳窑;

上面的代碼,定義了引用y父能,它是變量x的引用神凑,別名,這樣子何吝,目標變量有兩個名稱溉委,即該目標原名稱和引用名,且不能再把該引用名作為其他變量名的別名爱榕。

四瓣喊、引用和指針有什么區(qū)別?

(1)指針:指針是一個變量黔酥,只不過這個變量存儲的是一個地址藻三,指向內(nèi)存的一個存儲單元;而引用跟原來的變量實質(zhì)上是同一個東西跪者,只不過是原變量的一個別名而已棵帽。如:

inta=1;int*p=&a;

inta=1;int&b=a;

? ??上面定義了一個整形變量和一個指針變量p,該指針變量指向a的存儲單元渣玲,即p的值是a存儲單元的地址逗概。

而下面2句定義了一個整形變量a和這個整形a的引用b,事實上a和b是同一個東西柜蜈,在內(nèi)存占有同一個存儲單元仗谆。

(2) ?引用不可以為空,當被創(chuàng)建的時候淑履,必須初始化隶垮,初始化后就不會再改變了;而指針可以是空值秘噪,可以在任何時候被初始化狸吞,指針的值在初始化后可以改變,即指向其它的存儲單元。

(3)可以有const指針蹋偏,但是沒有const引用便斥;

(4)指針可以有多級,但是引用只能是一級(int **p威始;合法 而 int &&a是不合法的)

(5)”sizeof引用”得到的是所指向的變量(對象)的大小枢纠,而”sizeof指針”得到的是指針本身的大小黎棠;

(6)指針和引用的自增(++)運算意義不一樣晋渺;

(7)如果返回動態(tài)內(nèi)存分配的對象或者內(nèi)存,必須使用指針脓斩,引用可能引起內(nèi)存泄漏木西;

(8)從內(nèi)存分配上看,程序為指針變量分配內(nèi)存區(qū)域随静,而不為引用分配內(nèi)存區(qū)域八千,因為引用聲明時必須初始化,從而指向一個已經(jīng)存在的對象燎猛。引用不能指向空值恋捆。

? ??? ??注:標準沒有規(guī)定引用要不要占用內(nèi)存,也沒有規(guī)定引用具體要怎么實現(xiàn)重绷,具體隨編譯器 http://bbs.csdn.net/topics/320095541

(9)從編譯上看鸠信,程序在編譯時分別將指針和引用添加到符號表上,符號表上記錄的是變量名及變量所對應(yīng)地址论寨。指針變量在符號表上對應(yīng)的地址值為指針變量的地址值,而引用在符號表上對應(yīng)的地址值為引用對象的地址值爽茴。符號表生成后就不會再改葬凳,因此指針可以改變指向的對象(指針變量中的值可以改),而引用對象不能改室奏。這是使用指針不安全而使用引用安全的主要原因火焰。從某種意義上來說引用可以被認為是不能改變的指針。

(10)不存在指向空值的引用這個事實胧沫,意味著使用引用的代碼效率比使用指針的要高昌简。因為在使用引用之前不需要測試它的合法性。相反绒怨,指針則應(yīng)該總是被測試纯赎,防止其為空。

下面用通俗易懂的話來概述一下:

指針-對于一個類型T南蹂,T*就是指向T的指針類型犬金,也即一個T*類型的變量能夠保存一個T對象的地址,而類型T是可以加一些限定詞的,如const晚顷、volatile等等峰伙。見下圖,所示指針的含義:


引用-引用是一個對象的別名该默,主要用于函數(shù)參數(shù)和返回值類型瞳氓,符號X&表示X類型的引用。見下圖栓袖,所示引用的含義:


總之匣摘,可以歸結(jié)為"指針指向一塊內(nèi)存,它的內(nèi)容是所指內(nèi)存的地址叽赊;而引用則是某塊內(nèi)存的別名恋沃,引用不改變指向。"

五必指、指針傳遞和引用傳遞

在C++中囊咏,指針和引用經(jīng)常用于函數(shù)的參數(shù)傳遞,然而塔橡,指針傳遞參數(shù)和引用傳遞參數(shù)是有本質(zhì)上的不同的:

? ??? ??指針傳遞參數(shù)本質(zhì)上是值傳遞的方式梅割,它所傳遞的是一個地址值。值傳遞過程中葛家,被調(diào)函數(shù)的形式參數(shù)作為被調(diào)函數(shù)的局部變量處理户辞,即在棧中開辟了內(nèi)存空間以存放由主調(diào)函數(shù)放進來的實參的值,從而成為了實參的一個副本癞谒。值傳遞的特點是被調(diào)函數(shù)對形式參數(shù)的任何操作都是作為局部變量進行底燎,不會影響主調(diào)函數(shù)的實參變量的值弹砚。(這里是在說實參指針本身的地址值不會變)

? ??? ??而在引用傳遞過程中双仍,被調(diào)函數(shù)的形式參數(shù)雖然也作為局部變量在棧中開辟了內(nèi)存空間,但是這時存放的是由主調(diào)函數(shù)放進來的實參變量的地址桌吃。被調(diào)函數(shù)對形參的任何操作都被處理成間接尋址朱沃,即通過棧中存放的地址訪問主調(diào)函數(shù)中的實參變量。正因為如此茅诱,被調(diào)函數(shù)對形參做的任何操作都影響了主調(diào)函數(shù)中的實參變量逗物。

? ??? ??引用傳遞和指針傳遞是不同的,雖然它們都是在被調(diào)函數(shù)椛螅空間上的一個局部變量翎卓,但是任何對于引用參數(shù)的處理都會通過一個間接尋址的方式操作到主調(diào)函數(shù)中的相關(guān)變量。而對于指針傳遞的參數(shù)尔当,如果改變被調(diào)函數(shù)中的指針地址莲祸,它將影響不到主調(diào)函數(shù)的相關(guān)變量蹂安。如果想通過指針參數(shù)傳遞來改變主調(diào)函數(shù)中的相關(guān)變量,那就得使用指向指針的指針锐帜,或者指針引用田盈。


六、返回引用和返回指針

C++返回引用類型

? ??A& a(){ return *this;} 就生成了一個固定地址的指針缴阎,并把指針帶給你允瞧。

但A a() { return *this;}會生成一個臨時對象變量,并把這個臨時變量給你蛮拔,這樣就多了一步操作述暂。

當返回一個變量時,會產(chǎn)生拷貝建炫。當返回一個引用時畦韭,不會發(fā)生拷貝,你可以將引用看作是一個變量的別名肛跌,就是其他的名字艺配,引用和被引用的變量其實是一個東西,只是有了兩個名字而已衍慎。

問題的關(guān)鍵是转唉,當你想要返回一個引用而不是一個拷貝時,你要確保這個引用的有效性稳捆,比如:

? ??? ??int & fun() { int a; a=10; return a; }

這樣是不行的赠法,因為a會在fun退出時被銷毀,這時返回的a的引用是無效的乔夯。

這種情況下砖织,如果fun的返回類型不是int & 而是int就沒有問題了。

返回指針的話末荐,誰調(diào)用該函數(shù)镶苞,誰負責接觸返回的指針。

全局變量鞠评,局部靜態(tài)變量,局部動態(tài)分配變量 都可以作為函數(shù)返回值壕鹉。?

局部自動變量不行

函數(shù)內(nèi)部等局部變量剃幌,存儲在棧中的變量是不能作為返回值的,雖然可以讀取正確的值晾浴,但是這是一塊未分配的內(nèi)存负乡,當別的進程用到時就會出錯,這個指針相當于野指針脊凰。返回值可以是局部動態(tài)分配的內(nèi)存空間抖棘,這一部分分配在堆上茂腥,在主動釋放之前別的進程是無法使用的內(nèi)存區(qū)域。

不管是指針還是引用都是如此切省。


七最岗、特別之處const

為什么要提到const關(guān)鍵字呢?因為const對指針和引用的限定是有差別的:

常量指針VS常量引用

常量指針:指向常量的指針朝捆,在指針定義語句的類型前加const般渡,表示指向的對象是常量。

定義指向常量的指針只限制指針的間接訪問操作芙盘,而不能規(guī)定指針指向的值本身的操作規(guī)定性驯用。


?常量指針定義"const int* pointer=&a"告訴編譯器,*pointer是常量儒老,不能將*pointer作為左值進行操作蝴乔。

常量引用:指向常量的引用,在引用定義語句的類型前加const驮樊,表示指向的對象是常量薇正。也跟指針一樣不能對引用指向的變量進行重新賦值操作。



指針常量VS引用常量

在指針定義語句的指針名前加const巩剖,表示指針本身是常量铝穷。在定義指針常量時必須初始化!而這是引用與生俱來的屬性佳魔,無需使用const曙聂。

指針常量定義"int* const pointer=&b"告訴編譯器,pointer(地址)是常量鞠鲜,不能作為左值進行操作宁脊,但是允許修改間接訪問值,即*pointer(地址所指向內(nèi)存的值)可以修改贤姆。


常量指針常量VS常量引用常量

常量指針常量:指向常量的指針常量榆苞,可以定義一個指向常量的指針常量,它必須在定義時初始化霞捡。


定義"const int* const pointer=&c"

告訴編譯器坐漏,pointer和*pointer都是常量,他們都不能作為左值進行操作碧信。


而不存在所謂的"常量引用常量"赊琳,因為引用變量就是引用常量。C++不區(qū)分變量的const引用和const變量的引用砰碴。程序決不能給引用本身重新賦值躏筏,使他指向另一個變量,因此引用總是const的呈枉。如果對引用應(yīng)用關(guān)鍵字const趁尼,起作用就是使其目標稱為const變量埃碱。即

沒有:const double const& a=1;

只有const double& a=1;

double?b=1;

constdouble&?a=b;

b=2;//正確

a=3;//出錯error: assignment of read-only reference `a'

總結(jié):有一個規(guī)則可以很好的區(qū)分const是修飾指針,還是修飾指針指向的數(shù)據(jù)——畫一條垂直穿過指針聲明的星號(*)酥泞,如果const出現(xiàn)在線的左邊砚殿,指針指向的數(shù)據(jù)為常量;如果const出現(xiàn)在右邊婶博,指針本身為常量瓮具。而引用本身就是常量,即不可以改變指向凡人。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末名党,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子挠轴,更是在濱河造成了極大的恐慌传睹,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岸晦,死亡現(xiàn)場離奇詭異欧啤,居然都是意外死亡,警方通過查閱死者的電腦和手機启上,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門邢隧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人冈在,你說我怎么就攤上這事倒慧。” “怎么了包券?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵纫谅,是天一觀的道長。 經(jīng)常有香客問我溅固,道長付秕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任侍郭,我火速辦了婚禮询吴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘亮元。我一直安慰自己汰寓,他們只是感情好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布苹粟。 她就那樣靜靜地躺著,像睡著了一般跃闹。 火紅的嫁衣襯著肌膚如雪嵌削。 梳的紋絲不亂的頭發(fā)上毛好,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音苛秕,去河邊找鬼肌访。 笑死,一個胖子當著我的面吹牛艇劫,可吹牛的內(nèi)容都是我干的吼驶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼店煞,長吁一口氣:“原來是場噩夢啊……” “哼蟹演!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起顷蟀,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤酒请,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鸣个,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體羞反,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年囤萤,在試婚紗的時候發(fā)現(xiàn)自己被綠了昼窗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡涛舍,死狀恐怖澄惊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情做盅,我是刑警寧澤缤削,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站吹榴,受9級特大地震影響亭敢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜图筹,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一帅刀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧远剩,春花似錦扣溺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至痢掠,卻和暖如春驱犹,著一層夾襖步出監(jiān)牢的瞬間嘲恍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工雄驹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留佃牛,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓医舆,卻偏偏與公主長得像俘侠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蔬将,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

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

  • 1.語言中變量的實質(zhì) 要理解C指針爷速,我認為一定要理解C中“變量”的存儲實質(zhì), 所以我就從“變量”這個東西開始講起吧...
    金巴多閱讀 1,768評論 0 9
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,516評論 1 51
  • 指針是C語言中廣泛使用的一種數(shù)據(jù)類型娃胆。 運用指針編程是C語言最主要的風格之一遍希。利用指針變量可以表示各種數(shù)據(jù)結(jié)構(gòu); ...
    朱森閱讀 3,440評論 3 44
  • 基本內(nèi)置類型 算術(shù)類型字符整型布爾值浮點數(shù) 空類型(void) 算術(shù)類型 帶符號類型和無符號類型int、short...
    2625K閱讀 3,174評論 0 1
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,097評論 1 32