指针和引用

指针变量

存放地址的变量称为指针变量。指针变量是一种特殊的变量,它不同于一般的变量,一般变量存放的是数据本身,而指针变量存放的是数据的地址。

假设在程序中声明了1个int型的变量a,其值为68。系统为变量a分配的首地址为0X65FDF4H,pa是存放变量a地址的指针变量,即pa中存放的值为0x065FDF4H。

对变量a的访问有两种方式:一是直接按地址0x065FDF4H找到a的存储单元,从而对变量a进行访问;二是按系统为pa分配的地址先找到pa,然后根据pa的值(即变量a地址0x065FDF4H)找到变量a在内存中的存储单元,从而对变量a进行访问。对于前一种访问方式称为直接访问方式,后一种访问方式称为间接访问方式。 如果一个指针变量存放的是某个对象的地址,则称这个指针变量指向该对象。在C++程序设计中,指针变量只有确定了指向才有意义。

引用变量

引用的作用是给一个变量起一个别名,例如有一个变量a,想给他起一个别名b,可以写成:

int a;
int &b = a;

这样声明后,代表a和b就是同一变量单元。&不是代表取地址符号,而是引用声明符,说明定义了b可并没有为它另开辟内存单元,b和a就是同一变量。在声明一个引用型变量时,必须同时为它初始化,即声明它代表哪一个变量,在声明一个变量的引用后,在本函数执行期间,该引用一直与其代表的变量相联系,不能再作为其他变量的别名。

指针和引用的区别

一、引用不可以为空,但指针可以为空

前面也说过了引用是对象的别名,引用为空 —— 对象都不存在,怎么可能有别名!故定义一个引用的时候,必须初始化。而声明指针是可以不指向任何对象,也正是因为这个原因,使用指针之前必须做判空操作,而引用就不必。

二、引用不可以改变指向,而指针可以

虽然引用不可以改变指向,但是可以改变初始化对象的内容。例如就++操作而言,对引用的操作直接反应到所指向的对象,而不是改变指向;而对指针的操作,会使指针指向下一个对象,而不是改变所指对象的内容。

三、sizeof结果不同

“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;

四、引用比指针更安全。

由于不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,因此引用很安全。对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL,所以不安全。const指针虽然不能改变指向,但仍然存在空指针,并且有可能产生野指针(即多个指针指向一块内存,free掉一个指针之后,别的指针就成了野指针)。

指针传递和引用传递

从概念上讲。指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。 而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量)。

在C++中,指针和引用经常用于函数的参数传递,然而,指针传递参数和引用传递参数是有本质上的不同的:

指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。(这里是在说实参指针本身的地址值不会变)

而在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

引用传递和指针传递是不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量,那就得使用指向指针的指针,或者指针引用。

为了进一步加深大家对指针和引用的区别,下面我从编译的角度来阐述它们之间的区别:

程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变其指向的对象(指针变量中的值可以改),而引用对象则不能修改。

示例

#include "stdio.h"
 
void set(int *p)
{
    printf("\n<----------->\n");
    printf("p = %p, &p = %p, *p = %d\n", p, &p, *p);
    *p = 4096;
    printf("p = %p, &p = %p, *p = %d\n", p, &p, *p);
    int a = 2048;
    p = &a;
    printf("p = %p, &p = %p, *p = %d\n", p, &p, *p);
    printf("<----------->\n\n");
}
int main(int argc, char **argv)
{
    int a = 1024;
    int *p = &a;
    printf("p = %p, &p = %p, *p = %d\n", p, &p, *p);
    set(p);
    printf("p = %p, &p = %p, *p = %d\n", p, &p, *p);
}

接下来看看该程序是如何执行的。首先定义一个指针变量p,p的值为变量a的地址,所以第一次打印时结果为:

p = 0x7fff50ae3b74, &p = 0x7fff50ae3b78, *p = 1024

然后将指针变量传递给函数set,此时实际上是值传递,是指针变量p的一个拷贝,作用域为局部。他们具有相同的值,但在内存中的地址不同,所以此时打印结果如下:

p = 0x7fff50ae3b74, &p = 0x7fff50ae3b38, *p = 1024

接下来对指针变量p所指向的内存区域进行操作,此时会影响main函数中的变量p,因为他们指向同一个地址,打印结果如下:

p = 0x7fff50ae3b74, &p = 0x7fff50ae3b38, *p = 4096

接着改变局部指针变量p的指向,此时p的值发生改变,地址不变。

p = 0x7fff50ae3b4c, &p = 0x7fff50ae3b38, *p = 2048

最后回到main函数,p的地址和值没有改变,受影响的是p的值指向的内存区域的值发生改变。所以从整个过程看来,函数里可以对操作p指向的内存区域,但无法改变p的值。如果需要改变,也可以传指针的应用,如:

void set(int *&p)
{
 
}
-- EOF --
发表于: 2015-06-02 11:22
标签: C++ 指针 引用