引用,其本质就是指针,将它用在一些特别的场合,会比指针更简洁,更方便。具体说来,指针多用于动态内存管理和对数组的操作等,C风格的代码接收和返回指针;引用则往往用在接收和返回类类型的名字空间域函数或类域函数,以避免类对象的复制开销。但是请注意,引用毕竟不完全等同于指针,它们有一些差异:
  1、引用必须初始化,指针则不然。
int *pi;//可以
int &ri;//不行,未初始化
int i;int &ri=i;//可以
  2、引用不能为空引用,指针则不然。
int *pi=NULL;//可以
int *pi=NULL;int &ri=*pi;//会引发问题。
  3、引用在初始化后,不能再引用其他对象,指针则不然。
void f(int &ri)
{
    int i=3;
    ri=4;//改变所引用的实参
    ri=&i;//错误,类型不符
    ri=i;//ri并未转为引用局部变量i
}
  虽然每次调用f时,都有可能会使形参ri,引用不同的实参,但是在f函数内,无法在调用处,引用初始化后,再改变其所引用的对象。显然包括引用在内,在很多时候,初始化与赋值运算符的语义有很大区别。
  4、引用在必要时实现为指针,但是常规语法取不到这个内部指针的地址。
int i=3;int *pi=&i;//i的值为3,地址为&i;pi的值为&i,地址为&pi
int i=3;int &ri=i;//i的值为3,地址为&i;ri的值为3,地址为&i,初始化后ri和i基本为同义语。
  实际上,当引用ri初始化为i后,语言实现可能只将ri优化处理成i的别名,而不将其实现为指针;也可能使用一个内部指针来完成工作。比如第3条中的f函数的形参ri,每次具体调用函数时,都可能会传入不同地址的实参。因为设计引用就是要避免对象复制的,因此它会压入堆栈一个指向被引用类型的指针。有可能这样来实现:
int i=3;
int &ri=i;//可实现为int *temp_pi=&i;以后每次源程序中使用ri时,就会被相应替换成*temp_pi
  即使实现为这样的内部指针,也无法用正常的语法来取得它的地址。因为按照上边的分析,初始化后,取地址操作&ri将等价于&*temp_pi。那么这对用户来说,有什么会造成影响的不同吗?来看看一种不太常见的情况:可变个数参数表的省略号...前一个参数,如果是引用类型,将无法正常工作;如果是指针类型或简单类型等,则不会有问题。如有以下声明:
void f(int i,...);
  则在实现代码中,可以使用C标准库的stdarg.h来取得可变个数部分的参数。有兴趣的话可以去看看stdarg.h的具体代码,它是用宏来完成的,针对不同的环境,采用了相应的手段。我用一段功能类似的伪代码来说明一下:

void f(int i,...)//此函数用i来表示可变个数参数的个数
{
    int ix;
    void *base=&i;
    for(ix=0;ix<i;ix++)
    {
        base+=sizeof(int);//或者依实现为base-=sizeof(int)
        cout<<"第"<<ix+1<<"个int参数的值为:";
        cout<<*((int *)base)<<endl;
    }
}
 
  如果有调用f(5,1,2,3,4,5);则5,4,3,2,1,5将从右到左依次被压入堆栈,然后调用f函数。在f函数内部来看,那几个未命名的可变参数,是每个占一个sizeof(int)大小,一个接一个挨着放在堆栈上的,并且局部变量i指向了堆栈顶的位置。这时可以用相对于堆栈顶位置的位移量来取得每一个局部变量。当函数返回时,由调用点来清理堆栈。这样,f(5,1,2,3,4,5);和f(3,10,11,12);均可正确调用和返回,因为每处调用均知道自己用了几个参数,所以它可以在函数返回后,正确地在堆栈上弹出参数(其缺点就是每处调用都要自己清理,这样方式的程序长度,要比清理只出现在被调函数返回处的方式,要长一些)。这就是所谓的cdecl调用约定。然而,如果...前的参数为引用类型,如:
void f(const int &ri,...);//如果使用非左值进行调用,引用必须为const
调用f(5,1,2,3,4,5)将会发生什么呢?
因为int &ri是个引用参数,所以实际上会是:
push 5;
push 4;
push 3;
push 2;
push 1;
const int temp_i=5;
const int *temp_pi=&temp_i;
push temp_pi;
  也就是说,和5个可变个数参数并排挨着的实参ri,在实际调用时,会被处理成向堆栈压进一个const int *类型的地址值。那么在f函数内的代码中,void *base=&ri;又将指向何处呢?按前面的分析,&ri会被处理成&*temp_pi,其结果就是temp_pi的值,temp_i的地址。试图取回和1、2、3、4、5挨着的这个内部指针的地址,其结果却是内部指针temp_pi的值,temp_i的地址,它并不在可变个数参数的1、2、3、4、5中1的旁边。所以调用将无法正常取得...部分的参数。

