引用
引用是c++中的,在C语言中是无法使用的。
引用可以理解为给变量起一个别名(这时候大家可能会想到typedef,但是它们不同(原因看后面))。起一个别名之后,我们使用变量别名其实就是使用变量自己,生活中,我们会有自己的小名,那么别人使用小名和本名叫你,对于你而言其实都是一样的。
int a = 10;
int& nub = a; // 给a取一个别名
上面的第二行代码就是给变量a起了一个别名nub ,这里可以看到在起别名的时候,要使用&符号。由于nub是a的别名,所以nub其实就是a(先这么理解),所以使用nub和使用a是一摸一样的。
注意: (原因在后面)
1. 引用是给变量起别名,所以它在定义的时候必须=一个变量(也就是定义一个引用的时候必须初始化为一个变量)
2. 当一个引用变量作为一个变量的别名之后,那么这个引用变量就不能作为别的变量的别名了。
例: 上面代码中nub已经作为a的别名了, 在执行 int &nub = c; 这是我们再将nub作为c的别名的时候,那么就会报错
void swap1(int &a, int &b) // 作为参数定义时不需要初始化,但是下面定义的时候需要初始化
{
int temp = a;
a = b;
b = temp;
}
上面的函数就使用引用作为参数。
当我们调用函数传递实参的时候,那么函数中的a,b就作为传入实参的别名在函数中使用。
原因: 我们使用引用作为实参,也可以实现值传递(函数参数为指针)的效果,函数中对形参的操作会影响到外部的实参。 就像上面swap1函数实现了a,b两数值交换,那么函数内a,b值进行交换那么作为实参的两变量的值也互换了。
其实理解这个原因很简单,操作别名和操作本名是一样的,所以在swap1函数内操作别名就行党羽操作传入的实参,所以互换别名的值不久是互换原名的值吗。
那么我们应该怎么调用函数呢? 和原来的一样。
int a = 100, b = 10; swap(a,b);
int sc = 10; int &md = sc; 我们同时取sc和md的地址,&sc,&md,会发现两个地址是一样的。
引用的本质
现在我们来看一下引用的本质: 我们前面说到过,int &a = b; 就可以将a看作b,对a操作就是对b操作,而且我们打印a和b的地址发现地址竟然一样。我们可能会认为a和b就是一个东西,代表一片内存。
但是,其实引用变量是由自己的内存的,也就是a有自己的内存并不是和b一样。那引用是什么呢?
我们在引用作为函数参数的时候说到过,它也可以实现和指针类似的功能,这是为什么呢?
因为,引用就是指针,对引用就是指针,所以指针有的特性它都有。
既然引用是指针为什么和指针的定义不一样?
因为,c++编译器在底层给你换掉了。
int &a = b; 还是这个例子:当我们定义一个引用的时候,其实在底层编译器将这段代码转换成了
int * const a = b; 这不就和指针的定义一样了吗 ?(那为什么由const,后面说,其实你也能想到了)。
当我们使用引用时, 是直接 a = 20, printf("%d",a); 在底层编译器将它替换成了.*a = 20;
printf("%d",*a);
函数调用时也是进行了底层的替换,
void swap1(int &a, int &b) // 作为参数定义时不需要初始化,但是下面定义的时候需要初始化
{
int temp = a;
a = b;
b = temp;
}
上面这个参数在底层编译器进行了替换:
/*执行引用的函数时,在底层会被替换*/
void swap1(int* const _a, int* const _b) // 变量名可能不一样,底层可能换成别的
{
int temp = *_a;
*_a = *_b;
*_b = *_a;
}
其实就是,引用定义和引用访问的替换。 上面说的,编译器替换可能减参数的变量名换一个(变量名并不重要)。
前面说到的,引用无法同时作为两个变量的别名,就是因为int *const, *const的指针是无法指向别的地址的,那么自然引用无法同时作为两个变量的别名。
引用是给变量取别名,指针也是变量,所以也可以取别名
int * b = &c;
int * &a = b; // 因为变量b是int*类型的,所以定义此类型的别名,自然要使用int*类型来定义。
结合上面的本质: 其实这就是定义了一个双指针。
为什么使用指针的引用呢?
我们双指针中说到,它可以用在作为函数参数,将函数中的静态变量带出函数,那这里的指针引用也是一样的。
void demo(int*& a)
{
static int nub = 10;
a = &nub;
}
上面的函数就使用了指针引用作为参数了,因为a是int*类型的引用,所以我们在函数中使用a就相当于直接在函数中使用实参,(实参是一个int*的一级指针) 所以用a和用一个一级指针是一样的。
我们定义的别名,是通过别名修改原名中的值,如swap1中的值互换,我们可以使用const将引用设置成只读,这样我们使用别名只能访问不能改值。
/*用变量初始化常引用*/
int z = 10;
const int& q = z; // 只能访问数据,不能修改数据
就像上面的代码,我们在定义z别名之前加了const,这样q作为z的别名只能访问z中的值,但是无法修改它的值。
/*用字面量初始化常引用(2等这种常数)*/
const int &q1 = 10; // 为什么可以呢,编译器会对这样定义的引用去分配内存,相当于一个常量了(其实没啥
用)
// 不要这样使用
看上面的代码,这样使用,编译器就和之前的处理不同了,遇到等于一个字面量的值的常引用,
const int &q1 = 10;这时候编译器会认为,是定义了一个const修饰的int变量,会为它分配空间。对其实我们这样不如直接 const int q1 = 10; 所以方法就不要使用了
上面说到,引用就是指针,指针的使用是比较难理解的,所以c++使用引用来使我们更加方便,当我们定义了一个引用之后,我们就认为对这个别名操作就是对原来变量操作。这样就避免使用指针,一些操作是在底层进行了。
所以,我们使用引用就认为它是定义了相应类型值得别名,之后我们使用引用就和使用原变量得方法一致。