1.1、使用引用四个知识点需要明白
1 引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关系。故 而类型与原型保持一致,且不分配内存。与被引用的变量有相同的地址。
2 声明的时候必须初始化,一经声明,不可变更。
3 可对引用,再次引用。多次引用的结果,是某一变量具有多个别名。
4 &符号前有数据类型时,是引用。其它皆为取地址。
1.2、引用的本质
首先我们来测试一下引用占用多大字节
#include <iostream>
using namespace std;
struct test_a{
int &a;
};
struct test_b{
int *p;
};
int main(void)
{
cout << "引用大小:" << sizeof(struct test_a) << endl;
cout << "指针大小:" << sizeof(struct test_b) << endl;
return 0;
}
运行结果:
在同为整形的前提下,他们都是占用四个字节,可是在这边看来指针是可以修改的指向别的变量,但是引用不行。
#include <iostream>
using namespace std;
int main(void)
{
int a = 10;
int b = 40;
int *p;
int &re = a;
re = b;
p = &b;
*p = 80;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "re = " << re << endl;
cout << "*p = " << *p << endl;
return 0;
}
运行结果:
我们来分析一下这个结果,因为引用在一开始就初始化为某个变量的别名,也就是a的别名,之后将不再改变。
re = b;
对于这个语句,我们初衷的是将re作为b的别名,但是实际上此刻的re就已经代表a了,所以这个语句相当于。
a = b;
所以就有 a = 40 的这个结果,而使用指针取b的地址,所以是可以通过指针来修改对应的值。
这个和指针类似,并且必须在开头初始化,此刻我不禁想到 int * const ,因为在C++中,const修饰的就是真正意义上的常量,这也必须在开始定义时候进行初始化。
例如我们来做一个对比:
我们看一下这部分代码,我们可以看到第一行,编译器已经提示出错,必须要初始化,第二行就是给指针类似 int &re = a; 这个过程,第三句想修改该指针的指向,但是发现无法修改,编译器已经报错了,类似于 re = b ,该语句我们开始也是想引用变量re换为变量b,但是发现这是一个普通的赋值功能,类似于第四句。
所以我们也知道引用其实也是占用空间的!
所以对于引用,如果想去探究,可以将其当成int * const指针来研究,但是在实际中就没必要,我们只需要知道引用的使用即可。
1.3、作为函数的返回值
作为返回值也需要注意
当返回值是局部变量时:
#include <iostream>
using namespace std;
int& my_max(int a, int b)
{
int temp;
if (a <= b){
temp = b;
}
else {
temp = a;
}
return temp;
}
int main(void)
{
int a = 0;
int &re = a;
re = my_max(10,37);
cout << "re = " << re << endl;
return 0;
}
运行结果:
我们发现,这也是可以达到我们想要的结果,因为局部变量temp是在 re = my_max(10,37); 这个语句运行完才会被出栈释放,而返回temp的引用也就是可以有效的赋值给re这个引用。
但是我们考虑到如果在引用初始化的时候让他等于局部变量的引用,是不是会出错,因为在运行完那个语句之后,局部变量就会被释放,那么此刻引用所对应的就不是原来的值了。
#include <iostream>
using namespace std;
int& my_max(int a, int b)
{
int temp;
if (a <= b){
temp = b;
}
else {
temp = a;
}
return temp;
}
int main(void)
{
int a = 0;
int &re = a;
int &re1 = my_max(56, 98);
re = my_max(10,37);
cout << "re = " << re << endl;
cout << "re1 = " << re1 << endl;
return 0;
}
运行结果:
结果显示re1是一个非法数值,所以在引用作为返回值时,如果返回的是局部变量,一定要注意不要再初始化进行给引用赋值。
当返回值是全局或者static变量时:
其实刚才在上面讨论过后,在这里哪怕不写代码验证,我们都知道完全是可以没问题进行使用的。
1.4、指针引用
在说明之前,我们先写一段代码
#include <iostream>
using namespace std;
struct student_struct{
int id;
char name[255];
};
int student_init(struct student_struct **pst)
{
struct student_struct *petmp ;
petmp = (struct student_struct *)malloc(sizeof(struct student_struct));
if (petmp == NULL){
cout << "malloc err" << endl;
return -1;
}
petmp->id = 10;
memset(petmp->name,0x00,255);
memcpy(petmp->name,"gale",4);
*pst = petmp;
return 0;
}
void student_free(struct student_struct **pst)
{
if (*pst == NULL){
return;
}
else{
free(*pst);
*pst = NULL;
}
}
int main(void)
{
int a = 0;
int &re = a;
struct student_struct *pst;
student_init(&pst);
cout << "id = " << pst->id << " name:" << pst->name << endl;
student_free(&pst);
return 0;
}
运行结果:
这段代码主要实现一个功能,就是对于学生信息管理的初始化,采用的是申请内存的方式,在这里的话,运用指针的指针,可能会有些绕,导致在写代码的时候容易出错,那么我们能不能用指针引用来处理简化呢?
答案是肯定的整形能够使用引用,那么指针肯定也可以使用,我们使用引用写这部分代码如下:
#include <iostream>
using namespace std;
struct student_struct{
int id;
char name[255];
};
int student_init(struct student_struct* &pst)
{
pst = (struct student_struct*)malloc(sizeof(struct student_struct));
if (pst == NULL){
printf("malloc err\r\n");
return -1;
}
pst->id = 10;
memset(pst->name,0x00,255);
memcpy(pst->name,"gale",4);
return 0;
}
void student_free(struct student_struct* &pst)
{
if (pst == NULL){
return;
}
else{
free(pst);
pst = NULL;
}
}
int main(void)
{
struct student_struct *pst;
struct student_struct* &student_re = pst;
student_init(student_re);
cout << "id = " << student_re->id << " name:" << student_re->name << endl;
student_free(student_re);
return 0;
}
运行结果:
使用指针引用也可以达到目前的功能,但是看这部分代码,你会发现使用指针的引用方式看起来简单很多,如果在之前使用指针的指针,如果一不小心少了个*,那么调试起来会有点耗时,但是使用指针引用后,发现就变的很简单了。
当然不是说一定要使用指针引用,因人而异,喜欢使用什么方式就用什么方式,毕竟代码只是实现某个功能而已。