C++中引用、指针,传值的联系和区别

来源:互联网 发布:java程序员认证培训 编辑:程序博客网 时间:2024/04/30 18:15

CPU型号:Intel(R) Core(TM) i5-2450M 

系统:windows 10

IDE:Microsoft Visual C++ 6.0(下文中简称VC)

制图软件:Photoshop cs5


首先介绍下概念:

原变量:原变量即实参,引用、指针、传值都是指一个函数对应的形参

引用:即pass by reference,引用是某一变量的别名,即引用和被引用的变量的地址为同一个地址,对引用的操作与对变量的操作完全一样(要强调的一点,引用是C++中的概念,在.c中编译是编译不过的

指针:指针的大小由当前CPU的寻址位数决定的(因此常见为32位即4个字节),指针本身是一个地址,地址里面的值为一个指向内存的存储单元,即一个地址

传值:即pass by value,传值是最简单的传递值的方式,它会在被调用函数中生成一个变量副本,作用范围为该函数体


共同点:三者可以作为传递值来使用,实际上也都是一个地址

不同点:引用和原变量为同一个地址;指针多声明了一个变量(指针本身,局部变量),但是指针的值为原变量;传值也多声明一个变量(传值本身,局部变量)


接下来用代码来说明三者作为函数参数传递时的联系和区别:

#include <iostream>using namespace std;void PassByValue(short value){    ++value;    cout<<"PassByValue:Addr is 0x"<<&value<<",sizeof(value)="<<sizeof(value)<<",value is "<<value<<endl<<endl;}void PassByPointer(short *value){    ++*value;    cout<<"PassByPointer:Addr is 0x"<<&value<<",sizeof(value)="<<sizeof(value)<<",value is "<<value<<",*value="<<*value<<endl<<endl;}void PassByReference(short &value){    value++;    cout<<"PassByReference:Addr is 0x"<<&value<<",sizeof(value)="<<sizeof(value)<<",value is "<<value<<endl<<endl;}void main(void){    short n = 5;    cout<<"Main:Addr is 0x"<<&n<<",value is "<<n<<endl<<endl;    PassByValue(n);    cout<<"Main(After PassByValue):Addr is 0x"<<&n<<",value is "<<n<<endl<<endl<<endl;    PassByPointer(&n);    cout<<"Main(After PassByPointer):Addr is 0x"<<&n<<",value is "<<n<<endl<<endl<<endl;    PassByReference(n);    cout<<"Main(After PassByReference):Addr is 0x"<<&n<<",value is "<<n<<endl<<endl<<endl;}

执行结果如下:



从红色框部分可以看出,原变量n的地址为0x0019FF3C,值为5。

经过传值调用后,传值的地址为0x0019FEEC,不再是原变量的地址,对传值的值进行改变,并不会对原变量产生影响。

经过指针调用后,指针的地址为0x0019FEEC,其值为0x0019FF3C,值为原变量的地址,而为了改变原变量的值,必须通过加取值符*才能对原变量进行改变。

经过引用调用后,引用的地址为0x0018FF3C,跟原变量为同一个地址,改变引用的值当然可以改变原变量的值。

(传值和指针的地址一样的原因是因为传值和指针都是局部变量,并且是它们的内存空间是由系统中的栈自行申请及释放的,也就是超出作用域就被释放。虽然它们地址相同,但是意义不同,即所占有该地址的时间不同)


由此可以做出结论:

(1)在作为函数参数调用中,引用即省空间又省时间(声明和定义变量需耗费额外的内存和CPU计算时间)

(2)引用本质上就是原变量,只是名字不一样而已,可以直接使用;指针的值才是原变量的地址,该改变原变量的值必须通过取值符*后才能改变

(3)对形参进行sizeof()操作,就会发现传值和引用的大小就是原变量的类型大小,而指针的大小就是指针的大小,不是原变量的类型大小(代码中将原变量的类型定义为short,就是为了和指针的大小区分开来)


如要要做进一步的延伸,就可以讨论指针的特性和地址(引用本质上就是一个地址)的区别:

(4)存在多级指针,但不存在多级引用(多级引用毫无意义,因为引用本身就是个地址,对地址取地址毫无意义)

(5)指针可以指向任何地址,但是引用必须指向用户可使用的内存地址(换句话说就是指针灵活,但是破坏性也大,而引用只能引用已经定义的除了指针变量,数组外的变量,且不能直接对地址引用)


通俗点讲,指针可以指向任何地址,包括NULL,(其实NULL的定义为#define NULL 0),而一般情况下我们把指向NULL的指针理解为空指针,实际上这并不是安全的做法!指针强大但是也很危险!

#include <iostream>using namespace std;void main(void){int *p=NULL;int *q=(int *)0x00000011;//指向绝对地址0x00000011,需强制转换//cout<<*p<<endl;cout<<*q<<endl;}
编译时不会出错,运行时就完蛋了。


而引用只能指向已定义的非指针、数组变量的变量,且不能直接对地址进行引用(NULL也算一个地址)。

#include <iostream>using namespace std;void main(void){int a;int *p = &a;//int &q = a;int &q = p;//对指针引用,报错// int &q = 0x00000011;//对地址引用,报错}

(6)指针声明时可以不用初始化,引用声明时必须初始化

#include <iostream>using namespace std;void main(void){int *p;int &q;}
运行会报错:

error C2530: 'q' : references must be initialized


(7)指针的值可以随意改变,引用一旦定义就无法改变其引用的地址

#include <iostream>using namespace std;void main(void){    int a = 1, b = 2 ;    int *p = &a;    int &q = a;    cout<<"a addr:"<<&a<<",b addr:"<<&b<<endl<<endl;    cout<<"p addr :"<<p<<",value:"<<*p<<endl;    p = &b;    cout<<"p addr :"<<p<<",value:"<<*p<<endl<<endl;    cout<<"q addr :"<<&q<<",value:"<<q<<endl;    q = b;    cout<<"q addr :"<<&q<<",value:"<<q<<endl;}


由结果可知,指针重新指向后可以改变其地址,但是引用就无法做到,地址无法改变。这里可以大胆猜想,引用只有在声明定义的时候才能决定它的地址,之后的操作都只是赋值。



0 0