关于指针的三个容易弄混淆的概念及发散~~~
对于指针来讲,有3个比较容易混淆的概念:
- 指针地址
- 指针保存的地址
- 指针所保存的地址的值
在这里,我们分别来看看他们的定义:
- 指针地址:指针自身的地址,即内存中用于存放指针变量的内存地址
- 指针保存的地址:指针所保存的变量在内存中的地址,通俗讲就是指针所指向的对象的内存地址
- 指针所保存的地址的值:指针所指对象的数值。
下面,我们用一个具体的例子来看看上面仨个东东的不同~~~
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <iostream> using namespace std; int main() { int i; int *p=0; //初始化指针p cout<< "i的地址为:" <<&i<<endl; cout<< "p的值为:" <<p<<endl; cout<< "p的内存地址为:" <<&p<<endl; i=3; cout<< "i的地址为:" <<&i<<endl; cout<< "i的值为:" <<i<<endl; p=&i; cout<< "p的值为:" <<p<<endl; cout<< "p的内存地址为:" <<&p<<endl; cout<< "p所指向的值为:" <<*p<<endl; *p=5; cout<< "p所指向的变成:" <<*p<<endl; cout<< "此时i的值变成:" <<i<<endl; return 0; } |
程序分析:
在程序的5,6行,我们定义了一个变量i,和一个指针*p,同时将这个指针初始化为0①。然后我们采用&运算符来得到整型变量i的地址,并输出之。后面两句关于p的值和内存就是初始化该指针时系统自动为其分配的地址和值(假设p的内存地址为B);
在程序的第10行中,我们将3赋与i,这时i的地址并没有发生变化。比如说如果原来i的地址为A,这是i的地址仍然为A,只不过该地址A中间的值已经成为了3。那么后面两句的输出就显而易见了,i的地址输出应该为A,i的值为3;
在程序的第13行,我们利用取地址运算符&将i的地址赋给了p,那么这个时候,p中的值应该就是i的地址,即为A。而p的内存地址并没有发生改变,仍然为B。在这里,我们就可以看到,对于指针p来讲:
- p的值即为该指针p所保存的整型变量i的地址A
- 指针p的地址还是原来初始化时候的内存地址B
- 而利用间接运算符*访问并读取到得i得值即为i的值,也就是3
所以说在这里,指针地址就是B,指针保存的地址就是A,指针所保存的地址的值就是3②。
在程序的17行,我们利用了指针可以随意修改指向对象的值得功能,将i中的值修改成了5,那么相应地输出p和i的值就应该是5了③。
综上所述,该程序的输出为
从这个输出上我们就可以明显看出上面三个概念本质上的区别了。
注释:
① 我们在最开始定义一个指针的时候,最应该做的就是先将这个指针变量进行初始化,以防止它变成“野指针”。因为一个野指针可以指向任何内存地址,并且可以对该地址中的数值进行随意地修改或删除,这样很可能对整个程序造成不良后果!所以在定义指针的时候,一定要先将其初始化成0,即上面函数中的“int *p=0”。这样就会把这个指针的值限定成为0,从而不会引发野指针的问题。
② 们在这里分析一下造成这种原因的机理。(注:在这里的矩形分别代表了整型变量i和指针变量p。矩形框中间的大矩形代表了该变量的数值,而小矩形代表了该变量的地址。)我们可以看到,如蓝色的笔所示,i的地址为A,p的地址为B。现在有绿色的笔标出了程序第13行中的语句,将i的地址赋给p,那么如右图所示,p里面的值就成为了i的地址A。在看看红笔。我们采用了间接运算符*访问i的值。正如红线所标示的一样,首先,它从p的值中找到i的地址为A,然后在从i的地址中读取到i的值为3。这样,就完成了利用指针p读取整型变量i的值了。
③ 这也是为什么我们在程序之初需要对指针p进行初始化的原因了。从上面可以看出,只要*p修改了i的值,那么连i自己也会把自己的值从3修改到了5。这就好比是我通过地址找到你家,然后我在你家里面把你家的电视机拿了,那么你家就真没有电视机了~~~虽然我举得这个例子不是很恰当,但是这个就是指针能干的事情啊,囧啊~~~
另外,程序第13行和程序第17行的位置千万不要搞颠倒了哈。这里面是有逻辑顺序的。首先我们需要向指针p说明变量i的地址(p=&i),之后我们才能对其进行操作(*p=5)。这里绝对不可能是先操作了再找地址,这肯定是不符合逻辑的。如果程序这样写,在编译的时候不会犯错,但是运行的时候错误就出来了,如下:
好了,收工~~~