如何建立公司网站是什么,h5网站需要哪些技术,中科宁波网站建设,wordpress主题首页文件上期回顾 在学习完函数重载之后#xff0c;我们可以使用多个重名函数进行操作#xff0c;会发现C真的是弥补了好多C语言的不足之处#xff0c;真的不禁感概一下#xff0c;时代的进步是需要人去做出改变的#xff0c;而不是一味的使用啊#xff01;所以我们今天继续学一下…上期回顾 在学习完函数重载之后我们可以使用多个重名函数进行操作会发现C真的是弥补了好多C语言的不足之处真的不禁感概一下时代的进步是需要人去做出改变的而不是一味的使用啊所以我们今天继续学一下C对C语言的指针的改变吧 一、引用的诞生 在C语言中指针的使用是很复杂的涉及了二级指针三级指针乃至我们很少见的多级指针这会让我们使用起来很麻烦程序的可读性很差如果你不是一个功底很深的程序员根本就要花上很长时间才会略知一二。 但是我们在C中并不是摒弃了指针而是发明了一个新的东西在某些场合可以代替指针----引用 二、引用的概念 那什么是引用呢 我们语文中的引用是不是给某个东西起个别名然后再用双引号引起来。在现实生活中我们会有很多别名比如李逵在家叫“铁牛”江湖人称“黑旋风”。这都是引用所以我们C中的引用也不例外就是给一个变量起别名。 我们来一起思考一个问题C中的引用会开辟一个新的内存空间吗因为在C语言中指针会开辟空间的。 我们还是以现实生活为例你有很多别名那就有很多个你吗肯定不是吧 所以我们C的引用也是只有一个空间的我们只是给变量起了个别名但是引用是跟它引用的变量共用一块内存空间的。 概念引用不是新定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。 我们了解了概念之后那引用是如何使用的呢
三、引用的使用 数据类型 引用变量名 引用实体 注意引用的数据类型要和引用实体数据类型一致不一致的情况我们放在常引用讲解 #include iostream
using namespace std;
int main()
{int a 3;inta1 a;cout a a endl a1 a1 endl;return 0;
} 看到下面的输出结果也印证了引用是给变量取别名 四、引用的特性
4.1 引用必须初始化 如果我们写了这样的一段代码是编译不过去的会报错正是因为引用没有初始化出现的错误。我们可以这样理解引用实体都没有哪里来的引用呢就像一个人根本不存在他就不可能有别名的。 #include iostream
using namespace std;
int main()
{int a 3;inta1;return 0;
}
4.2 引用的改变 会 改变引用实体 那如果是这样的代码呢我们在此基础上➕了一行a1改变引用会改变引用实体吗 #include iostream
using namespace std;
int main()
{int a 3;inta1 a;a1;cout a a endl a1 a1 endl;return 0;
} 答案是一定的因为引用跟引用实体共用同一个空间改变引用就是改变引用实体。 4.3 引用不改变指向也不可以同时引用多个实体 那一个引用可以引用多个实体吗或者可以改变引用指向的对象吗因为我们C语言的指针可以改变指向所以我们来探讨一下比如下面这段代码 #include iostream
using namespace std;
int main()
{int a 3;inta1 a;int b 5;a1 b;cout a a endl a1 a1 endl;cout a的地址为 a endl;cout a1的地址为 a1 endl;cout b的地址为 b endl;return 0;
} 我们可以看到虽然把b的值赋给了a1但是没有改变引用的指向a1的地址还是跟a一样的引用跟指针的区别之一也显现出来了所以我们可以得出下面两个结论 1. 引用不可以引用多个对象 2. 引用不可以改变指向指向的对象 4.4 一个实体可以有多个引用 在日常生活中你可以有很多个别名所以在C中一个实体也可以有多个引用。 #include iostream
using namespace std;
int main()
{int a 3;int a1 a;int a2 a;int a3 a;cout a a endl;cout a1 a1 endl;cout a2 a2 endl;cout a3 a3 endl;return 0;
} 因为一个实体可以有多个引用所以这个输出就一定都会是3的。 我相信大家一定会有这样的疑问我们上面所做的事情C语言的指针也可以做呀没什么特别的嘛大家不要着急继续看下去引用使用的地方并不是在这里哦 我们可以发现目前的引用都是在引用变量那是否可以引用常量或者是常变量呢接下来是我们要讲的重点之一
五、常引用
在学习之前我们要先知道这个概念 权限一般是指我们可以操作的范围使用的范围权限只可以平移和缩小不可以放大 了解之后我们就开始对常引用的学习吧 那什么是常引用呢就是对常量和常变量的引用但是一定要记住权限来看下面的代码:
#include iostream
using namespace std;
int main()
{const int a 3;int a1 a;return 0;
} 这段代码可以正常运行吗 我们先来分析一下在程序中我们定义了一个常变量aa本来是变量但是被const修饰了变得不可被修改所以权限是 “只读”然而我们用int类型的引用来去引用a是不可以的因为我们的引用变量a1是int类型的权限是“读和写”可以被修改。所以这里的权限是被放大了a1可以修改a不可以修改权限被放大该程序错误 那怎么弄才是对的呢还是得看权限只能平移和缩小所以我们更改了代码
#include iostream
using namespace std;
int main()
{const int a 3;const int a1 a;return 0;
} 这段代码中a是常变量引用变量a1也是常变量这是权限的平移程序正确。 那有没有权限缩小的呢当然有来看下面代码
#include iostream
using namespace std;
int main()
{int a 3;const int a1 a;return 0;
} 这段代码就是a本来可以修改是“读写”然后我们的引用变量a1是➕const修饰的权限是“只读”这里就是权限的缩小程序正确。 以上都是对常变量的引用那如何来引用常量呢 猜对了还是靠权限来 #include iostream
using namespace std;
int main()
{const int a1 3;return 0;
} 我们的常量也是不可被修改的权限是“只读”所以要引用常量的时候要加上const把权限也变成“只读” 六、引用和引用实体的数据类型不同 当我们了解了这些之后我们要填一下上面埋的坑当引用和引用实体的数据类型不一样的时候如何引用呢 我们要了解一个概念当类型不同的时候一定会发生类型转换比如下面的代码
#include stdio.h
int main()
{int a 3;double b a;printf(a %d\nb %lf,a,b);return 0;
}
我们先定义了一个整形变量a再将a变量赋值给double类型的b两个类型不同会发生隐式类型转换就是把a转换成double类型再赋值给b那我们的a的数据类型是什么呢 从上面可以看出a还是整型但是不是把a转换成double之后再赋值给b的吗怎么a还是int类型我们带着这样的疑问去学习一个新的知识
凡是涉及到类型的转换都会产生一个临时变量存放转换的值。这个临时变量具有常属性
这也就解释了为什么a还是int类型。
我们用图解的方法来看一下 我们在了解上面的概念之后看下面的代码
#include iostream
using namespace std;
int main()
{int a 3;const double b a;return 0;
} 我们定义了一个int类型的变量a用double类型的引用变量去引用a因为类型不同会发生类型转换但本质是先创建一个double类型的中间变量再把a的值转换到这个中间变量中由这个中间变量赋值给我们的b为什么要➕const因为这个中间变量具有常属性。 七、引用的使用场景
其实在上面我们仅仅是在介绍引用该怎么使用没有介绍引用都用在哪里所以接下来我们来学习引用使用场景。
7.1 做函数参数 我们在C语言中写一个交换函数是需要传地址的也就是用到一级指针 void Swap(int* x,int* y)
{int tmp *x;*x *y;*y tmp;
}
而在C中我们可以用引用来解决这个问题 因为引用就是引用实体的别名所以改变引用就是改变引用实体。 void Swap(int x,int y)
{int tmp x;x y;y tmp;
}
7.2 做返回值 第二个用途就是用引用当返回值 但是有一个条件函数返用引用作为返回值的时候返回值不可以被销毁 下面的例子主要是讲解我们用引用做返回值的时候返回值不可以被销毁。
我们先看这样的一段代码 我们是定义了一个函数Count里面实现的是简单的nn最后是1然后将n的值返回可是n是函数里面创建的一个临时变量啊临时变量出了作用域就销毁了 而我们函数的返回值是n的引用引用实体销毁了引用还能正常使用吗肯定不能的对吧但是这里要分两种情况ret可能是1也可能是随机值这主要看编辑器销毁的速度。 int Count()
{int n 0;n;// ...return n;
}
using namespace std;
int main()
{int ret Count();cout ret endl;return 0;
} 如果我们是下面的代码呢打印出来的结果又是什么呢 很神奇吧一个是3可以理解有可能是z没有被销毁但是后面怎么是10了呢 这是因为我们函数栈帧是可以复用的所以我们的那个空间本来是3后来虽然被销毁了又被征用了变成10了。而我们的引用还是指向那个位置的所以就变成10了但是也可能是随机值。 int Add(int x,int y)
{int z x y;return z;
}
using namespace std;
int main()
{int ret Add(1,2);cout ret endl;Add(3,7);cout ret endl;return 0;
} 但是有没有什么办法来解决这样的问题呢也就是保证返回值不被销毁。 我们可以定义一个静态变量就可以了因为静态变量是在堆上开辟的函数的销毁不会影响静态变量。 需要注意的一点就是局部的静态变量只可以初始化一次 多次进入这个函数只会执行一次初始化剩下的都跳过 这样我们就可以正确的返回z的引用了 int Add(int x,int y)
{static int z x y;return z;
}
using namespace std;
int main()
{int ret Add(1,2);cout ret endl;return 0;
}
所以我们的我们函数返回值要是引用的话返回值不呢被销毁
八、传值和传引用的效率比较 以值作为参数或者返回值类型在传参和返回期间函数不会直接传递实参或者将变量本身直接返回而是传递实参或者返回变量的一份临时的拷贝因此用值作为参数或者返回值类型效率是非常低下的尤其是当参数或者返回值类型非常大时效率就更低。 8.1 函数参数 用 引用和值 的效率比较
#include time.h
#include iostream
using namespace std;
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;
} 我们可以看到当数据过大的时候函数参数用引用的效率很快 8.1 函数返回值用引用和值的效率比较
#include iostream
#include time.h
using namespace std;
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;
} 当数据过大的时候函数返回值用引用的效率更快 通过上述代码的比较发现传值和指针在作为传参以及返回值类型上效率相差很大。这就是引用的优势不需要开辟临时空间。
九、引用和指针的区别 1. 引用概念上定义一个变量的别名指针存储一个变量地址。 2. 引用在定义时必须初始化指针没有要求 3. 引用在初始化时引用一个实体后就不能再引用其他实体而指针可以在任何时候指向任何一个同类型实体 4. 没有NULL引用但有NULL指针 5. 在sizeof中含义不同引用结果为引用类型的大小但指针始终是地址空间所占字节个数(32位平台下占4个字节) 6. 引用自加即引用的实体增加1指针自加即指针向后偏移一个类型的大小 7. 有多级指针但是没有多级引用 8. 访问实体方式不同指针需要显式解引用引用编译器自己处理 9. 引用比指针使用起来相对更安全 10. 空指针没有任何指向删除无害引用是别名删除引用就删除真实对象