分享

c++中"指针"终结理解

 昵称10504424 2013-02-19
分类: c++专区 内存相关 用心之作 59人阅读 评论(0) 收藏 举报

目录(?)[+]

  1. 二取地址符号
  2. 三参数的传递方式
  3. 四指针与对象
我们可以对指针这样定义:

通过指针中存放的首地址,应用程序顺利地找到某个变量。就好像我最近认识了一位朋友,他叫我有空去他家坐坐,然后,他留下了地址。某个周末我正闲着,忽然想起这位朋友,于是,我就根据他留的地址去找他,结果,当我来到傻B街230号出租房时,里面走出一个我不认识的人,于是,我问他我这位朋友去哪了,陌生人说,我刚租了这房子,你找的可能是前一位租户吧。

所以,指针所指向的地址,有可能是变量B,也有可能是变量F,或者变量S,指针是房东,可以把房子租给B,C,或F,它可以动态为变量分配内存,也可以把变量销毁(delete),交不起房租就滚蛋(析构函数)。

从上面的故事中,我们看到指针的两个用途:索引内存和分配内存

看看下面这个例子。

你猜猜,它运行后会出现什么?

我们看到了,pint里面存的就是整型100的首地址,因为它是int*,是指向int的指针,所以指针知道,找到首地址后,我只关注从首地址开始,连续的4个字节,后面的我不管了,因为我只知道int有四个字节。上面的例子,我们看到pint的值就是0x10f1968,这就是整型100在内存中的首地址,所以,100所拥有的内存块可能是:

0x10f1968 , 0x10f1969, 0x10f196A, 0x10f196b

总之是连续的内存块来保存这4个字节。

new int(100),表示指针pint在首地址为0x10f1968的内存区域创建了一个4个字节的区域,里面保存的值就是整型100,所以,pint取得的就是100的首地址,而加上*号就不同了,看看上面的例子,*pint的值就是100了。这样一来,我们又得到一个技巧:

利用指针标识符 * 放在指针变量前即可获得指针所指地址中存储的实际值。


我都大家一个很简单的技巧。看看下面两行代码。

int *p = new int(200);

int p = 200;

因为 * 放在类型后或放在变量名前面都是可以的,即int* pint和int *pint是一个道理。这样一来,我们不妨把int *pint 看作int (*pint),将整个*pint看作一个整体,这样看上去是不是和下面的声明很像?

int a = 30;

所以,int* p = new int(30)中,*p返回的值就是int的本值30,而p则只是返回30的首地址。

再看看下面的代码:

现在你可以猜猜它的运行结果是什么。


从上面的代码我们又看到了指针的第三个功能:创建数组

上例中,我创建了有三个元素的数组。在使用完成后,要使用delete来删除已分配的内存,所以,我们的第一个例子中,其实不完善,我们没有做内存清理。

int* pint = new int(100);

/****/

delete pint;

为什么指针可以创建数组?前面我提到过,指针是指向首地址的,那么你想想,我们的数组如果在堆上分配了内存,它们是不是也按一定次序存放在一块连续的内存地址中,整个数组同样构成了一段内存块。

二、取地址符号&

很多书和教程都把这个符号叫引用,但我不喜欢翻译为引用,因为引用不好理解,如果叫取地址符,那我估计你就会明白了,它就是返回一个变量的首地址。

看看例子:

我们不能直接对指针变量赋值,要把变量的地址传给指针,就要用取地址符&。上面的代码中我们声明了int类型的变量a,值为50,通过&符号把变量a的地址存到p指针中,这样,p指向的就是变量a的首地址了,故:a的值的50,而p的值就应该是a的地址。

那么,这样做有啥好处呢?我们把上面的例子再扩展一下,变成这样:

先预览一下结果。



不知道大家在这个例子中发现了什么?

我们定义了变量a,值为50,然后指针p指向了a的首地址,但注意,后面我只是改变了p所指向的那块内存中的值,我并没有修改a的值,但是,你看看最后a的值也变为了250,想一想,这是为什么?

三、参数的传递方式

很多书,包括一些计算机二级的考试内容,那些傻S砖家只是想出一大堆与指针相关的莫名其妙的考题,但很让人找不到指针在实际应用到底能干什么,我估计那些砖家自己也不知识吧。所以,我们的考试最大的失败,就是让学生不知识学了有什么用。

上面介绍了指针可以存首地址,可以分配内存,可以创建数组,还说了取地址符&,那么,这些东西有什么用呢?你肯定会问,我直接声明一个变量也是要占用内存的,那我为什么要吃饱了没事干还要用指针来存放首地址呢?

好,我先不回答,我们再说说函数的参数传递。看看下面这样的例子。


我们希望,在调用函数fn后,变量a的值会加上100,现在我们运行一下,看看结果:

我们可能会很失望,为什么会这样?我明明是把20传进了fn函数的,为什么a的值还是不变呢?不用急,我们再把代码改一下:

运行结果如下:


看到了吗?变量a和fn函数的参数x的地址是不一样的,这意味着什么呢?这说明,变量a的值虽然传给了参数x,但实际上是声明了一个新变量x,而x的值为20罢了,最后加上100,x的中的值是120,但a的值没有变,因为在函数内被+100的根本不是变量a,而是变量x(参数)。

这样,就解释了为什么么函数调用后a的值仍然不变的原因。

那么,如何让函数调用后对变量a作修改,让它变成120呢?这里有两个方法:

(1)指针法。把参数改为指针类型。

这里要注意,把变量传给指针类型的参数,要使用取地址符&。

那么,这次运行正确吗?


好了,终于看到想要的结果了。

(2)引用法,就是把参数改为&传递的。

可以看到,这样的运行结果也是正确的。


四、指针与对象

不管是类还是结构(其实结构是一种特殊的类),它们在创建时还是要创建内存的,但是,创建类的对象也有两种方式,直接声明和用指针来分配新实例。


我们来看看这个例子,首先定义了一个类Test,在类的构造函数中输出对象被创建的个息,在发生析构时输出对象被销毁。

接着, 我们分别在两个函数中创建Test类的对象,因为对象是在函数内部定义的,根据其生命周期原理,在函数返回时,对象会释放,在内存中的数据会被销毁。理论上是这样的,那么,程序实际运行后会如何呢?

这时候我们发现一个有趣的现象,在第一个函数直接以变量形式创建的对象在函数执行完后被销毁,因为析构函数被调用;可是,我们看到第二个函数中并没有发生这样的事,用指针创建的对象,在函数完成时居然没有调用析构函数。

直接创建对象,变量直接与类实例关联,这样一来,当变量的生命周期结束时,自然会被处理掉,而用指针创建的实例,指针变量本身并不存储该实例的数据,它仅仅是存了对象实例的首地址罢了,指针并没有与实例直接有联系,所以,在第二个函数执行完后,被销毁的是Test*,而不是Test的对象,仅仅是保存首地址的指针被释放了而已,而Test对象依然存在于内存中,因此,在第二个函数完成后,Test的析构函数不会调用,因为它还没死呢。

那么,如何让第二个函数在返回时也销毁对象实例呢?还记得吗,我前文中提过。对,用delete.。


现在看看,是不是在两个函数返回时,都能够销毁对象。

现在你明白了吧?

由此,可以得出一个结论:指针只负责为对象分配和清理内存,并不与内存中的对象实例有直接关系。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多