泉州响应式网站建设,网站建设管理与维护,wordpress修改文章页面模板,别人买了域名做违法网站前言在C语言的学习中#xff0c;并没有引用这个概念#xff0c;但是在C中#xff0c;加入了引用这个概念#xff0c;说明引用也是很重要的#xff0c;但是我们怎么理解引用呢#xff1f;我是这么理解的#xff0c;例如在水浒传中#xff0c;108个英雄好汉都是自己的外号…前言在C语言的学习中并没有引用这个概念但是在C中加入了引用这个概念说明引用也是很重要的但是我们怎么理解引用呢我是这么理解的例如在水浒传中108个英雄好汉都是自己的外号例如及时雨宋江豹子头林冲花和尚鲁智深等等我们就可以将引用理解为一个变量的外号既然是外号说明虽然名称不同但是还是指的是同样的人。引用概念引用不是新定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。 int a 10;int b a;cout a b endl;cout a b endl;return 0;通过执行我们发现不仅a和b的值相同而且他们的地址也是相同的这也就证明了引用和本体使用同一块内存空间。注意引用类型必须和引用实体是同一种类型。 引用特性1. 引用在定义时必须初始化2. 一个变量可以有多个引用3. 引用一旦引用一个实体再不能引用其他实体1.定义时必须初始化在定义c时并没有初始化这时非法的因为引用就是一个外号没有一个本体那么是给谁取外号呢这显然是不行的。2.一个变量可以有多个引用还是以刚才水浒传的例子李逵在家时被叫做“铁牛”但是有的人还把他叫做“黑子”还有人叫他“黑旋风”我们的引用也是类似一个本体可以有多个引用。 int a 10;int b a;int c b;int d a;printf(%d %d %d %d\n, a, b, c, d);printf(%p %p %p %p\n, a, b, c, d);return 0;我们发现虽然他有四个名字但是本质上还是一个变量拥有同样的内存空间。3. 引用一旦引用一个实体再不能引用其他实体意思就是“引用”是一个忠诚的男人自打初始化开始引用只要成立就会一直跟着本体。 int a 10;int c 20;int b a;b c;printf(%d %d %d \n, a, b, c);printf(%p %p %p \n, a, b, c);我们发现当我们对b进行赋值时并不会改变a和b的引用关系只会改变他的值所以说“引用”是一个忠诚的男人。常引用有时候我们需要引用的是一个常量我们就必须使用常量引用。 const int a 10;// 该语句编译时会出错a为常量,这样会将权限扩大 //int ra a; const int ra a;// 该语句编译时会出错b为常量// int b 10; const int b 10;double d 12.34;// 该语句编译时会出错类型不同//int rd d; const int rd d;使用场景引用一般有两个使用场景第一个场景是做函数的参数第二个是做函数的返回值。做参数当我们要交换两个变量的值我们要传入指针才能改变实参的值。// 1、引用做参数
void swap(int* p1, int* p2) // 传地址
{int tmp *p1;*p1 *p2;*p2 tmp;
}void swap(int r1, int r2) // 传引用
{int tmp r1;r1 r2;r2 tmp;
}我们前边在实现单链表的时候我们需要传入二级指针是为了修改一级指针的为了让形参的改变影响到实参。当然在学习了C的引用之后我们就可以使用引用传递参数来代替二级指针传递参数。部分代码示例int* singleNumbers(int* nums, int numsSize, int returnSize){
//int* singleNumbers(int* nums, int numsSize, int* returnSize){int* a (int*)malloc(sizeof(int) * 2);//...//*returnSize 2;returnSize 2;return a;
}int TestList()
{SLTNode* plist NULL;SListPushBack(plist, 1);SListPushBack(plist, 2);SListPushBack(plist, 3);SListPushBack(plist, 4);SListPushFront(plist, 5);SListPushFront(plist, 6);SListPushFront(plist, 7);SListPrint(plist);return 0;
}2.做返回值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;
}为什么会出现这样的结果呢那是因为我们对函数进行了引用传返回值此时的ret就是c的引用也就是c的别名此时再调用Add(3,4)时ret也被改为了他的值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;
}值和引用的作为返回值类型的性能比较#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()
{int a 10;int ra a;couta aendl;coutra raendl;return 0;
}在底层实现上实际是有空间的因为引用是按照指针方式来实现的。int main()
{int a 10;int ra a;ra 20;int* pa a;*pa 20;return 0;
}我们通过反汇编来观察发现引用底层还是通过指针实现的底层也是要开不同的空间的。引用和指针的不同点:1. 引用概念上定义一个变量的别名指针存储一个变量地址。2. 引用在定义时必须初始化指针没有要求3. 引用在初始化时引用一个实体后就不能再引用其他实体而指针可以在任何时候指向任何一个同类型实体4. 没有NULL引用但有NULL指针5. 在sizeof中含义不同引用结果为引用类型的大小但指针始终是地址空间所占字节个数(32位平台下占4个字节)6. 引用自加即引用的实体增加1指针自加即指针向后偏移一个类型的大小7. 有多级指针但是没有多级引用8. 访问实体方式不同指针需要显式解引用引用编译器自己处理9. 引用比指针使用起来相对更安全总结这篇文章介绍了“引用”的相关操作并且讲解了“引用”和“指针”的不同希望大家点赞收藏。