目录
一、引用
1、引用的使用
int a = 10;
int& ra = a;//引用
int* p = &a;//取地址
引用ra是变量a的别名
2、引用的规则
int& c;//err
int& ra = a;
int&& raa = a;//err
int & rb = ra;//都是a的别名
//滞后
int a = 100;
int* p = NULL;
int& ra = *p;//err
p = &a;//err
ra += 100;//err
//其他类型引用
double a = 10;
double& ra = a;
double& rb = ra;
//数组的引用
int ar[5] = { 1,2,3,4,5 };//数组描述时要说类型和大小
int& ra = ar[1];//元素
ra += 100;//ar[1]=102
int& rb = ar;// err 不能引用一整个数组
int(&rb)[5] = ar;//是数组的话 描述大小和类型
//指针的引用
int a = 10, b = 20;
int* p = &a;
int* s = p;
int*& rp = p; //rp是p的别名
*rp = 100;//可解引用
rp = &b;//可以指向其它地址
//int &br[10]; 定义一个数组,每个元素都是引用
//int& *p;//定义一个指针指向引用
(1)不允许使用空引用,定义引用必须进行初始化。
(2)没有多级引用。
(3)一个变量可以有多个引用,都是它的别名。
(4)不允许滞后引用。
(5)每一种类型都有它的引用。
3、引用的分类
补充:
值类别分为:左值、将亡值、右值
能取地址的是左值,不能取地址的是右值
int a = 10;
&a;//左值
&10;//右值 err
将亡值具名后是左值,未具名前是右值
(1)左值引用
int a = 10;
int& b = a;//左值引用
int& c = 10;//err
"="右边的值为左值
(2)右值引用
int&& cy = 100;//right
//int tmp=100;
//int && cy=tmp;
cy += 100;//right
“=”右边的值为右值
该过程产生将亡值tmp
(3)万能引用 (常引用)const
int a = 10; //普通引用和常性普通引用都能引用
const int b = 10; //只能用常性引用来引用
int& ra = a;
const int& cra = a;
int& rb = b;//err
const int& crb = b;
const int& cx = 100;//right
//int tmp=100;
//const int & cx=tmp; cx+=100 err 常量不能改
“=”右边的值可以是右值,也可以是左值
该过程产生将亡值tmp
4、引用的实现
对比代码:上面是原码,下面是编译之后改动的代码。
void func(int& rb)
{
int* ip = &rb;
*ip = 100;
}
int main()
{
int a = 10;
int& ra = a;
func(a);
ra = 200;
return 0;
}
编译器在编译的过程中会把引用变为常性指针。
void func(int* const rb)
{
int* ip = rb;
*ip = 100;
}
int main()
{
int a = 10;
int* const ra = &a;
func(&a);
*ra = 200;
return 0;
}
引用在底层被编译器编译为指针,在语法规则上引用没有空间,在机器代码中分配空间。
二、引用和指针的区别
按照语法规则:
(1)有二级指针,没有二级引用。
(2)指针指向变量地址,引用是变量的别名。
(3)程序为指针变量分配内存区域,但是不给引用分配。
(4)指针使用前+“ * ”,引用直接使用。
(5)指针的字节大小为4或者8字节,而引用的字节大小则为变量的字节大小。
(6)引用一旦初始化,不能改变,指针可以改变去存储不同的地址。
int a = 10, b = 20;
int* ip = &a;
int& ra = a;
*ip = 100;
ip = &b;
*ip = 200;
ra = 300;
ra = b;//a=b 只是把b的值给ra
(7)++ 之后效果不一样。
int arr[10] = { 12,23,34,45,56,67,78,89 };
int* ip = &arr[1];
int& ra = arr[1];
++ip;//指向arr[2]地址
++ra;//arr[1]+1
++(*ip);//arr[1]+1