当前位置: 首页 > wzjs >正文

上海网站建设专业公司排名青岛网站建设方案服务

上海网站建设专业公司排名,青岛网站建设方案服务,即墨网站建设公司,做网站后台系统的规范目录 一、缺省参数 1.1 全缺省参数 1.2 半缺省参数 1.3 声明和定义分离后的缺省参数 1.4 编译器对程序的处理 二、函数重载 2.1 函数重载的定义 2.2 支持函数重载的原理 三、引用 3.1 引用的概念 3.2 引用的特性 3.3 引用的应用价值 3.3.1 做参数 3.3.2 做返回值 …

 

目录

一、缺省参数

1.1 全缺省参数

 1.2 半缺省参数

1.3 声明和定义分离后的缺省参数

1.4  编译器对程序的处理

二、函数重载

2.1 函数重载的定义

 2.2 支持函数重载的原理

 三、引用

3.1 引用的概念

3.2 引用的特性

3.3 引用的应用价值

3.3.1 做参数

3.3.2 做返回值

3.4 引用和指针的区别 


一、缺省参数

        缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。

#include<iostream>
using namespace std:void Func(int a = 0)
{cout << a << endl;
}int main()
{Func(1); //传参1,调用函数,a的值为1Func();  //不传参也可以调用,调用以后,a的值就是0return 0;
}

        缺省参数分为全缺省和半缺省两类:

1.1 全缺省参数

  1. 函数定义时所有参数都分别指定了缺省值。
  2. 调用时可以不用传递任何参数,调用函数时采用缺省值。
#include<iostream>
using namespace std;void Func(int a = 10, int b = 20, int c = 30)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl;
}
int main()
{Func(1, 2, 3);Func(1, 2);Func(1);Func();return 0;
}

 1.2 半缺省参数

        缺省部分参数,并且缺省值必须从右往左连续给,缺省参数不能跳跃给。

        例如:void Func(int a, int b = 20, int c);是错误的写法。

#include<iostream>
using namespace std;void Func(int a, int b = 20, int c = 30)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;
}
int main()
{Func(1, 2, 3);Func(1, 2);Func(1);return 0;
}

        值得注意的是:函数Func合计起来有4种调用方式,例如,传递两个参数,在调用Func时,就指定给了前两个参数,不能跳跃传递。

1.3 声明和定义分离后的缺省参数

        在实践写程序的过程中,往往需要定义和声明分离,那么,如果定义和声明分离的话,在使用缺省参数的时候,能不能同时给呢?

        C++规定,定义和声明分离时不能同时是缺省参数,否则会出现重定义默认参数应该在声明的时候给缺省参数,定义的时候不给缺省参数。例如:

//Stack.h
#include<stdlib.h>struct Stack
{//...int* a;int size;int capacity;
};void StackInit(struct Stack* ps, int n = 4);
void StackInit(struct Stack* ps, int x);//Test.cpp
#include"Stack.h"
int main()
{struct Stack st1;StackInit(&st1, 100);struct Stack st3;StackInit(&st3);struct Queue q;return 0;
}

1.4  编译器对程序的处理

         编译器在预处理阶段,.h文件会被展开。当需要使用函数的时候,就在全局域找到了对应函数的声明。此时,需要了解函数调用的定义和声明的本质,例如StackInit(&st1, 100);。

        函数调用会在编译时,在底层形成一个汇编指令如call StackInit(0x00112233);。而此处没有函数的地址,因为这里只有声明,为call StackInit(?)那在什么时候会去找地址呢

  • 我们知道,编译器编译时有几个阶段:预处理编译(检查语法并搜索)汇编链接
  • call StackInit(?)会在链接的时候,通过名字StackInit去找地址并把(?)处填上。
  • 在整个编译阶段,只有声明,程序是能通过的。检查语法并搜索,在main函数中用了struct Stack的类型,编译器先在局部搜索,后转向全局搜索,能搜索的到。相反的,struct Queue的类型是搜索不到的。
  • 此外,StackInit也会进入全局搜索,只有声明是可以的,参数传递也是匹配的。
  • 调用StackInit(&st3);时,在语法检查阶段,声明都对应不上,所以会报错。
  • 假如声明的时候不给缺省参数,此时为void StackInit(struct Stack* ps, int n);,编译的时候发现,调用StackInit(&st3)函数的时候发现不匹配,所以就报错了。
  • 这就证明了,缺省参数只能在声明的时候给。调用StackInit(&st3)函数时,传递一个参数,另一个参数使用缺省值,相当于n传了参数4,类似于StackInit(&st3, 4)。

        声明和定义分离也体现了:声明就是函数的原型,有函数名、参数、类型、返回值。在编译阶段,通过声明就能够做到检查函数名、参数、类型、返回值能不能一一对应,合乎语法,编译就能通过。举个例子,例如AB同时做任务,A直接把任务做完上传,直接做完就是没有声明的过程,而直接定义;B答应这周做好,答应这周做好就是一个声明。(声明是一种承诺,定义是一种兑现)

        汇编代码是指令,函数本质也是多条指令的集合,所以函数的地址是这些指令的第一句指令的地址,类似于数组的地址。所以,调用函数的本质就是,call这个函数的地址,然后跳到这个地址指向的地方去,找到这些指令,然后把这些指令依次取给CPU,去依次执行,就完成了函数的功能。

