东莞网站免费制作,html5网站开发框架,让一个网站掉排名,网站专业好找工作吗文章目录 前言概述引用特性应用场景做参数做返回值 传值、传引用效率比较引用和指针的区别 前言
在学习C语言的时候会遇到指针#xff0c;会有一级指针、二级指针…很容易让人头昏脑胀。在C里面#xff0c;引入了引用的概念#xff0c;会减少对指针的使用。引用相当于给一个… 文章目录 前言概述引用特性应用场景做参数做返回值 传值、传引用效率比较引用和指针的区别 前言
在学习C语言的时候会遇到指针会有一级指针、二级指针…很容易让人头昏脑胀。在C里面引入了引用的概念会减少对指针的使用。引用相当于给一个变量起了一个别名比如“高总”指的是小编。
概述
引用不是新定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。
类型 引用变量名(对象名) 引用实体
#includeiostreamusing namespace std;int main()
{int a 10;int b a;int c a;return 0;
}调试过程中发现引用变量的地址和引用实体的地址是相同的也就是说引用实际上就是给一个变量起了一个别名。 注意引用类型必须和引用实体是同种类型的
引用和C语言中只针的操作其实差不多我们在反汇编语言中可以看到 引用特性
1. 引用在定义时必须初始化
int main()
{int a 10;int ra; //错误语法return 0;
}在这段代码中int ra没有初始化编译器会报错 正确代码
int main()
{int a 10;int ra a;return 0;
}一个变量可以有多个引用
int main()
{int a 10;int ra a;int rra a;return 0;
}上述代码中ra、rra都是对变量a的引用。这是没有问题的比如小编有不止一个外号。
引用一旦引用一个实体再不能引用其他实体
int main()
{int a 10;int temp 20;int ra a;ra temp;cout ra endl;cout a endl;return 0;
}运行结果 在这段代码中ra是变量a的引用ratemp是将temp的值赋给ra引用的实体即a。
应用场景
做参数
效果
做输出型参数形参的改变可以影响实参减少拷贝提高效率
void Swap(int a,int b)
{int tmp a;a b;b tmp;
}int main()
{int x 0, y 1;Swap(x, y);cout x y endl;return 0;
}运行结果 上述代码确实实现了交换两个数字的功能
形参a是对实参x的引用和x表示同一块空间形参b是对实参y的引用和y表示的是用一块空间。所以在函数内交换a和b实际上就是在交换x和y。
做返回值
以前的传值返回
int Add(int a, int b)
{int sum a b;return sum;
}int main()
{int x 4;int y 3;int ans Add(x, y);cout ans endl;return 0;
}这里随着函数栈帧调用的结束sum也会销毁。那为什么最后还能打印出最终结果
对于这种传值返回会有一个临时变量的生成这种临时变量是用来存储返回值的当返回值比较小的时候这个临时变量就是寄存器。通过反汇编我们可以看到把sum值赋给了寄存器eax。 以上是在局部变量中
那么以satic修饰的变量在静态区此变量虽然不会随着调用函数的栈帧销毁而销毁但是在传值返回的时候也会创建临时变量。 因此不难看出传值返回都会生成一个中间变量。 以上是以前的写法那么在学了引用后我们需要使用引用返回
引用返回和传值返回不同函数栈帧销毁后不需要创建临时变量来存储返回值。但是函数栈帧销毁后返回的变量仍然存在。
也就是说返回的变量不能存储在调用的函数的栈帧中所以返回的变量是存储在静态区的变量或者是在堆上申请的变量。
先来看下面的代码
int Add(int a, int b)
{int c a b;return c;
}
int main()
{int ret Add(1, 2);Add(3, 4);cout Add(1, 2) is : ret endl;return 0;
}运行结果 并不是我们需要的结果这是为什么呢
主函数中首先调用的是函数Add(1,2)此函数函数调用结束后该函数对应的栈空间就被回收了也就是说Add函数中c变量就没有意义了。中ret引用Add函数返回值实际应用的就是一块已经被释放的空间。 然后调用Add(3,4)函数此函数函数调用结束后该函数对应的栈空间就被回收了也就是说Add函数中c变量就没有意义了。注意空间被收回是说空间不能使用了但是空间本身还在而ret引用的c的位置被修改成了7因此ret的值就被修改了。 关于引用返回需要强调的是
函数运行时系统需要给该函数开辟独立的栈空间用来保存该函数的形参、局部变量以及一些寄存信息等函数运行结束后该函数的栈空间就会被系统收回空间被收回指的是这块栈空间暂时不能被使用但是内存还在
注意 如果函数返回时出了函数作用域如果返回对象还在(还没还给系统)则可以使用引用返回如果已经还给系统了则必须使用传值返回。
传值、传引用效率比较
以值作为参数或者返回值类型在传参和返回期间函数不会直接传递实参或者将变量本身直接返回而是传递实参或者返回变量的一份临时的拷贝因此用值作为参数或者返回值类型效率是非常低下的尤其是当参数或者返回值类型非常大时效率就更低。
#include time.h
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A a) {}
void TestRefAndValue()
{A a;// 以值作为函数参数size_t begin1 clock();for (size_t i 0; i 10000; i)TestFunc1(a);size_t end1 clock();// 以引用作为函数参数size_t begin2 clock();for (size_t i 0; i 10000; i)TestFunc2(a);size_t end2 clock();// 分别计算两个函数运行结束后的时间cout TestFunc1(A)-time: end1 - begin1 endl;cout TestFunc2(A)-time: end2 - begin2 endl;
}int main()
{TestRefAndValue();return 0;
}运行结果 性能比较
#include time.h
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A TestFunc2() { return a; }
void TestReturnByRefOrValue()
{// 以值作为函数的返回值类型size_t begin1 clock();for (size_t i 0; i 100000; i)TestFunc1();size_t end1 clock();// 以引用作为函数的返回值类型size_t begin2 clock();for (size_t i 0; i 100000; i)TestFunc2();size_t end2 clock();// 计算两个函数运算完成之后的时间cout TestFunc1 time: end1 - begin1 endl;cout TestFunc2 time: end2 - begin2 endl;
}int main()
{TestReturnByRefOrValue();return 0;
}运行结果 通过上述代码的比较发现传值和指针在作为传参以及返回值类型上效率相差很大
引用和指针的区别
在语法概念上引用就是一个别名没有独立空间和其引用实体共用同一块空间
在底层实现上实际是有空间的因为引用是按照指针方式来实现的
引用和指针的不同点:
引用概念上定义一个变量的别名指针存储一个变量地址。引用在定义时必须初始化指针没有要求引用在初始化时引用一个实体后就不能再引用其他实体而指针可以在任何时候指向任何一个同类型实体没有NULL引用但有NULL指针在sizeof中含义不同引用结果为引用类型的大小但指针始终是地址空间所占字节个数(32位平台下占4个字节)引用自加即引用的实体增加1指针自加即指针向后偏移一个类型的大小有多级指针但是没有多级引用访问实体方式不同指针需要显式解引用引用编译器自己处理引用比指针使用起来相对更安全