C++中的指針

技術(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)的概念如下表:


    與指針有關(guān)的概念.png

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è)變量的地址。


    指向指針的指針.png
  • 一個(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);
    
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盹舞,一起剝皮案震驚了整個(gè)濱河市产镐,隨后出現(xiàn)的幾起案子隘庄,更是在濱河造成了極大的恐慌,老刑警劉巖癣亚,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丑掺,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡述雾,警方通過(guò)查閱死者的電腦和手機(jī)街州,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)玻孟,“玉大人唆缴,你說(shuō)我怎么就攤上這事∈螋幔” “怎么了面徽?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)匣掸。 經(jīng)常有香客問(wèn)我趟紊,道長(zhǎng),這世上最難降的妖魔是什么旺聚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任织阳,我火速辦了婚禮,結(jié)果婚禮上砰粹,老公的妹妹穿的比我還像新娘唧躲。我一直安慰自己,他們只是感情好碱璃,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布弄痹。 她就那樣靜靜地躺著,像睡著了一般嵌器。 火紅的嫁衣襯著肌膚如雪肛真。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天爽航,我揣著相機(jī)與錄音蚓让,去河邊找鬼。 笑死讥珍,一個(gè)胖子當(dāng)著我的面吹牛历极,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播衷佃,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼趟卸,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起锄列,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤图云,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后邻邮,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體竣况,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年饶囚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帕翻。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡萝风,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出紫岩,到底是詐尸還是另有隱情规惰,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布泉蝌,位于F島的核電站歇万,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏勋陪。R本人自食惡果不足惜贪磺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诅愚。 院中可真熱鬧寒锚,春花似錦、人聲如沸违孝。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)雌桑。三九已至喇喉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間校坑,已是汗流浹背拣技。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耍目,地道東北人膏斤。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像制妄,于是被迫代替她去往敵國(guó)和親掸绞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350