发表于 @ 2009年03月01日 14:19:00 | 评论( 0 ) | 编辑| 举报| 收藏

新一篇:精解C++的switch语句

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/myliupp/archive/2009/03/01/3947023.aspx

C++中引用和指针的不同相关推荐

  1. C++中引用与指针的区别(详细介绍)

    转载:http://www.cnblogs.com/tracylee/archive/2012/12/04/2801519.html C++中的引用与指针的区别 指向不同类型的指针的区别在于指针类型可 ...

  2. Java 基础 | Java 中引用与指针的关系

    前言:关键字包含 #指针,java 引用,空指针,地址访问,引用类型,在 Java 编程语言中,程序员不需要担心程序的内存使用.Java 语言的自动垃圾收集器会不时地清理那些变成垃圾的对象. 如果垃圾 ...

  3. C++中引用,指针,指针的引用,指针的指针

    定义一个指针的三种写法都对:1. int * p;  2. int* p;  3. int *p; 习惯不同而已 定义一个函数指针的三种写法都对:1. int *p(); 2. int * p();  ...

  4. c++中引用及指针详解

    这里写目录标题 1.指针 1.1.什么是指针 指针的本质 指针与地址 程序中如何声明指针以及如何使用运算符&和* 1.2.指针有什么作用 指针与函数参数 2.引用 2.1.什么是引用 2.2. ...

  5. 浅谈C++中引用和指针的区别

    之前我们介绍了什么是引用,错过的小伙伴们可以戳这里 ↓ https://blog.csdn.net/Sun_Life_/article/details/89304920 既然引用底层是用指针形式实现的 ...

  6. c++中引用和指针的区别

    1.指针是一个实体,需要分配内存空间.引用只是变量的别名, 不需要分配内存空间. 2.引用在定义的时候必须进行初始化,并且不能够改变.指针在 定义的时候不一定要初始化,并且指向的空间可变.引用的初始 ...

  7. c++中的引用和python中的引用_【总结】C++、C#、Java、Javascript、Python中引用的区别...

    首先分两大阵营:C++中引用是一块阵营, C#.Java.Javascript.Python中引用是另一块阵营. 之所以这样分是因为同一阵营中引用使用方法基本一样. C++引用本质是个常量指针,而其他 ...

  8. c++中的引用和指针

    引用和指针 1.引用: 2.指针: 区别: 1.引用: C++是 C 语言的继承,它可进行过程化程序设计,又可以进行以抽象数据类型为特点的基于 对象的程序设计,还可以进行以继承和多态为特点的面向对象的 ...

  9. C++中引用传递与指针传递区别(进一步整理)

    C++中引用传递与指针传递区别(进一步整理) 博客分类: C/C++ CC++C#J#  从概念上讲.指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变 ...

最新文章

  1. 目标跟踪之卡尔曼滤波---理解Kalman滤波的使用
  2. jmeter中文_JMeter安装配置
  3. 正则表达式三 :编译
  4. Docker镜像加速器配置
  5. 卷积的C语言实现的MFC版本
  6. Java Thread pool的学习笔记
  7. 2021 NOI游记
  8. 线程池默认多少个线程_我需要多少个线程?
  9. 前端开发者必备的20个文档和在线工具
  10. a start job is running for延迟90s的解决办法
  11. [linux/ unix] 查看占用端口的 进程ID 的区别
  12. equals方法 和 ==的区别
  13. matlab求带参数二重定积分,matlab二重定积分
  14. 大学应用计算机应用基础课程介绍,大学计算机应用基础(Windows 7+Office 2010)(刘艳)...
  15. ESP32 INMP441麦克风驱动
  16. 软件工程(1)软件开发方法
  17. nginx小技巧-动态域名(微信,小程序80端口)
  18. WordPress 5.2中的致命错误恢复模式
  19. 服务器数据恢复的两种方法
  20. 【云原生-K8s】cka认证2022年12月最新考题及指南

热门文章

  1. javascript高精度计算解决方案
  2. Java IO流--练习2
  3. Oracle Database_buffer_cache大小的设置及依据
  4. 什么是缓存里的脏数据.
  5. yar java_Yar 的传输协议学习以及 Java 版本的实现
  6. python贪婪匹配顺序_Python正则表达式:贪婪模式返回多个空匹配
  7. imdb导mysql_keras如何导入本地下载的imdb数据集?
  8. SpringCloud 应用在 Kubernetes 上的最佳实践 — 线上发布(可灰度)
  9. mac怎么合并两个容器_PDF怎样合并?在Mac上合并PDF文件的最佳方法
  10. xamarin textview 滚动_Apple Music有原生滚动歌词了!喜大普奔!