声明和定义分离后,程序就分成了3个文件Stack.h、Stack.cpp、Test.cpp:

  1. 预处理阶段,要做的事情:展开头文件、宏替换、条件编译、去掉注释,此时变2个文件了,即Stack.cpp、Test.cpp分别生成Stack.i、Test.i。
  2. 编译阶段:检查语法->生成汇编代码 Stack.i、Test.i分别生成Stack.s、Test.s,之间是独立的。
  3. 汇编阶段:把汇编代码转成二进制机器码Stack.s、Test.s分别生成Stack.o、Test.o。
  4. 链接阶段:把Stack.o、Test.o合并到一起,有些地方得用函数名去其他文件中找函数地址。所以链接错误经常会报:LNK2019:.....,此时就要看声明所对应的函数定义阶段了,此时也可以说,有函数的定义采用函数的地址。

二、函数重载

2.1 函数重载的定义

        函数重载是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(1.参数个数或2.类型或3.类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

#include<iostream>
using namespace std;void f(int a, char b)
{cout << "f(int a, char b)" << endl;
}void f(char a, int b)
{cout << "f(char a, int b)" << endl;
}int main()
{f(10, 'a');f('a', 10);return 0;
}

 2.2 支持函数重载的原理

        C语言不支持重载,链接时,直接用函数名去找地址。如果函数同名,编译器就不知道到底要找哪个函数。为什么C++支持函数重载?

        C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,名字修饰产生唯一内部名称,是支持重载的关键。

        名称修饰是编译器在编译源代码时为函数、类等名称添加额外信息的过程,生成内部链接名称。该内部链接名称包含原名称以及其他信息,如参数类型、返回类型等。以Linux下C++编译器编译为例,可能生成的内部名称为:

_Z3Addii
int Add(int left, int right)
{cout << "int Add(int left, int right)" << endl;return left + right;
}
///
_Z3Adddd(Linux)
double Add(double left, double right)
{cout << "double Add(double left, double right)" << endl;return left + right;
}

其中:_Z表示前缀,3是(Add)字节长度,i i - 参数为int int,d d - 参数为double double

        可以通过Linux下观察C和C++编译器编译后结果:

        1. 采用C语言编译器编译后结果

        2. 采用C语言编译器编译后结果

 三、引用

3.1 引用的概念

        引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

类型& 引用变量名(对象名) = 引用实体;
#include<iostream>
using namespace std;int main()
{int a = 0;int& b = a;cout << &a << endl;//00CFFA68cout << &b << endl;//00CFFA68a++;b++;int& c = a;int& d = c;d++;return 0;
}

        C++中,复用了&符号。放在1个变量前是取地址,放在两个变量中间是按位与,放在类型名后变量前是引用。

        由上述程序中,可以知道,b就是a的别名,如果有需要,别名可以取多个,别名可以取别名,此时b、c、d都是a的别名。

3.2 引用的特性

        C++规定,引用的3条特性:

  1. 引用必须初始化;
  2. 引用定义后,不能改变指向;
  3. 一个变量可以有多个引用。
int main()
{int a = 0;//&a == 0x00f9f9d0	//1.引用必须初始化//int& b;//err//2.引用定义后,不能改变指向int& b = a;//&b == 0x00f9f9d0int c = 2;//&c == 0x00f9f9b8b = c;//不是改变指向,而是赋值,此时a == 2//3.一个变量可以有多个引用int& d = b;return 0;
}

3.3 引用的应用价值

3.3.1 做参数

#include<iostream>
using namespace std;void Swap(int* a, int* b)//传地址
{//...
}
void Swap(int& a, int& b)//代表a是x的别名,b是y的别名
{//...//此时的交换就是x和y的交换int tmp = a;a = b;b = tmp;
}
int main()
{int x = 0, y = 1;//Swap(&x, &y);Swap(x, y);return 0;
}

引用做参数有以下两个特点:

  1. 输出型参数 - 改变形参会影响实参,不仅仅是用的,而且要修改这个变量,让外边也拿到这个修改后的值。相对于输入型参数 - 给一个参数就是直接用的。
  2. 传参对象比较大时,可以减少拷贝,提高效率。

        有了这些效果,指针也可以,但是感觉上引用更方便,那么C++中,引用能不能替代指针?

        指针和引用的功能是类似的。指针的作用就是指向找到并改变,引用也同样可以找到改变。C++的引用,对指针使用比较复杂的场景进行一些替换,让代码更简单易懂,但是不能完全替代指针。引用不能完全替代指针的原因就是:引用定义后,不能改变指向。如:假设三个节点的链表删除中间节点,就不能用引用替代指针。

typedef struct Node
{struct Node* next;struct Node* prev;int val;
}LNode, *PNode;
//把struct Node重命名为LNode,把struct Node* 重命名为PNodevoid PushBack(struct Node* phead, int x)
{phead = newnode;
}void PushBack(struct Node*& phead, int x)
{phead = newnode;
}int main()
{PNode plist = NULL;return 0;
}

3.3.2 做返回值

#include<iostream>
using namespace std;int func()
{int a = 0;return a;
}//编译器肯定不会用a做返回值,所以在出func函数作用域的时候,会产生一个临时变量,会把a给临时变量
//如果a比较小,会存在寄存器中int main()
{    int ret = func();cout << "ret = " << ret << endl;return 0;
}

        这里的返回值不是a,因为a在func函数中,当调用func函数结束后,a会销毁,用a作为返回值可能导致ret不是0而是随机值。我们可以通过程序实现传引用返回,返回a的别名。

#include<iostream>
using namespace std;int& func()
{int a = 0;return a;//返回a的引用
}int main()
{    int ret = func();cout << "ret = " << ret << endl;return 0;
}

        返回的是a的引用,ret就是a的别名的别名,此时意味着ret==a,野引用。

 证明1:

#include<iostream>
using namespace std;int& func()
{int a = 0;return a;//返回a的引用
}void fx()
{int b = 1;
}int main()
{int ret = func();cout << ret << endl;//0fx();cout << ret << endl;//随机值return 0;
}

 证明2:

#include<iostream>
using namespace std;int& func()
{int a = 0;return a;
}
int& fx()
{int b = 1;return b;
}int main()
{int& ret = func();cout << ret << endl;//0fx();cout << ret << endl;//1return 0;
}

 证明3:

#include<iostream>
using namespace std;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;
}

        其原理图为:

 

        基于此,可以得出以下结论:返回的变量出了函数的作用域就生命周期到了要销毁(局部变量),不能用引用返回。

        什么情况下可以用引用返回? - 哪些变量出了作用域不销毁呢?

        全局变量、static静态变量、malloc堆上变量等可以用引用返回。即如下代码:

#include<iostream>
using namespace std;int& func()
{static int a = 0;return a;
}int main()
{int ret = func();cout << ret << endl;return 0;
}

3.4 引用和指针的区别 

语法的角度上:

  1. 引用是别名,不开空间;指针是地址,需要开空间存地址;
  2. 引用必须初始化,指针可以初始化也可以不初始化;
  3. 引用不能改变指向,指针可以;
  4. 引用相对更安全,没有空引用。但是有空指针,容易出现野指针,但是不容易出现野引用;
  5. sizeof计算大小、++、解引用访问等方面的区别。引用结果为引用类型的大小,指针始终是地址空间所占字节个数;
  6. 有多级指针,但是没有多级引用。

底层的角度上:

  • 汇编层面上,没有引用,都是指针,引用编译后也转换成指针了。
#include<iostream>
using namespace std;int main()
{int a = 10;int& ra = a;//语法上不开空间,底层开空间ra = 20;int* pa = &a;//语法上开空间,底层开空间*pa = 20;return 0;
}

        综上:1.引用底层是用指针实现的;2.语法含义和底层实现是背离的。

http://www.dtcms.com/wzjs/167921.html

相关文章:

  • 网站建设制作汕头足球世界排名
  • 北京网站开发公司哪家好百度快照客服
  • 企业平台网站建设营销方法有哪些方式
  • 2021免费正能量网站培训机构招生方案
  • 网站建设7重庆seo招聘
  • 网页制作步骤是什么win优化大师
  • 做外贸 访问国外网站淄博网站推广
  • 响应式网站设计制作嘉兴seo外包
  • 织梦教育咨询企业网站模板推广链接点击器网页
  • 万州做网站多少钱微信公众号推广方法有哪些
  • 阿里云做的海外网站怎么样谷歌搜索引擎
  • 如何建网站平台营销的四种方式
  • 洛阳网站建设哪家好企业网站制作费用
  • 网站建设案列抖音关键词排名优化软件
  • 佳作哪个公司做网站比较好汕头seo网络推广
  • 多语言外贸网站建设网站提交链接入口
  • 办网多少钱郑州seo技术代理
  • 做变形记图网站百度手机
  • 自己做网站的难度泉州百度开户
  • java cms建站全网搜索软件
  • 做网站后台怎么弄深圳网站建设三把火科技
  • 做qq图片的网站吗百度人工服务
  • 网站建设服务谁便宜宁波 seo排名公司
  • 网站动态网页不利于seo百度一下 你就知道官网 新闻
  • 济南专业做网站的公司哪家好如何快速优化网站排名
  • 可靠的上海网站建设百度投放广告联系谁
  • 如何自建网站入口口碑营销
  • 免费推广的手段和方法企业网站seo公司
  • 学ui哪家培训机构好广州seo优化
  • 江苏省住房和城乡建设部网站模板建网站价格