C++ 中的指针

一、指针

  • C++ 的指针很好理解,但是操作起来就有点难度了。而我对指针传参过程的理解也是费了点心思。主要是 JS 写得脑萎缩了。

  • 先用一张图回忆一下指针

C++Pointer
P 是一个 int 型的指针,指向内存 Ox6efff4

  • 与指针配套食用的是取地址运算符&和解除引用运算符*,于是我们就需要知道三个属性:
    &p是指针的变量地址
    p指针指向的地址(即存储数据的地址)
    *p指针指向的地址的值
    

二、传参过程中的指针

  • C++ 中参数的传递有三种,值传递引用传递以及指针传递
  • 值传递:形参是实参的拷贝,函数内部对形参的修改不能改变外部实参的值,换言之,传递的值不会被修改。
  • 引用传递:形参相当于是实参的别名,函数内部对形参的修改相当于对外部实参的修改,即传递的值会被修改。
  • 指针传递

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include<iostream>
    using namespace std;
    void function(int *x){
    cout << "x: " << x << endl;
    cout << "*x: " << *x << endl;
    *x = 2;
    }

    int main(){
    int a = 1;
    int *p = &a;

    cout << "a: " << a << endl;
    cout << "&a: " << &a << endl;
    cout << "p: " << p << endl;

    function(p);
    cout << "a: " << a << endl;
    return 0;
    }

    运行结果:
    result

三、二级指针

  • 其实上面说得都不是啥重点。
  • 指向指针的指针被称为二级指针。
  • 二级指针图示:

pointer
同样我们需要知道这些:

  • pp是二级指针保存的地址
  • &pp是二级指针的地址
  • *pp是二级指针所指向的地址里面保存的数据(其实也是一个地址,相当于p
  • **pp是二级指针所指向的地址里面保存的地址(数据),该地址里面保存的数据(相当于*p

    二级指针作为参数传递:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28

    #include<iostream>
    using namespace std;

    void function(int **x){
    cout << "x: " << x << endl;
    cout << "*x: " << *x << endl;
    cout << "**x: " << **x << endl;
    **x = 2;
    }

    int main(){
    int a = 1;
    int *p = &a;
    int **pp = &p;

    cout << "a: " << a << endl;
    cout << "&a: " << &a << endl;
    cout << "p: " << p << endl;

    cout << "pp: " << pp << endl;
    cout << "*pp: " << *pp << endl;
    cout << "**pp: " << **pp << endl;

    function(pp);
    cout << "a: " << a << endl;
    return 0;
    }

    运行结果:
    point2

    程序说明:

    • 这么简单就不用说明了吧。
    • 不过要注意的是,因为传的是二级指针,所以函数原型中声明参数的时候需要int **x使用两个星号。
  • 来个更直观一点的图示:
    截图20180725234511.png
    让我们来看看各个变量代表的值是什么:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #include<iostream>
    using namespace std;

    int main(){
    int x = 3;
    int *p = &x;
    int **pp = &p;

    cout << "x: " << x << endl;
    cout << "&x: " << &x << endl;

    cout << "p: " << p << endl;
    cout << "*p: " << *p << endl;
    cout << "&p: " << &p << endl;

    cout << "pp: " << pp << endl;
    cout << "*pp: " << *pp << endl;
    cout << "**pp: " << **pp << endl;
    cout << "&pp: " << &pp << endl;
    cout << "*&pp: " << *&pp << endl;

    return 0;
    }

运行结果:

20180725234912.png

所以得出一个结论:

x == *p == **p;
&x == p == *pp;
&p == pp == *&pp;

注意那个 *&pp

四、关于字符数组

  • 很明显这里才是重点
  • 在多数情况下,C++ 将数组名解释为数组第一个元素的地址。
  • 下面这个语句声明一个 double 类型的指针,它指向了 wages 数组的第一个元素

    1
    2
    double wages[3] = {100.0, 200.0, 300.0};
    double *p = wages; // wages = &wages[0]
  • 然后我们就可以进行指针变量加一的操作,该操作增加的值等于指向的类型占用的字节数。

    *(p+1) == wages[1] // true

  • 可以将一维数组视为一个指针,使用方括号就是等同于解除引用,同理可得,二维数组可视为一个二级指针(二维数组 != 二级指针 )

    tacos[0] == *tacos; // true
    tacos[1] == *(tacos + 1); //true

  • 有一个思考:

    char a = '$'; // 这是一个字符
    char *p = 'abcdefg' // 这是什么?可以理解为字符串吗?
    

五、二维数组与指针

  • 当我们对二维数组执行像一维数组一样的操作的时候

    1
    2
    int a[2][3] = { {1, 2, 3}, {4, 5, 6} };
    int **p = a;

    编译器会报错:error: cannot convert 'int (*)[3]' to 'int**' in initialization
    原因其实很简单,二维数组的数组名已经不是第一个元素的地址了,而是数组指针,它指向的是第一行数组的首地址,也称为行指针。因此它应该是这样的:

    1
    int (*p)[3] = a;

    该定义的含义数组指针p,指向的是一个数组元素为int类型并且数组元素的个数为3的一个数组指针
    若要取其中第二行第三列(从 1 开始计数)的元素:

    *(*(p+1)+2)

  • 关于二维数组的传参(补充)
    因为二维数组名是数组指针,所以我们在将数组名作为参数传参的时候也是传的数组指针,这时候在函数原型声明的时候可以将数组指针转化为void *

六、使用 const 修饰的指针

  • 第一种:const * p

    int gorp = 16;
    int chips = 12;
    const int * p = &gorp;
    
    *p = 20; // error: 不允许修改指针所指向的地址里面的内容
    p = &chips; // OK:允许修改指针的指向(可以修改 p 的值)
    
  • 第二种:* const p

    int gorp = 16;
    int chips = 12;
    int * const p = &gorp;
    
    *p = 20; // OK: 允许修改指针所指向的地址里面的内容
    p = &chips; // error: 不允许修改指针的指向(不可以修改 p 的值)
    
  • 第三种:const * const p

    既不允许修改 p 指针所指向的地址里面的内容,也不允许修改指针的指向
    
  • 思考:
    下面代码是否允许?

    const int gorp = 16;
    int *p = &gorp;
    

参考:
https://blog.csdn.net/qq_32483145/article/details/52901230
https://blog.csdn.net/zhouxuguang236/article/details/12256321
《C++ Primer Plus》