main是什么意思(指针和引用的区别)
如果有更好的建议或者想看更多关于综合百科技术大全及相关资讯,可以多多关注茶馆百科网。

指针和引用在形式上差别很大,但在功能上似乎是一样的,都可以直接引用对象,直接操作对象。但是什么时候使用指针呢?何时使用报价?这两者很容易混淆。这里我将详细介绍指针和引用,力求把最真实的一面展现给大家。
1、指针和引用的定义
在深入介绍之前,我们先来看看指针和引用的定义,以及指针和引用的区别,然后分别讨论指针和引用,以及为什么会有这些深度细节上的区别。指针的权威定义:
在声明中
* cv-限定符-seqoptD1
而thedeclarationd 1中的thenthetypeoftheidentifierofDis是“derived-declarator-type-listT”,那么thetypeoftheidentifierofDis是“derived-declarator-type-listcv-qualifier-seqpindertot”。cv-qualifiers为pplytothepointerandnottotheobjectpointerto。
《ANSIC++Standard》的——
注意:有些读者可能不理解cv-qualifier-seq。
CV限定符(CV限定符)
有三种类型的CV-限定符:const-qualifier、volatile-qualifier和const-volatile-qualifier。
const类对象的非静态、非可变和非引用数据成员是const限定的;volatile类对象的非静态和未引用的数据成员是volatile限定的;const-volatile类对象的非静态和未引用的数据成员是const-volatile限定的。当CV-限定符用于限定数组类型时,事实上,数组成员是由CV-限定符限定的,而不是数组类型。
复合类型不是由CV-qualifier定义的,因为其成员是由CV-qualifier定义的,也就是说,即使其成员是由CV-qualifier定义的,复合类型也不是CV-qualified对象。
权威的参考定义:
在声明中
D1
而thedeclarationd 1中的thenthetypeoftheidentifierofDis是“derived-declarator-type-listT”,那么typeoftheidentifierofDis是“derived-declarator-type-listcv-qualifier-seqreferencetoT”。cv-qualified referencesareill-formed except当cv-限定符通过theuseofatypedeformattemplatetype参数引入时,在这种情况下,cv-限定符被忽略。
《ANSIC++Standard》的——
这些定义乍一看很难理解。如果是,说明你对C还不熟悉,还有很长的路要走。让我们用简单的话来总结一下:
指针——对于一个T类型,T*是指向T的指针类型,也就是T*类型的变量可以存储一个T对象的地址,T类型可以加上一些限定符,比如const,volatile等等。参见下图,显示了指针的含义:
Reference-Reference是对象的别名,主要用于函数参数和返回值类型,符号X表示类型X的引用.参见下图,显示了引用的含义:
2、指针和引用的区别
第一,引用不能为空,但指针可以为空。如我之前所说,引用是对象的别名。如果引用为空,——对象不存在,怎么会有别名!因此,在定义引用时,必须对其进行初始化。所以如果你有一个指向另一个对象的变量,但它可能是空的,那么你应该使用指针;如果变量总是指向一个对象,也就是说,你的设计不允许变量为空,那么你应该使用一个引用。如下图所示,如果定义了一个引用变量,在没有初始化的情况下,它甚至不会通过编译(编译时错误):声明指针不能指向任何对象,也正是因为这个原因,在使用指针之前必须进行空操作,但引用不是必须的。
其次,参照物不能改变方向,对一个对象是‘至死不渝’;但是指针可以改变方向,指向其他对象。注意:虽然引用不能改变方向,但是可以改变初始化对象的内容。比如在操作上,参考的操作直接反映指向的对象,而不是改变方向;指针的操作会使指针指向下一个对象,而不是改变所指对象的内容。请参见以下代码:
ng>C++代码#include<iostream>usingnamespacestd;intmain(intargc,char**argv){inti=10;int&ref=i;ref++;cout<<"i="<<i<<endl;cout<<"ref="<<ref<<endl;intj=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,表示指向的对象是常量。
定义指向常量的指针只限制指针的间接访问操作,而不能规定指针指向的值本身的操作规定性。
常量指针定义"constint*pointer=&a"告诉编译器,*pointer是常量,不能将*pointer作为左值进行操作。
常量引用:指向常量的引用,在引用定义语句的类型前加const,表示指向的对象是常量。也跟指针一样不能利用引用对指向的变量进行重新赋值操作。
●指针常量VS引用常量
在指针定义语句的指针名前加const,表示指针本身是常量。在定义指针常量时必须初始化!而这是引用天生具来的属性,不用再引用指针定义语句的引用名前加const。
指针常量定义"int*constpointer=&b"告诉编译器,pointer是常量,不能作为左值进行操作,但是允许修改间接访问值,即*pointer可以修改。
●常量指针常量VS常量引用常量
常量指针常量:指向常量的指针常量,可以定义一个指向常量的指针常量,它必须在定义时初始化。常量指针常量定义"constint*constpointer=&c"告诉编译器,pointer和*pointer都是常量,他们都不能作为左值进行操作。
而就不存在所谓的"常量引用常量",因为跟上面讲的一样引用变量就是引用常量。C++不区分变量的const引用和const变量的引用。程序决不能给引用本身重新赋值,使他指向另一个变量,因此引用总是const的。如果对引用应用关键字const,起作用就是使其目标称为const变量。即没有:Constdoubleconst&a=1;只有constdouble&a=1;
总结:有一个规则可以很好的区分const是修饰指针,还是修饰指针指向的数据——画一条垂直穿过指针声明的星号(*),如果const出现在线的左边,指针指向的数据为常量;如果const出现在右边,指针本身为常量。而引用本身与天俱来就是常量,即不可以改变指向。
4、指针和引用的实现
我们利用下面一段简单的代码来深入分析指针和引用:
C++代码
#include<iostream>usingnamespacestd;intmain(intargc,char**argv){inti=1;int&ref=i;intx=ref;cout<<"xis"<<x<<endl;ref=2;int*p=&i;cout<<"ref="<<ref<<",i="<<i<<endl;}上面的代码用g++test.c编译之后,然后反汇编objdump-da.out,得到main函数的一段汇编代码如下:
C++代码
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寄存器中,即inti=18048726:008048727:8d44241clea0x1c(%esp),%eax//esp寄存器里的变量i的地址传给eax804872b: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:e8cbfeffffcall8048618<_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>804874d:8b542414mov0x14(%esp),%edx8048751:89542404mov%edx,0x4(%esp)8048755:890424mov%eax,(%esp)8048758:e85bfeffffcall80485b8<_ZNSolsEi@plt>804875d:c7442404388604movl$0x8048638,0x4(%esp)8048764:088048765:890424mov%eax,(%esp)8048768:e8bbfeffffcall8048628<_ZNSolsEPFRSoS_E@plt>//从8048739~8048768这些行就是执行"cout<<"xis"<<x<<endl;"804876d:8b442418mov0x18(%esp),%eax//将寄存器esp中的ref传到eax中8048771:c70002000000movl$0x2,(%eax)//将0x2存到eax寄存器中8048777:8d44241clea0x1c(%esp),%eax//esp寄存器里的变量i的地址传给eax804877b:89442410mov%eax,0x10(%esp)//将寄存器eax中的内容(即i的地址)传到寄存器esp中的p804877f:8b5c241cmov0x1c(%esp),%ebx8048783:8b442418mov0x18(%esp),%eax8048787:8b30mov(%eax),%esi8048789:c7442404068904movl$0x8048906,0x4(%esp)8048790:088048791:c7042440a00408movl$0x804a040,(%esp)8048798:e87bfeffffcall8048618<_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>804879d:89742404mov%esi,0x4(%esp)80487a1:890424mov%eax,(%esp)80487a4:e80ffeffffcall80485b8<_ZNSolsEi@plt>80487a9:c74424040d8904movl$0x804890d,0x4(%esp)80487b0:0880487b1:890424mov%eax,(%esp)80487b4:e85ffeffffcall8048618<_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>80487b9:895c2404mov%ebx,0x4(%esp)80487bd:890424mov%eax,(%esp)80487c0:e8f3fdffffcall80485b8<_ZNSolsEi@plt>80487c5:c7442404388604movl$0x8048638,0x4(%esp)80487cc:0880487cd:890424mov%eax,(%esp)80487d0:e853feffffcall8048628<_ZNSolsEPFRSoS_E@plt>//这些行就是执行"cout<<"ref="<<ref<<",i="<<i<<endl;"80487d5:b800000000mov$0x0,%eax80487da:83c428add$0x28,%esp80487dd:5bpop%ebx80487de:5epop%esi80487df:89ecmov%ebp,%esp80487e1:5dpop%ebp80487e2:c3ret
从汇编代码可以看出实际上指针和引用在编译器中的实现是一样的:
●引用int&ref=i;
8048727:8d44241clea0x1c(%esp),%eax//esp寄存器里的变量i的地址传给eax804872b:89442418mov%eax,0x18(%esp)//将寄存器eax中的内容(i的地址)传给寄存器中的变量ref,即int&ref=i
●指针int*p=&i;
8048777:8d44241clea0x1c(%esp),%eax//esp寄存器里的变量i的地址传给eax804877b:89442410mov%eax,0x10(%esp)//将寄存器eax中的内容(即i的地址)传到寄存器esp中的p
虽然指针和引用最终在编译中的实现是一样的,但是引用的形式大大方便了使用也更安全。有人说:"引用只是一个别名,不会占内存空间?"通过这个事实我们可以揭穿这个谎言!实际上引用也是占内存空间的。
5、指针传递和引用传递
为了更好的理解指针和引用,我们下面来介绍一下指针传递和引用传递。当指针和引用作为函数的函数是如何传值的呢?(下面这一段引用了C++中引用传递与指针传递区别(进一步整理))
●指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。
●引用传递过程中,被调函数的形式参数也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
引用传递和指针传递是不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量,那就得使用指向指针的指针,或者指针引用。
参考文献
[1]TheC++ProgrammingLanguage(SpecialEdition),BjarneStroustrup
[2]ANSIC++Standard
[3]汇编语言
本文主要介绍了关于main是什么意思(指针和引用的区别)的相关养殖或种植技术,综合百科栏目还介绍了该行业生产经营方式及经营管理,关注综合百科发展动向,注重系统性、科学性、实用性和先进性,内容全面新颖、重点突出、通俗易懂,全面给您讲解综合百科技术怎么管理的要点,是您综合百科致富的点金石。
以上文章来自互联网,不代表本人立场,如需删除,请注明该网址:http://23.234.50.4:8411/article/98638.html