四川大良网站建设,网站规划建设实训报告,wordpress网站排名,模板免费下载网址文章目录 #x1f36a;一、引用的概念#x1f36a;二、引用的特性#x1f37f;1、引用在定义时必须初始化#x1f37f;2、一个变量可以有多个引用#x1f37f;3、引用一旦引用一个实体#xff0c;再不能引用其他实体 #x1f36a;三、常引用(被const 修饰的引用)#x… 文章目录 一、引用的概念二、引用的特性1、引用在定义时必须初始化2、一个变量可以有多个引用3、引用一旦引用一个实体再不能引用其他实体 三、常引用(被const 修饰的引用)1、权限的放大2、权限的平移3、权限的缩小4、临时变量具有常性 四、引用的使用场景1、做参数传引用传参2、做返回值引用做返回值3、传值、传引用效率比较 传值和传引用的作为参数的性能比较 传值和传引用的作为返回值的性能比较 4、传引用返回修改返回对象5、总结 五、引用和指针的区别 一、引用的概念 引用不是新定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。 比如李逵在家称为铁牛江湖上人称黑旋风 比如抓 “周树人” 和我 鲁迅有什么关系本质上其实是一个人 引用的操作符 类型 引用变量名(对象名) 引用实体 举个栗子演示
int main()
{int a 0;int b a; //引用cout a endl;cout b endl;return 0;
}☝️代码段中相当于给变量a取了一个别名b通过标识名b可以在其被定义的作用域中访问变量a可以看到地址是一样的说明a和b共用同一块内存空间
注意引用类型必须和引用实体是同种类型的
二、引用的特性
1、引用在定义时必须初始化
请看代码与注释
void TestRef()
{int a 10;// int ra; // 该条语句编译时会出错int ra a;cout a a endl;cout ra ra endl;
}int main()
{TestRef();return 0;
}int ra; 该条语句编译时会出错是不可以的必须要进行初始化 2、一个变量可以有多个引用 一个变量可以有多个引用并且引用可以嵌套定义 请看代码与注释
void TestRef()
{int a 10;// int ra; // 该条语句编译时会出错int ra a; // ra是a的引用int rra a; // rra是a的引用int rrra ra; // rrra是ra的引用cout a a endl;cout ra ra endl; cout rra rra endl;cout rrra rrra endl;
}int main()
{TestRef();return 0;
} 基于引用这种可以嵌套定义并且无需多次解引用就可以直接访问被引用变量的这种特性很多时候使用引用可以避免多级指针的出现 3、引用一旦引用一个实体再不能引用其他实体
举个栗子演示
void TestRef()
{int a 10;int b 20;int x a; int x b; cout a a endl;cout x x endl; cout b b endl;cout x x endl;
} 由于这个特性引用无法完全代替指针(比如链表中结构体的next指针无法用引用来代替因为引用一旦引用一个实体再不能引用其他实体)灵活性也不如指针但是引用也因此比指针更安全这也是引用这个语法的设计初衷之一(使用指针很容易出现野指针非法访问内存空间的情况) 三、常引用(被const 修饰的引用) 在引用的过程中权限可以平移权限可以缩小但是权限不能放大 1、权限的放大 假如a是鲁智深 可以喝酒 可以吃肉 不能杀人 给a取个别名b 叫花和尚 可以喝酒 可以吃肉 可以杀人 a鲁智深和 b花和尚是同一个人当然不可以杀人 举个栗子
int main()
{//权限的放大const int a 0;int b a;return 0;
}2、权限的平移
举个栗子
int main()
{//权限的平移const int a 0;const int b a;return 0;
}3、权限的缩小
举个栗子
int main()
{//权限的缩小int a 0; //a可以修改可以影响bconst int b a;return 0;
}a可以修改可以影响b 4、临时变量具有常性
举个栗子
int main()
{int i 0;double d i;//临时变量具有常性return 0;
}✅正确操作
int main()
{int i 0;const double d i;return 0;
}这里涉及一个知识点 代码段中b去引用ii会发生隐式类型转换i转换的结果会存入一个临时空间中。 (当赋值等号右边有运算表达式或有变量发生类型转换时表达式或类型转换的结果都会先存入一个临时空间后再赋值给等号左边的变量) 因此这里的d引用的实质上是一块临时空间 四、引用的使用场景
1、做参数传引用传参
举个栗子
void Swap(int left, int right)
{int temp left;left right;right temp;
}int main()
{int a 10;int b 20;Swap(a, b);cout a a endl;cout b b endl;return 0;
}2、做返回值引用做返回值
先看一下传值返回
//传值返回
int Count()
{int n 0;n;// ...return n;
}int main()
{int ret Count();cout ret endl;return 0;
}我们非常的熟悉结果为 1
再来看一下下面这段代码
//传引用返回
int Count()
{int n 0;n;// ...return n;
}int main()
{int ret Count();//这里打印的结果可能是1也可能是随机值cout ret endl;return 0;
}这里输出的结果有两种可能一种为 1 另一种可能为随机值原因是传引用返回返回的是n的别名但是这里存在的问题是我们返回n的别名也就是访问n这块空间访问n这块空间就有两个结果如果这个栈帧没有清除它的值就是1如果它的空间被清了那么它的就会是一个 随机值 看图比较一下两段代码
上面的理解了之后再看一段代码
int Count()
{int n 0;n;// ...return n;
}int main()
{int ret Count();//这里打印的结果可能是1也可能是随机值cout ret endl;cout ret endl; //被覆盖return 0;
}这是为什么呢 函数调用要先传参也就是先取值这个时候还没有建立栈帧取值之前还没有被覆盖传参过去之后建立栈帧值不会受到影响那第二次调用再去取值这时这个值已经被建立的栈帧覆盖了所以输出的是随机值 理解了之后再看一段代码
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;
}这样的函数不能使用引用返回是非常不安全的相当于野引用野指针的方式 注意如果函数返回时出了函数作用域如果返回对象还在(还没还给系统)则可以使用引用返回如果已经还给系统了则必须使用传值返回 3、传值、传引用效率比较 传值和传引用的作为参数的性能比较
#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;
}4、传引用返回修改返回对象
传引用返回还有一个隐藏作用
假如一个顺序表要进行读和修改数据
struct SeqList
{int* a;int size;int capacity;
};C的接口设计
//C的接口设计//读取第i个位置的值
int SLAT(struct SeqList* ps, int i)
{assert(i ps-size);//...return ps-a[i];
}
//修改第i个位置的值
void SLModify(struct SeqList* ps, int i, int x)
{assert(i ps-size);//...ps-a[i] x;
}CPP的接口设计
//CPP的接口设计//读 or 修改第i个位置的值
int SLAT(struct SeqList ps, int i)
{assert(i ps.size);//...return ps.a[i];
}int main()
{struct SeqList s;//...SLAT(s, 0) 10;//修改SLAT(s, 1) 20;SLAT(s, 2) 30;cout SLAT(s, 0) endl;//打印cout SLAT(s, 1) endl;cout SLAT(s, 2) endl;return 0;
}这里相比 C的接口设计来看非常的香减少了拷贝
5、总结
传引用传参任何时候都可以 1、提高效率 2、输出型参数形参的修改影响实参 传引用返回出了函数作用域对象还在才可以用 1、提高效率 2、修改返回对象 五、引用和指针的区别 在语法概念上引用就是一个别名没有独立空间和其引用实体共用同一块空间 在底层实现上实际是有空间的因为引用是按照指针方式来实现的 int main()
{int a 0;int* p1 a;int ref a;return 0;
}我们来看下引用和指针的汇编代码对比 可以看到引用和指针底层是一样的可以说是引用就是化了妆的指针
引用和指针的不同点 1. 引用概念上定义一个变量的别名指针存储一个变量地址。 2. 引用在定义时必须初始化指针没有要求 3. 引用在初始化时引用一个实体后就不能再引用其他实体而指针可以在任何时候指向任何一个同类型实体 4. 没有NULL引用但有NULL指针 5. 在sizeof中含义不同引用结果为引用类型的大小但指针始终是地址空间所占字节个数(32位平台下占4个字节) 6. 引用自加即引用的实体增加1指针自加即指针向后偏移一个类型的大小 7. 有多级指针但是没有多级引用 8. 访问实体方式不同指针需要显式解引用引用编译器自己处理 9. 引用比指针使用起来相对更安全 这期内容有一点难理解希望烙铁们能理解消化有所收获哦 总结 以上就是 【C】引用 的全部内容啦 本文章所在【C初阶】专栏感兴趣的烙铁可以订阅本专栏哦 前途很远也很暗但是不要怕不怕的人面前才有路。 小的会继续学习继续努力带来更好的作品 创作写文不易还多请各位大佬uu们多多支持哦