文章目录
- 一、猿创引用C++中的征文中引用与C的联系
- 二、引用的猿创引用概念
- 三、定义引用
- 四、征文中引用存在的猿创引用意义
- 五、引用的征文中相关用法
- 5.1 基本用法
- 5.2 引用做形参
- 5.3 常引用
- 5.4 引用指针
- 5.5 引用做返回值
- 5.6 结构体中存在引用成员
- 六、引用和指针的猿创引用特点及区别总结
一、C++中的征文中引用与C的联系
一句话概括:
C++中的引用,是猿创引用C中指针的升级版。
二、征文中引用的猿创引用概念
引用是C++对C的一个重要扩充。
作用是征文中给变量起个别名。
对引用的猿创引用操作与对变量直接操作完全一样。 (类似于linux中的征文中硬链接文件)
引用不是定义一个新变量,而是猿创引用给已存在的变量取了一个外号,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
举个形象的例子,鲁智深又被叫做"花和尚",这里的花和尚和鲁智深都是同一个人,花和尚就是鲁智深的引用,说白了引用其实就是取外号。
三、定义引用
&
在定义引用时,作用是引用标识符,标识定义的是一个引用;
在C++中 & 有三个作用:
- 定义引用时,表示引用标识符,标识定义的是一个引用;
- 有两个操作数时,a&b,位运算的 按位与;
- 其他场景都表示取变量地址的意思;
定义引用格式:
类型名 &引用名 = 引用的目标;
如:
int a = 10;//定义一个引用b 引用的目标是a//定义成功后,使用b 和使用 a就是一样的了int& b = a;
要求:
- 定义引用时必须要有引用的目标来初始化;
- 引用和引用的目标类型要保持一致;(继承和多态除外)
- 引用的目标一旦确定了,后面就不能再修改引用的目标了;
四、引用存在的意义
C++的面向程序较大型化,所以使用引用,可以避免指针指向错误的空间,而出现程序的崩溃或严重BUG。
以下面代码为例:
#include using namespace std;int add(int *a,int *b){ //a++;如果偏移会出现越界访问错误 return *a + *b;}int main(){ int a=10; int b=20; int *p;//野指针,p中指向的地址是随机的 //*p=100;出错 int *const p1=&a; //p1++;报错 int c=20; //p1=&c;报错 cout << add(&a,&b) << endl; return 0;}
总结:
- 在C++中const修饰的变量,一定要进行初始化;
- 在程序中不小心对指针进行偏移操会出现访问越界错误;单纯使用指针的方式无法保证代码的稳定性。
- 可能出现野指针问题;
- 应当用
const
修饰指针变量避免以上出现的问题;
所以引用就来了。
五、引用的相关用法
5.1 基本用法
#include using namespace std;int main(){ int a=10; int b=60; int& c=a; cout << a << "," << c <
结果展示:
从结果我们可以发现:
这个引用b变量,并没有开辟新的空间。
c = b;
这种用法不是改变 c 引用的目标,而是将 b 的值赋给 r 一份儿。
5.2 引用做形参
好处:不用再考虑值传递和地址传递的问题了
#include using namespace std;int myfun(int& aa){ cout << "myfun():" << &aa << endl; aa=aa+10;}int main(){ int a=10; cout << "main():" << &a <
结果展示:
分析结果可知:
a 的地址和 aa 的地址是一样的,所以使用 aa 就是使用 a;
总结:
使用引用并没有开辟空间,在函数传参是最为常用的一种方式。
自此学了引用之后,函数传参,如果是结构体的话,推荐大家使用引用的方式进行传参。
同时,为了代码稳定与健状性,推存使用const修饰的引用做为函数的形参。
5.3 常引用
const修饰的引用被称为常引用。
const修饰的引用,不仅可以引用左值,也可以引用右值。
什么是左值?什么是右值?
左值:有地址的量即左值,比如变量就是一个左值,因为变量是有地址的。一个左值,既可以放在 = 号左边,也可以放在 = 号的右边。
右值:没有地址的量即一个右值,立即数就是一个右值,常量字符串也是一个右值。只能放在 = 号的右边。
C++中有这种常引用,主要是用来保护数据的。同时,这种引用即可以引用左值,也可以引用右值。
(C++中的常引用,即为一种语法糖)
常引用举例:
#include using namespace std;int myfun(const int& a,const int& b){ cout << a+b << endl; //a=600;错误,不能通过常引用修改引用目标的值。}void show(const string& name){ //name="xiaoming";使用常引用就可以避免这种问题的出现。 cout << name << endl;}int main(){ int a = 10; int b = 20; myfun(a,b); int c = 100; const int& c1 = c; //c1=500;报错, c=500;//不影响c,因为c的类型在定义时就已经确定了 是没有const的 //const int& c2 = 100;//常引用可以引用常量,C++的语法糖。 //语法糖的底层实现。 int temp = 100; const int& c2 = temp; //const修饰的这种常引用或者说叫左值引用,比单纯使用引用对C++程序代码来讲更具有意义。 //因为他就是一种安全性的标识。 //int &c3 = 100;//普通的引用不能 const int& c4 = c+100;//常引用可以引用临时值 //int &r5 = c+100;//普通的引用不能 const int m = 10; //int& r6 = m; //引用const变量时也需要常引用 const int& r6 = m; string name="yemaoxu"; show(name); return 0;}
总结:
- 不能通过常引用修改引用目标的值;
- 常引用可以引用常量,普通的引用不能;
- 常引用可以引用临时值,普通的引用不能;
- 引用const变量时也需要常引用;
- 语法糖的底层实现
int temp = 100;const int& c2 = temp;
5.4 引用指针
#include #include using namespace std;typedef struct Node{ int data; struct Node *next;}node_t;//引用指针的用法//node_t * &r = phead;void create_node(node_t * &r, int data){ r = (node_t *)malloc(sizeof(node_t)); r->data = data; r->next = NULL;}int main(){ node_t *phead = NULL; create_node(phead, -1); phead->data = 100; cout<data<
结果展示:
5.5 引用做返回值
我们平时使用的函数,返回值都是一个右值;
但是引用作为返回值,返回的是一个左值;
引用做返回值时,不能返回局部变量的引用,因为局部变量占用的空间
在函数结束时,就被操作系统回收了;
可以返回全局变量的引用,或者static
修饰的局部变量的引用。
#include #include using namespace std;int& add(const int& a,const int& b){ //int temp=a+b; static int temp=a+b; return temp;}int main(){ int a=10; int b=20; int ret=add(a,b); cout << ret <
结果展示:
总结:
- 引用做返回值,返回的是一个左值;
- 不能返回局部变量的引用;
5.6 结构体中存在引用成员
#include #include using namespace std;struct Work{ int a; int& b;};int main(){ int m=20; //struct Work work1;错误的 struct Work work2={ 10,m};//必须初始化引用成员才可以 return 0;}
总结:
结构体中存在引用成员,必须初始化引用成员。
六、引用和指针的特点及区别总结
从编译器角度来讲:
引用就是一种升级版的指针。
从语法形式来讲:
- 引用是引用的是已经存在一块合法的空间。
- 引用变量即是引用空间的变量的别名。
- 指针可以是一个野指针,他可以指向任何的地方。
- 指针可以进行无限次的赋值,引用只可以被引用一次。
- 引用必须初始化,指针可以不初始化;
- 引用不可以改变指向,指针可以;
- 不存在指向NULL的引用,指针可以指向NULL ;
- 指针在使用前需要检查合法性,引用不需要;
- 可以定义指针数组、不可以定义引用数组;
int a = 10,b = 20;int *arr[2] = { &a, &b} //正确------------------------------------int &arr[2] = { a, b} //错误
- 可以定义数组指针,也可以定义数组引用
int arr[2][2] = { 10,20,30,40};int (*arr_p)[2] = arr;-----------------------------------int arr[2] = { 10,20};int (&arr_p)[2] = arr;//错误的数组引用int (&arr_p)[2][2] = arr;//正确的数组引用
- 可以定义指针函数,也可以定义引用函数
int *func_p(int a, int b){ }-----------------------------------int &func_p(int a, int b){ }
- 可以定义函数指针,也可以定义函数引用
int func(int a, int b){ }int (*func_p)(int a, int b);func_p = func;------------------------------------int (&func_r)(int a, int b) = func;
- 可以定义指针的指针(二级指针) 不可以定义引用的引用(二级引用)
int a = 100;int *p = &a;int **pp = &p;---------------------------------int a= 100;int &r = a;int &&rr = r; //错误