一、指针
C++ 的指针很好理解,但是操作起来就有点难度了。而我对指针传参过程的理解也是费了点心思。
主要是 JS 写得脑萎缩了。先用一张图回忆一下指针
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
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;
}运行结果:
三、二级指针
- 其实上面说得都不是啥重点。
- 指向指针的指针被称为二级指针。
- 二级指针图示:
同样我们需要知道这些:
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
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;
}运行结果:
程序说明:
- 这么简单就不用说明了吧。
- 不过要注意的是,因为传的是二级指针,所以函数原型中声明参数的时候需要
int **x
使用两个星号。
来个更直观一点的图示:
让我们来看看各个变量代表的值是什么:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
}
运行结果:
所以得出一个结论:
x == *p == **p;
&x == p == *pp;
&p == pp == *&pp;
注意那个 *&pp
四、关于字符数组
- 很明显这里才是重点。
- 在多数情况下,C++ 将数组名解释为数组第一个元素的地址。
下面这个语句声明一个 double 类型的指针,它指向了 wages 数组的第一个元素
1
2double 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
2int 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》