C++中的指針

技術交流QQ群:1027579432,歡迎你的加入!

1.Cpp中的指針

  • 每個變量都有一個內存位置,每一個內存位置都定義了可使用&地址運算符訪問的地址,它表示了變量在內存中的一個地址
        int var1;
        int var2[10];
        cout << "變量var1的地址:" << &var1 << endl;  // 訪問變量var1的在內存中的位置,使用&地址運算符,獲得變量在內存中的地址
        cout << "變量var2的地址:" << &var2 << endl;
    

2.什么是指針?

  • 指針也是一個變量,它存放的值是另一個變量的地址,即變量在內存中的位置,即內存中的直接地址。與其他變量或常量一樣,在使用指針存儲其他變量地址之前,必須對指針進行聲明,指針變量聲明的一般形式是:
        數據類型 *指針變量名;
    
  • 在上述指針的指針變量聲明過程中,數據類型必須是一個有效的C++數據類型,用來聲明指針變量的星號*與乘法中使用的星號是相同的。但是,在上面的聲明語句中,星號是用來指定一個變量是指針,有效的指針變量的聲明是:
        int *p;  
        float *p1;
        double *ptr;
        char *p2;
    
  • 由于所有指針變量中存放的都是另一個變量在內存中的地址,所以不管指針指向的是int,float,char還是其他數據類型,指針變量的值實際數據類型都是一樣的!都是一個代表內存地址的十六進制數。不同數據類型的指針變量之間唯一的區別是:指針所指向的變量或常量的數據類型不同。32位機系統中,指針變量所占的字節數是sizeof(*p) = 4個字節。

3.C++中使用指針

  • 指針變量使用時,會頻繁使用以下幾個操作:定義一個指針變量,把另外一個變量的地址賦值個指針,訪問指針變量中可用地址的值,這些操作都是通過*運算符來返回位于操作數所指定地址的變量的值。
        int var = 20;  // 普通變量的初始化
        int *ip;  // 指針變量的聲明
        ip = &var;   // 用指針變量存儲普通變量var的地址
        cout << "var的值是:" << var << endl;
        cout << "普通變量var在內存中的地址: " << &var << endl;
        cout << "指針變量ip在內存中的地址: " << &ip << endl;
        cout << "通過訪問指針變量ip中存放的普通變量的地址來得到普通變量var的值: " << *ip << endl;
    

4.Cpp中指針詳解

  • C++中與指針有關的概念如下表:


    與指針有關的概念.png

4.1 C++中的NULL指針

  • 在指針變量聲明時,如果沒有明確的地址可以賦值時,為指針變量賦值一個NULL值是一個很好的習慣。NULL指針是一個定義在標準庫中的值為0的常量。如下面的程序:
        int *ptr = NULL;
        cout << "指針變量ptr在內存中的地址:: " << &ptr << endl;
        cout << "ptr的值是: " << ptr << endl;
    
  • 在大多數的操作系統上,程序不允許訪問地址為0的內存,因為該內存是操作系統保留的。然而,內存地址0有特別重要的意義,它表明該指針不指向一個可訪問的內存位置。但按照慣例,如果指針包含空值(零值),則假定它不指向任何東西。如果檢查一個空指針,需要使用if語句:
        if(ptr)  // 如果ptr非空,則完成
        if(!ptr)  // 如果ptr為空,則完成
    
  • 因此,如果所有未使用的指針都被賦予空值,同時避免使用空指針,就可以防止誤用一個未初始化的指針。很多時候,未初始化的變量存有一些垃圾值,導致程序難以調試。

4.2 指針的算術運算

  • 指針變量中存放的是一個用數值表示另一個變量的地址,可以對指針變量執行算術運算。對指針進行四個算術運算:++ -- - +,假設ptr是一個指向地址是1000的整型指針變量,是一個32位整數,對該指針執行算術運算ptr++后,ptr的指向位置是1004。因為ptr每增加一次,它都指向下一個整數位置,即當前位置往后移動4個字節。這個運算在不影響內存位置中實際值的情況下,移動指針到下一個內存位置。如果ptr執向的是一個地址是1000的char型字符,上面的運算會導致指針指向位置1001,因為下一個字符位置是1001。
  • 遞增一個指針:在程序中使用指針代替數組,因為指針變量可以遞增,而數組不能遞增,因為數組名是一個指針常量,如下面的例子:
        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 << "通過訪問指針變量pp中存放的普通變量的地址來得到數組元素的值: " << *pp << endl;
            cout << "------------------------------------------------\n";
            pp++;  // 移動到下一個位置
        }
    
  • 遞減一個指針:對指針進行遞減運算,即把值減去其指向的數據類型的字節數,如下所示:
        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 << "通過訪問指針變量pt中存放的普通變量的地址來得到數組元素的值: " << *pt << endl;
            cout << "------------------------------------------------\n";
            pt--;  // 移動到下一個位置
        }
    
  • 指針的比較:指針可以用關系運算符進行比較,如果p1和p2指向兩個相關的變量,如同一個數組中的不同元素,則可以對p1和p2進行大小比較。
        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數組

  • 指針與數組是密切相關的,在很多情況下兩個是可以互換的。例如,通過一個指向數組開頭的指針,可以通過使用指針的算術運算或數組的索引來訪問數組,如下面例子:
        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 << "通過訪問指針變量pp中存放的普通變量的地址來得到數組元素的值: " << *pp << endl;
            cout << "------------------------------------------------\n";
            pp++;  // 移動到下一個位置
        }
    
  • 然而,指針與數組并不是完全可以互換的,如下面的例子:
        int b[MAX] = {1, 11, 111};
        cout << "b = " << b << endl;
        cout << "*b = " << *b << endl;
        for (int i = 0; i < MAX; i++){
            *b = i;   // 改變的是數組第一個元素的值 
            // b++;   數組名是一個指針常量,不能進行修改
            *(b + 2) = 555;
        }
    // 不是說數組內的值不能修改,而是只要b的值(地址)不變,那么相應位置的數值改變了,也不會影響該數組的首地址。
    

