(1)指针:指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。如:
int a=1;int *p=&a;
int a=1;int &b=a;
上面定义了一个整形变量和一个指针变量p,该指针变量指向a的存储单元,即p的值是a存储单元的地址。
而下面2句定义了一个整形变量a和这个整形a的引用b,事实上a和b是同一个东西,在内存占有同一个存储单元。
(2)可以有const指针,但是没有const引用;
(3)指针可以有多级,但是引用只能是一级(int **p;合法 而 int &&a是不合法的)
(4)指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化;
(5)指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了。
(6)"sizeof引用"得到的是所指向的变量(对象)的大小,而"sizeof指针"得到的是指针本身的大小;
(7)指针和引用的自增(++)运算意义不一样;
**作用: **给变量起别名,也就是新建一个指向,指向原变量的内存空间
语法: 数据类型 &别名 = 原名
例子:
#include<iostream>
using namespace std;
int main() {
int a = 10;
//给变量a指向的内存空间起一个别名,新的引用
int & b = a;
//int & c = 10; 这么使用不允许,引用必须指向内存存在的空间
cout << a << endl;
cout << b << endl;
system("pause");
return 0;
}
例子:
引用必须初始化
#include<iostream>
using namespace std;
int main() {
int a = 10;
//引用必须要初始化!!!
int & b; //此时会报错
system("pause");
return 0;
}
引用在初始化后,不可以改变
#include<iostream>
using namespace std;
int main() {
int a = 10;
int c = 20;
int & b = a;
//此时并不是将b改为c的引用,而是将20赋值给b指向的内存,也就是a指向的内存
b = c;
cout << a << endl; // 20
cout << b << endl; // 20
system("pause");
return 0;
}
截至目前,一共有两种参数传递方式,1、值传递,2、地址传递
现在,可以使用引用传递
例子:
#include<iostream>
using namespace std;
//值传递
void change1(int a) {
a = 20;
}
//地址传递
void change2(int * a) {
*a = 20;
}
//引用传递
void change3(int & a) {
a = 20;
}
int main() {
int a = 10;
change1(a);
cout << a << endl; //10,值传递,不会改变入参的源数据
int b = 10;
change2(&b);
cout << b << endl; //20,地址传递,会改变入参的源数据
int c = 10;
change3(c);
cout << c << endl; //20,引用传递,实际上,函数中的引用a,和c指向的是同一块内存中的数据
system("pause");
return 0;
}
作用:引用是可以作为函数的返回值存在的
注意:不要返回局部变量引用
用法:函数调用作为左值
例子:
#include<iostream>
using namespace std;
int& test() {
static int a = 10;
return a;
}
int main() {
int & yy = test();
cout << yy << endl; // 10
test() = 100;
cout << yy << endl; // 100
system("pause");
return 0;
}
引用的本质在c++内部实现是一个指针常量
#include<iostream>
using namespace std;
//在使用引用的时候,编译器帮我们做了一些操作
int main() {
int a = 10;
//实际上就是:int * const y = &a;
int & y = a;
//实际上就是:*y = 20;
y = 20;
//实际上就是:cout << *y << endl;
cout << y << endl;
return 0;
}
#include<iostream>
using namespace std;
//由于引用无法指向常量,所以可以添加const,编译器会帮我们进行一系列操作
int main() {
//实际上是:const int temp = 10; int & a = temp;
const int & a = 10;
//a = 20; 因为添加了const,所以不允许修改
return 0;
}
#include<iostream>
using namespace std;
//如果担心函数内修改变量值,那么可以添加const
void test(const int & a) {
// a = 20; 防止函数内修改
cout << a << endl;
}
int main() {
int a = 10;
test(a);
return 0;
}
指针参数传递本质上是值传递,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,会在栈中开辟内存空间以存放由主调函数传递进来的实参值,从而形成了实参的一个副本(替身)。值传递的特点是,被调函数对形式参数的任何操作都是作为局部变量进行的,不会影响主调函数的实参变量的值(形参指针变了,实参指针不会变)。
引用参数传递过程中,被调函数的形式参数也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参(本体)的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量(根据别名找到主调函数中的本体)。因此,被调函数对形参的任何操作都会影响主调函数中的实参变量。
引用传递和指针传递是不同的,虽然他们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,如果改变被调函数中的指针地址,它将应用不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量(地址),那就得使用指向指针的指针或者指针引用。
从编译的角度来讲,程序在编译时分别将指针和引用添加到符号表上,符号表中记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值(与实参名字不同,地址相同)。符号表生成之后就不会再改,因此指针可以改变其指向的对象(指针变量中的值可以改),而引用对象则不能修改。