原文链接
1. 一方面,每一种编程语言都使用指针。不止C/C++使用指针。
每一种编程语言都使用指针。C++将指针暴露给了用户(程序员),而Java和C#等语言则将指针隐藏起来了。
1. 另一方面使用指针的优点和必要性:
c++ primer plus中给引用的定义是:引用是已定义的变量的别名(另一个名称)
本质:引用是别名,指针是地址,具体的:
符号表
上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用
在符号表上对应的地址值为引用对象的地址值
。符号表生成后就不会再改,因此指针可以改变指向的对象(指针变量中的值可以改),而引用对象不能改。这是使用指针不安全而使用引用安全的主要原因。从某种意义上来说引用可以被认为是不能改变的指针。
int** p1; // 合法。指向指针的指针
int*& p2; // 合法。指向指针的引用
int&* p3; // 非法。指向引用的指针是非法的
int&& p4; // 非法。指向引用的引用是非法的
注意上述读法是从左到右。
定义一个引用的时候,必须初始化
,不初始化的话连编译都通不过(编译时错误)。因此如果你有一个变量是用于指向另一个对象,但是它可能为空,这时你应该使用指针;如果变量总是指向一个对象.,你的设计不允许变量为空,这时你应该使用引用。注意:正因为指针可以不指向任何对象,使用指针之前必须做判空操作,而引用就不必。
至死不渝
";但是指针可以改变指向
,而指向其它对象。说明:虽然引用不可以改变指向,但是可以改变初始化对象的内容。例如就++操作而言,对引用的操作直接反应到所指向的对象,而不是改变指向;而对指针的操作,会使指针指向下一个对象,而不是改变所指对象的内容。
野指针
)。总之,可以归结为"指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名,引用不改变指向。
"
为什么要提到const关键字呢?因为const对指针和引用的限定是有差别的:
1. 常量指针VS常量引用
常量指针:指向常量的指针,在指针定义语句的类型前加const,表示指向的对象是常量。
定义指向常量的指针只限制指针的间接访问操作,而不能规定指针指向的值本身的操作规定性。
gec@ubuntu:~/demo$ cat main.c
#include
int main(int argc,char **argv)
{
int i=1;
const int *pointer=&i;
*pointer=20;
return 0;
}
错误:
gec@ubuntu:~/demo$ gcc main.c -o main
main.c: In function ‘main’:
main.c:10:13: error: assignment of read-only location ‘*pointer’
*pointer=20;
常量指针定义"const int* pointer=&a"告诉编译器,pointer是常量,不能将pointer作为左值
进行操作。
常量引用:指向常量的引用,在引用定义语句的类型前加const,表示指向的对象是常量。也跟指针一样不能对引用指向的变量进行重新赋值操作。
gec@ubuntu:~/demo$ cat main.c
#include
int main(int argc,char **argv)
{
int i=1;
const int &pointer=i;
pointer=20;
return 0;
}
错误:
gec@ubuntu:~/demo$ g++ main.c -o main
main.c: In function ‘int main(int, char**)’:
main.c:10:13: error: assignment of read-only reference ‘pointer’
pointer=20;
^~
2.指针常量VS引用常量
在指针定义语句的指针名前加const,表示指针本身是常量。在定义指针常量时必须初始化!而这是引用与生俱来的属性,无需使用const。
指针常量定义"int* const pointer=&b"告诉编译器,pointer(地址)是常量,不能作为左值进行操作,但是允许修改间接访问值,即*pointer(地址所指向内存的值)可以修改。
gec@ubuntu:~/demo$ cat main.c
#include
int main(int argc,char **argv)
{
int *const pointer;
int* const &ref=pointer;
return 0;
}
这是错误的:
gec@ubuntu:~/demo$ g++ main.c -o main
main.c: In function ‘int main(int, char**)’:
main.c:7:18: error: uninitialized const ‘pointer’ [-fpermissive]
int *const pointer;
^~~~~~~
3.常量指针常量VS常量引用常量
常量指针常量:指向常量的指针常量,可以定义一个指向常量的指针常量,它必须在定义时初始化。
定义"const int* const pointer=&c"
告诉编译器,pointer和*pointer都是常量,他们都不能作为左值进行操作。
而不存在所谓的"常量引用常量",因为引用变量就是引用常量
。C++不区分变量的const引用和const变量的引用。程序决不能给引用本身重新赋值,使他指向另一个变量,因此引用总是const的
,修饰为const就一定不能作为左值
。如果对引用应用关键字const,起作用就是使其目标称为const变量。即
没有:const double const& a=1;
只有const double& a=1;
double b=1;
constdouble& a=b;
b=2;//正确
a=3;//出错error: assignment of read-only reference `a'
总结:有一个规则可以很好的区分const是修饰指针,还是修饰指针指向的数据——画一条垂直穿过指针声明的星号(*),如果const出现在线的左边,指针指向的数据为常量
;如果const出现在右边,指针本身为常量
。而引用本身就是常量,即不可以改变指向。
为了更好的理解指针和引用,下面介绍一下指针传递和引用传递。当指针和引用作为函数的参数是如何传值的呢?
副本
。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量
进行,不会影响主调函数的实参变量的值。通过栈中存放的地址访问主调函数中的实参变量
。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。引用传递和指针传递是不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量, 那就得使用指向指针的指针,或者指针引用。
从概念上讲。指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。
而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性
,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量)。
. 相同点
指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。
不同点
具体指没有int& const a这种形式,而const int& a是有的,前者指引用本身即别名不可以改变,这是当然的,所以不需要这种形式,后者指引用所指的值不可以改变)
sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
不可对常量取引用,例如 int &r = 100; 是错误的,因为常量是匿名的。
引用必须在定义时同时赋值,不可单独定义引用,例如 int &r; 是错误的。
`