4.4 指針數組

  • 引入:一個由3個整數組成的數組
        int c[MAX] = {6, 66, 666};
        for (int i = 0; i < MAX; i++){
            cout << "c[" << i << "]的值是: " << c[i] << endl;
        }
    
  • 可能有一種情況是:希望數組存儲的是指向int/char或者其他數據類型的指針,下面是一個指向整數的指針數組的聲明:
        int * ptr[3];
    
  • 把ptr聲明為一個數組,由3個整數指針組成。因此,ptr中的每個元素,都是指向int類型的指針,見下面的例子:
        int cc[MAX] = {6, 66, 666};
        int *pc[MAX];  // 定義一個指針數組
        for (int i = 0; i < MAX; i++){
            pc[i] = &cc[i];  // 由于pc數組中的每個元素都是指向int類型的指針,所以這里賦值為cc[i]的每個元素的地址
        }
        for (int i = 0; i < MAX; i++){
            cout << "cc[" << i << "]的地址是: " << pc[i] << endl;
            cout << "cc[" << i << "] = " << *pc[i] << endl;
        }
    
  • 也可以用一個指向字符的指針數組來存儲一個字符串列表,如下面例子:
        const char *names[MAX] = { 
            "Curry", 
            "Harden", 
            "Durant",
        }; // const char * names[MAX]是指針數組,它的本質是存儲指針的數組,存儲的元素是char類型的指針
        for (int i = 0; i < MAX; i++){
            cout << "names[" << i << "] = "<< names[i] << endl; // 輸出字符串的值
            cout << "*names[" << i << "]的值是: " << *names[i] << endl; // 輸出指針所指向的字符串的首地址的值
            cout << "names[" << i << "]的地址是: " << &names[i] << endl; // 輸出指針所指向的字符串的首地址
        }
    

4.5 指向指針的指針(多級間接尋址)

  • 指向指針的指針是一種多級間接尋址的形式,或者說是一個指針鏈。通常,一個指針中存儲的是另一個變量的地址,當再定義一個指向指針的指針時,第一個指針變量就包含了第二個指針變量的地址,第二個指針變量存儲的是另一個變量的地址。


    指向指針的指針.png
  • 一個指向指針的指針變量必須進行聲明,即在變量名前加上兩個**,下面是一個指向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 傳遞指針給函數

  • C++中允許傳遞指針給函數,只需要簡單地聲明函數參數為指針類即可,下面的實例是傳遞一個long型指針給函數,并在函數內改變這個值:
        void getSeconds(unsigned long *par){
            *par = time(NULL);  // 獲取當前秒數
        }
        // 傳遞指針給函數
        unsigned long sec;
        getSeconds(&sec);
        // 輸出實際值
        cout << "number of seconds: " << sec << endl;
    
  • 能接收指針作為參數的函數,也可以接收數組作為參數,如下所示:
        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;
        // 傳遞一個指向數組的指針作為參數
        avg = getAverage(balance, 5);
        cout << "平均值: " << avg << endl;
    

4.7 從函數返回指針

  • 從函數返回指針,需要聲明一個返回指針的函數,如下所示:
        int * myFunction(){
            語句;
        }
    
  • 此外,C++不支持在函數外部返回局部變量的地址,除非定義的是局部變量static變量。
  • 返回從函數返回指針的實例如下:
        // 要生成和返回隨機數的函數
        int * getRandom(){
            static int r[10];
            // 設置種子
            srand((unsigned)time(NULL));
            for (int i=0; i<10;i++){
                r[i] = rand();
                cout << "r[" << i << "]=" << r[i] << endl;
            }
            return r;
        }
        // 從函數返回一個指針
        int *ppg;
        ppg = getRandom();   // 調用子函數,從子函數中返回一個指針
        for (int i = 0; i < 10; i++)
            cout << "*(ppg + " << i << ") = " << *(ppg + i) << endl;
    

5.指針小結

  • 指針的本質是變量,可以是各種數據類型,定義一個指針"*ip",其中"ip"需要賦于一個地址(可以用&符號獲取其他變量的地址再賦值給 ip),而"*ip"是一個具體的值,即讀取地址后獲得的值;&符號的意思是取地址,也就是返回一個對象在內存中的地址, *符號的意思是取得一個指針所指向的對象,也就是如果一個指針保存著一個內存地址,那么它就返回在那個地址的對象
        #include <iostream>
        using namespace std;
    
        int main()
        {
            int var = 20;
            int *ip;
            ip = &var;
    
            cout << "var的值:";
            cout << var << endl;
    
            cout << "變量ip的儲存地址:";
            cout << ip << endl;
    
            cout << "指針*ip的值:";
            cout << *ip << endl; 
            return 0;
        }
    
  • C++中允許聲明指向函數的指針,被稱為函數指針。函數指針的聲明類似于函數的聲明,只不過將函數名變成了(*指針名),定義方式: int (*fp)(int a); // 定義了一個指向函數(這個函數參數僅僅為一個int類型,函數返回值是int類型)的指針fp
        int func(int b);
        {
            cout << b;
            return ++b;
        }
        // 定義一個函數指針
        int(*p)(int);
        p = func;
        // 通過函數指針來調用函數
        (*p)(5);
    
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。