C 中的指针与引用详细解读
C++引用的作用和用法

C++ 引用的作用和用法引用的好处之一就是在函数调用时在内存中不会生成副本引用总结(1)在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不如意的问题。
(2)用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,且通过const的使用,保证了引用传递的安全性。
(3)引用与指针的区别是,指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。
程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
(4)使用引用的时机。
流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。
引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。
引用的声明方法:类型标识符&引用名=目标变量名;【例1】:int a; int &ra=a; //定义引用ra,它是变量a的引用,即别名(1)&在此不是求地址运算,而是起标识作用。
(2)类型标识符是指目标变量的类型。
(3)声明引用时,必须同时对其进行初始化。
(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
ra=1; 等价于a=1;(5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。
故:对引用求地址,就是对目标变量求地址。
&ra与&a相等。
(6)不能建立数组的引用。
因为数组是一个由若干个元素所成的集合,所以无法建立一个数组的别名。
(7)不能建立引用的引用,不能建立指向引用的指针。
因为引用不是一种数据类型!!所以没有引用的引用,没有引用的指针。
例如:int n;int &&r=n;//错误,编译系统把"int &"看成一体,把"&r"看成一体,即建立了引用的引用,引用的对象应当是某种数据类型的变量int &*p=n;//错误,编译系统把"int &"看成一体,把" *p "看成一体,即建立了指向引用的指针,指针只能指向某种数据类型的变量(8)值得一提的是,可以建立指针的引用例如:int *p;int *&q=p;//正确,编译系统把" int * "看成一体,把"&q"看成一体,即建立指针p 的引用,亦即给指针p起别名q。
C++中引用传递与指针传递的区别(面试常见)

C++中引⽤传递与指针传递的区别(⾯试常见)最近Garena⾯试的过程中,⾯试官提了⼀个问题,C++中引⽤传递和指针传递的区别?根据⾃⼰的经验,联想到了swap函数,只知道既可以⽤引⽤来实现,⼜可以⽤指针传递来实现,⾄于⼆者有何区别,⾃⼰还真没有考虑过。
痛定思痛,受虐之后,赶紧弥补⾃⼰的知识漏洞。
通过在⽹上搜集资料,⾃⼰也整理了⼀下。
精简版:指针:变量,独⽴,可变,可空,替⾝,⽆类型检查;引⽤:别名,依赖,不变,⾮空,本体,有类型检查;完整版:1. 概念 指针从本质上讲是⼀个变量,变量的值是另⼀个变量的地址,指针在逻辑上是独⽴的,它可以被改变的,包括指针变量的值(所指向的地址)和指针变量的值对应的内存中的数据(所指向地址中所存放的数据)。
引⽤从本质上讲是⼀个别名,是另⼀个变量的同义词,它在逻辑上不是独⽴的,它的存在具有依附性,所以引⽤必须在⼀开始就被初始化(先有这个变量,这个实物,这个实物才能有别名),⽽且其引⽤的对象在其整个⽣命周期中不能被改变,即⾃始⾄终只能依附于同⼀个变量(初始化的时候代表的是谁的别名,就⼀直是谁的别名,不能变)。
2. C++中的指针参数传递和引⽤参数传递 指针参数传递本质上是值传递,它所传递的是⼀个地址值。
值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,会在栈中开辟内存空间以存放由主调函数传递进来的实参值,从⽽形成了实参的⼀个副本(替⾝)。
值传递的特点是,被调函数对形式参数的任何操作都是作为局部变量进⾏的,不会影响主调函数的实参变量的值(形参指针变了,实参指针不会变)。
引⽤参数传递过程中,被调函数的形式参数也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。
被调函数对形参(本体)的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量(根据别名找到主调函数中的本体)。
因此,被调函数对形参的任何操作都会影响主调函数中的实参变量。
深入分析C语言中结构体指针的定义与引用详解

深入分析C语言中结构体指针的定义与引用详解在C语言中,结构体是一种用户自定义的数据类型,由多个不同类型的数据组成一个整体。
结构体指针则是指向结构体类型变量的指针,可以用来间接访问和操作结构体的成员。
要定义一个结构体指针,首先需要定义一个结构体类型。
结构体类型的定义通常放在函数外部,以便在整个程序中都可以使用该类型。
结构体类型的定义格式如下:```cstruct 结构体名数据类型成员1;数据类型成员2;//其他成员};```例如,我们定义一个表示学生的结构体类型`student`,包含学生的姓名和年龄:```cstruct studentchar name[20];int age;};```声明一个结构体指针时,需要使用结构体类型名并在后面加一个`*`表示该指针变量指向结构体类型的对象。
例如,我们声明一个指向`student`类型的结构体指针`p`:```cstruct student *p;```结构体指针必须指向实际存在的结构体变量,可以通过`malloc`函数动态分配内存空间来创建一个结构体对象,并将其地址赋给指针变量。
例如,我们创建一个`student`类型的对象并将其地址赋给指针变量`p`:```cp = (struct student*)malloc(sizeof(struct student));```通过`sizeof(struct student)`可以获取`student`类型的大小,`malloc`函数会根据指定的大小分配相应的内存空间,并返回分配的内存地址。
通过结构体指针,可以使用箭头运算符`->`来访问结构体的成员。
例如,我们可以通过指针`p`访问学生的姓名和年龄:```cstrcpy(p->name, "John");p->age = 18;```在上述代码中,`strcpy`函数用于将字符串`"John"`复制到`p->name`所指向的内存空间中,`p->age`则直接赋值为`18`。
C指针详解(经典,非常详细)

总结课:让你不再害怕指针指针所具有的四个要素:指针的类型,指针所指向的类型,指针指向的内存区,指针自身占据的内存。
0前言:复杂类型说明要了解指针,多多少少会出现一些比较复杂的类型,所以我先介绍一下如何完全理解一个复杂类型,要理解复杂类型其实很简单,一个类型里会出现很多运算符,他们也像普通的表达式一样,有优先级,其优先级和运算优先级一样,所以我总结了一下其原则:从变量名处起,根据运算符优先级结合,一步一步分析.下面让我们先从简单的类型开始慢慢分析吧:int p;//这是一个普通的整型变量int*p;//首先从P处开始,先与*结合,所以说明P是一//个指针,然后再与int结合,说明指针所指向//的内容的类型为int型.所以P是一个返回整//型数据的指针int p[3];//首先从P处开始,先与[]结合,说明P是一个数//组,然后与int结合,说明数组里的元素是整//型的,所以P是一个由整型数据组成的数组int*p[3];//首先从P处开始,先与[]结合,因为其优先级//比*高,所以P是一个数组,然后再与*结合,说明//数组里的元素是指针类型,然后再与int结合,//说明指针所指向的内容的类型是整型的,所以//P是一个由返回整型数据的指针所组成的数组int(*p)[3];//首先从P处开始,先与*结合,说明P是一个指针//然后再与[]结合(与"()"这步可以忽略,只是为//了改变优先级),说明指针所指向的内容是一个//数组,然后再与int 结合,说明数组里的元素是//整型的.所以P 是一个指向由整型数据组成的数//组的指针int**p;//首先从P开始,先与*结合,说是P是一个指针,然//后再与*结合,说明指针所指向的元素是指针,然//后再与int 结合,说明该指针所指向的元素是整//型数据.由于二级指针以及更高级的指针极少用//在复杂的类型中,所以后面更复杂的类型我们就//不考虑多级指针了,最多只考虑一级指针.int p(int);//从P处起,先与()结合,说明P是一个函数,然后进入//()里分析,说明该函数有一个整型变量的参数//然后再与外面的int结合,说明函数的返回值是//一个整型数据int(*p)(int);//从P处开始,先与指针结合,说明P是一个指针,然后与//()结合,说明指针指向的是一个函数,然后再与()里的//int结合,说明函数有一个int型的参数,再与最外层的//int结合,说明函数的返回类型是整型,所以P是一个指//向有一个整型参数且返回类型为整型的函数的指针int*(*p(int))[3];//可以先跳过,不看这个类型,过于复杂//从P开始,先与()结合,说明P是一个函数,然后进//入()里面,与int结合,说明函数有一个整型变量//参数,然后再与外面的*结合,说明函数返回的是//一个指针,,然后到最外面一层,先与[]结合,说明//返回的指针指向的是一个数组,然后再与*结合,说//明数组里的元素是指针,然后再与int结合,说明指//针指向的内容是整型数据.所以P是一个参数为一个//整数据且返回一个指向由整型指针变量组成的数组//的指针变量的函数.说到这里也就差不多了,我们的任务也就这么多,理解了这几个类型,其它的类型对我们来说也是小菜了,不过我们一般不会用太复杂的类型,那样会大大减小程序的可读性,请慎用,这上面的几种类型已经足够我们用了.1、细说指针指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。
引用传递与指针传递区别

C++中引用传递与指针传递区别在C++中,指针和引用经常用于函数的参数传递,然而,指针传递参数和引用传递参数是有本质上的不同的:指针传递参数本质上是值传递的方式,它所传递的是一个地址值。
值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。
值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。
(这里是在说实参指针本身的地址值不会变)而在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。
被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。
正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
引用传递和指针传递是不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。
而对于指针传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。
如果想通过指针参数传递来改变主调函数中的相关变量,那就得使用指向指针的指针,或者指针引用。
为了进一步加深大家对指针和引用的区别,下面我从编译的角度来阐述它们之间的区别:程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。
指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。
符号表生成后就不会再改,因此指针可以改变其指向的对象(指针变量中的值可以改),而引用对象则不能修改。
最后,总结一下指针和引用的相同点和不同点:★相同点:●都是地址的概念;指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。
★不同点:●指针是一个实体,而引用仅是个别名;●引用只能在定义时被初始化一次,之后不可变;指针可变;引用“从一而终”,指针可以“见异思迁”;●引用没有const,指针有const,const的指针不可变;(具体指没有int& const a这种形式,而const int& a是有的,前者指引用本身即别名不可以改变,这是当然的,所以不需要这种形式,后者指引用所指的值不可以改变)●引用不能为空,指针可以为空;●“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;typeid(T)== typeid(T&)恒为真,sizeof(T)==sizeof(T&)恒为真,但是当引用作为成员时,其占用空间与指针相同(没找到标准的规定)●指针和引用的自增(++)运算意义不一样;●引用是类型安全的,而指针不是,引用比指针多了类型检查★联系1. 引用在语言内部用指针实现(如何实现?)。
c语言中的指针详解

c语言中的指针详解在C语言中,指针是一种特殊的变量类型,它存储了一个变量的内存地址。
通过指针,我们可以间接访问和修改内存中的数据,这对于一些需要动态分配内存的操作非常有用。
以下是关于C语言指针的一些详细解释:1. 定义指针:使用"*"符号来定义指针变量。
例如,int* ptr; 定义了一个指向整型变量的指针 ptr。
2. 取址操作符(&):取地址操作符(&)用于获取变量的内存地址。
例如,&a 返回变量 a 的地址。
3. 解引用操作符(*):解引用操作符(*)用于访问指针所指向的变量的值。
例如,*ptr 返回指针 ptr 所指向的整型变量的值。
4. 动态内存分配:可以使用相关的库函数(如malloc和calloc)在运行时动态分配内存。
分配的内存可以通过指针来访问和使用,并且在使用完后应该使用free函数将其释放。
5. 空指针:空指针是一个特殊的指针值,表示指针不指向任何有效的内存地址。
可以将指针初始化为NULL来表示空指针。
6. 指针和数组:指针和数组在C语言中有密切的关系。
可以通过指针来访问数组元素,并且可以使用指针进行指针算术运算来遍历数组。
7. 传递指针给函数:可以将指针作为函数参数传递,以便在函数内部修改实际参数的值。
这种传递方式可以避免拷贝大量的数据,提高程序的效率。
8. 指针和字符串:字符串在C语言中实际上是以字符数组的形式表示的。
可以使用指针来访问和操作字符串。
需要注意的是,指针在使用时需要小心,因为不正确的操作可能导致程序崩溃或产生不可预料的结果。
对于初学者来说,理解指针的概念和使用方法可能需要一些时间和练习。
引用的概念

第4章 引用
变量a 10
a
a
100
20
变量b
b
b
图4-1
声明b是对整数a的引用, 并且使其初始化为变量a的一 个别名。一旦b同a的内存对象发生了联系,就不能改 变,而且,对b的访问就是对a的访问,对a的访问也就 是对b的访问。变量a和引用b共用同一内存空间
第4章 引用
说明: (1) 引用运算符与地址符使用的符号相同,尽管它们显
float & rj = j ; float & rrj = j ; float * pj = & rj ; // p j 指向 j ,取 r j 的地址就是取 j 的地址
第4章 引用
float * p ; float * &rp = p; //rp引用指针p float m=6.0; rp=&m; //使p指向m,对rp的访问就是对p的访问
第4章 引用
说明: (1) C++没有提供访问引用本身地址的方法,因为它与指 针或其它变量的地址不同,它没有任何意义。引用在建立时就 初始化,而且总是作为目标的别名使用,即使在应用地址操作 符时也是如此。
(2) 引用一旦初始化,它就维系在一定的目标上,再也分 不开。任何对该引用的赋值,都是对引用所维系的目标的赋值, 而不是将引用维系到另一个目标上。
# inciude <iostream.h> void swap ( int& rx , int& ry ) ; void main( ) { int x=20; int y=30;
cout<<"before swap,x:"<<x<< ",y:"<<y<<endl; swap ( x , y ) ; cout<<"after swap,x:"<<x<< ",y:"<<y<<endl; }
C语言指针详解

C语言指针详解1 程序如何运行当我们打开电脑中的任何一个程序运行时,我们的操作系统会将该程序存在硬盘的所有数据装载到内存中,然后有CPU 进行读取内存中的数据并进行计算,并将计算的结果返回给我们的操作系统,然后操作系统将相应的动作交付给相应的硬件来完成。
如:将声音数据交给声卡,最后有音响输出来,将图像交给显卡最后有显示器输出……但是还会有一部分数据会返回给内存,以供程序下面的语句继续使用。
我们都知道内存的容量有很大,如:4G,8G, 16G,有时候我们会打开很多的程序,所有的程序的数据都存放到我们的内存中,那么CPU是如何正确的读取我们的不同程序的数据并加以计算的哪?2 内存的假设设计为了让我们的CPU 可以很好的读取内存中的数据,内存必须做优化设计,于是给内存设定了集合设计,将我们的内存分成很多大小相同的方格(盒子),所有的数据将放入这些小盒子中,将不同的程序的数据放入到不同的小盒子中,这样就出现的模块化的内存,当我执行程序的一个命令时,CPU就会从相应的盒子读数据然后计算,由于我们硬件所能访问或计算的最小单位是字节,所以内存中的这样的一个小盒子的大小就给他规定一个字节。
3 地址和指针一般我们声明一块内存空间的时候,会给他取一个名字,为的是我们在编写程序的时候方便使用空间中存放的值,但是CPU 读数据的时候会忽视这个名字,因为CPU无法理解这样的数据,CPU 只能执行0,1代码,那么CPU是如何知道从什么地方读取数据,又到什么地方地址数据的读取的那,所以必须对内存做2次设计,就是将内存中分成的很多小盒子下面标注一些顺序的序号,例如:从第一个盒子开始,标注1,2,3,4,5,6,7,……每一个数字对应一个盒子,但是真正的内存如中不是使用这些十进制数字的,而是使用16进制整数表示的,如0x16ffee。
这些我们标记的数字就叫做内存中的地址。
由于这些地址和盒子是对应的关系,所以只要知道了地址,就可以得到对应盒子中存放的数据了,形象的说,我们说这个地址指向对应的盒子,在C语言中可以通过地址得到对应盒子的数据是*地址。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
C++中的指针与引用详细解读1、指针和引用的定义在深入介绍之前我们首先来看一下指针和引用的定义、指针和引用的区别,然后分别针对指针和引用展开讨论,深入细节为何有这些差异。
指针的权威定义:In a declaration T D where D has the form*cv-qualifier-seqopt D1And the type of the identifier in the declaration T D1is“derived-declarator-type-list T”,then the type of the identifier of D is “derived-declarator-type-list cv-qualifier-seq pointer to T”.The cv-qualifiers apply to the pointer and not to the object pointer to.——摘自《ANSI C++Standard》注:可能有些读者并不明白cv-qualifier-seqCV-qualifiers(CV限定符)CV-qualifiers有三种:const-qualifier(const限定符)、Volatile-qualifier(volatile限定符)、以及const-volatile-qualifier(const-volatile限定符)。
const类对象的非静态、非mutable、以及非引用数据成员是const-qualified;volatile类对象的非静态、非引用数据成员是volatile-qualified;const-volatile类对象的非静态、非引用数据成员是const-volatile-qualified。
当CV-qualifiers用于限定数组类型时,实际上是数组成员被该CV-qualifiers限定,而非该数组类型。
复合类型并不因其成员被CV-qualifier限定而被该CV-qualifier限定,也就是说,即使复合类型的成员有CV-qualifier限定,该复合类型也不是CV-qualified对象。
引用的权威定义:In a declaration T D where D has the form&D1And the type of the identifier in the declaration T D1is“derived-declarator-type-list T”,then the type of the identifier of D is “derived-declarator-type-list cv-qualifier-seq reference to T”.Cv-qualified references are ill-formed except when the cv-qualifiers are introduced through the use of a typedef or a template type argument,in which case the cv-qualifiers are ignored.——摘自《ANSI C++Standard》上面这些定义初看有些难懂,如果是这样的话,那说明你对C++还不够熟悉,你还有很长的路要走。
下面用通俗易懂的话来概述一下:指针-对于一个类型T,T*就是指向T的指针类型,也即一个T*类型的变量能够保存一个T 对象的地址,而类型T是可以加一些限定词的,如const、volatile等等。
见下图,所示指针的含义:引用-引用是一个对象的别名,主要用于函数参数和返回值类型,符号X&表示X类型的引用。
见下图,所示引用的含义:2、指针和引用的区别首先,引用不可以为空,但指针可以为空。
前面也说过了引用是对象的别名,引用为空——对象都不存在,怎么可能有别名!故定义一个引用的时候,必须初始化。
因此如果你有一个变量是用于指向另一个对象,但是它可能为空,这时你应该使用指针;如果变量总是指向一个对象,i.e.,你的设计不允许变量为空,这时你应该使用引用。
如下图中,如果定义一个引用变量,不初始化的话连编译都通不过(编译时错误):而声明指针是可以不指向任何对象,也正是因为这个原因,使用指针之前必须做判空操作,而引用就不必。
其次,引用不可以改变指向,对一个对象”至死不渝”;但是指针可以改变指向,而指向其它对象。
说明:虽然引用不可以改变指向,但是可以改变初始化对象的内容。
例如就++操作而言,对引用的操作直接反应到所指向的对象,而不是改变指向;而对指针的操作,会使指针指向下一个对象,而不是改变所指对象的内容。
见下面的代码:#include<iostream>using namespace std;int main(int argc,char**argv){int i=10;int&ref=i;ref++;cout<<"i="<<i<<endl;cout<<"ref="<<ref<<endl;int j=20;ref=j;ref++;cout<<"i="<<i<<endl;cout<<"ref="<<ref<<endl;cout<<"j="<<j<<endl;return0;}对ref的++操作是直接反应到所指变量之上,对引用变量ref重新赋值”ref=j”,并不会改变ref的指向,它仍然指向的是i,而不是j。
理所当然,这时对ref进行++操作不会影响到j。
而这些换做是指针的话,情况大不相同,请自行实验。
输出结果如下:再次,引用的大小是所指向的变量的大小,因为引用只是一个别名而已;指针是指针本身的大小,4个字节。
见下图所示:从上面也可以看出:引用比指针使用起来形式上更漂亮,使用引用指向的内容时可以之间用引用变量名,而不像指针一样要使用*;定义引用的时候也不用像指针一样使用&取址。
最后,引用比指针更安全。
由于不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,因此引用很安全。
对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL,所以不安全。
const指针虽然不能改变指向,但仍然存在空指针,并且有可能产生野指针(即多个指针指向一块内存,free掉一个指针之后,别的指针就成了野指针)。
总而言之,言而总之——它们的这些差别都可以归结为”指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名,引用不改变指向。
”3、特别之处const在这里我为什么要提到const关键字呢?因为const对指针和引用的限定是有差别的,下面听我一一到来。
常量指针VS常量引用常量指针:指向常量的指针,在指针定义语句的类型前加const,表示指向的对象是常量。
定义指向常量的指针只限制指针的间接访问操作,而不能规定指针指向的值本身的操作规定性。
常量指针定义”const int*pointer=&a”告诉编译器,*pointer是常量,不能将*pointer作为左值进行操作。
常量引用:指向常量的引用,在引用定义语句的类型前加const,表示指向的对象是常量。
也跟指针一样不能利用引用对指向的变量进行重新赋值操作。
指针常量VS引用常量在指针定义语句的指针名前加const,表示指针本身是常量。
在定义指针常量时必须初始化!而这是引用天生具来的属性,不用再引用指针定义语句的引用名前加const。
指针常量定义”int*const pointer=&b”告诉编译器,pointer是常量,不能作为左值进行操作,但是允许修改间接访问值,即*pointer可以修改。
常量指针常量VS常量引用常量常量指针常量:指向常量的指针常量,可以定义一个指向常量的指针常量,它必须在定义时初始化。
常量指针常量定义”const int*const pointer=&c”告诉编译器,pointer和*pointer都是常量,他们都不能作为左值进行操作。
而就不存在所谓的”常量引用常量”,因为跟上面讲的一样引用变量就是引用常量。
C++不区分变量的const引用和const变量的引用。
程序决不能给引用本身重新赋值,使他指向另一个变量,因此引用总是const的。
如果对引用应用关键字const,起作用就是使其目标称为const变量。
即没有:Const double const&a=1;只有const double&a=1;总结:有一个规则可以很好的区分const是修饰指针,还是修饰指针指向的数据——画一条垂直穿过指针声明的星号(*),如果const出现在线的左边,指针指向的数据为常量;如果const出现在右边,指针本身为常量。
而引用本身与天俱来就是常量,即不可以改变指向。
4、指针和引用的实现我们利用下面一段简单的代码来深入分析指针和引用:#include<iostream>using namespace std;int main(int argc,char**argv){int i=1;int&ref=i;int x=ref;cout<<"x is"<<x<<endl;ref=2;int*p=&i;cout<<"ref="<<ref<<",i="<<i<<endl;}上面的代码用g++test.c编译之后,然后反汇编objdump-d a.out,得到main函数的一段汇编代码如下:08048714<main>:8048714:55push%ebp8048715:89e5mov%esp,%ebp8048717:83e4f0and$0xfffffff0,%esp//为main函数的参数argc、argv保留位置804871a:56push%esi804871b:53push%ebx804871c:83ec28sub$0x28,%esp804871f:c744241c010000movl$0x1,0x1c(%esp)//将0x1存到esp寄存器中,即int i=1 8048726:008048727:8d44241c lea0x1c(%esp),%eax//esp寄存器里的变量i的地址传给eax 804872b:89442418mov%eax,0x18(%esp)//将寄存器eax中的内容(i的地址)传给寄存器中的变量ref,即int&ref=i804872f:8b442418mov0x18(%esp),%eax//将寄存器esp中的ref传给eax,即i的地址8048733:8b00mov(%eax),%eax//以寄存器eax中的值作为地址,取出值给eax8048735:89442414mov%eax,0x14(%esp)//将寄存器eax中的值传给寄存器esp中的x,即x=ref8048739:c7442404008904movl$0x8048900,0x4(%esp)8048740:088048741:c7042440a00408movl$0x804a040,(%esp)8048748:e8cb fe ff ff call8048618 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>804874d:8b542414mov0x14(%esp),%edx8048751:89542404mov%edx,0x4(%esp)8048755:890424mov%eax,(%esp)8048758:e85b fe ff ff call80485b8<_ZNSolsEi@plt>804875d:c7442404388604movl$0x8048638,0x4(%esp)8048764:088048765:890424mov%eax,(%esp)8048768:e8bb fe ff ff call8048628<_ZNSolsEPFRSoS_E@plt>//从8048739~8048768这些行就是执行"cout<<"x is"<<x<<endl;"804876d:8b442418mov0x18(%esp),%eax//将寄存器esp中的ref传到eax中8048771:c70002000000movl$0x2,(%eax)//将0x2存到eax寄存器中8048777:8d44241c lea0x1c(%esp),%eax//esp寄存器里的变量i的地址传给eax 804877b:89442410mov%eax,0x10(%esp)//将寄存器eax中的内容(即i的地址)传到寄存器esp中的p804877f:8b5c241c mov0x1c(%esp),%ebx8048783:8b442418mov0x18(%esp),%eax8048787:8b30mov(%eax),%esi8048789:c7442404068904movl$0x8048906,0x4(%esp)8048790:088048791:c7042440a00408movl$0x804a040,(%esp)8048798:e87b fe ff ff call8048618 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>804879d:89742404mov%esi,0x4(%esp)80487a1:890424mov%eax,(%esp)80487a4:e80f fe ff ff call80485b8<_ZNSolsEi@plt>80487a9:c74424040d8904movl$0x804890d,0x4(%esp)80487b0:0880487b1:890424mov%eax,(%esp)80487b4:e85f fe ff ff call8048618 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>80487b9:895c2404mov%ebx,0x4(%esp)80487bd:890424mov%eax,(%esp)80487c0:e8f3fd ff ff call80485b8<_ZNSolsEi@plt>80487c5:c7442404388604movl$0x8048638,0x4(%esp)80487cc:0880487cd:890424mov%eax,(%esp)80487d0:e853fe ff ff call8048628<_ZNSolsEPFRSoS_E@plt>//这些行就是执行"cout<<"ref="<<ref<<",i="<<i<<endl;"80487d5:b800000000mov$0x0,%eax80487da:83c428add$0x28,%esp80487dd:5b pop%ebx80487de:5e pop%esi80487df:89ec mov%ebp,%esp80487e1:5d pop%ebp80487e2:c3ret从汇编代码可以看出实际上指针和引用在编译器中的实现是一样的:引用int&ref=i;8048727:8d44241c lea0x1c(%esp),%eax//esp寄存器里的变量i的地址传给eax804872b:89442418mov%eax,0×18(%esp)//将寄存器eax中的内容(i的地址)传给寄存器中的变量ref,即int&ref=i指针int*p=&i;8048777:8d44241c lea0x1c(%esp),%eax//esp寄存器里的变量i的地址传给eax804877b:89442410mov%eax,0×10(%esp)//将寄存器eax中的内容(即i的地址)传到寄存器esp中的p虽然指针和引用最终在编译中的实现是一样的,但是引用的形式大大方便了使用也更安全。