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

浙江省住房城乡建设厅网站定制网站的好处有哪些

浙江省住房城乡建设厅网站,定制网站的好处有哪些,c#网站开发框架,东莞网站制作有名 乐云践新文章目录一、前言二、宏的优缺点分析1、概念回顾2、宏的缺点3、宏的优点三、inline内联函数1、概念2、特性①#xff1a;空间换时间#x1f381;趣味杂谈#xff1a;庞大的游戏更新包3、特性②#xff1a;inline实现机制4、特性③#xff1a;inline的声明与定义反汇编观察… 文章目录一、前言二、宏的优缺点分析1、概念回顾2、宏的缺点3、宏的优点三、inline内联函数1、概念2、特性①空间换时间趣味杂谈庞大的游戏更新包3、特性②inline实现机制4、特性③inline的声明与定义反汇编观察函数调用的流程内联函数的生成机制【⭐】四、总结与提炼一、前言 本文我们聊一聊一下宏和内联函数之间的关系。 在预处理章节我们重点介绍了什么是宏以及宏和函数之间的区别还记得下面这张图吗我们对宏和函数之间做了一个很细致的分析那在本文中我们来说的就不是普通的函数了它叫做【内联函数】是C里面独有的由inline关键字进行修饰具体怎样敬请看下去吧 二、宏的优缺点分析 在将内联函数之间前我们再来聊聊有关宏的一些内容 1、概念回顾 下面是宏的申明方式 #define name( parament-list ) stuff //其中的 parament-list 是一个由逗号隔开的符号表它们可能出现在stuff中注① 参数列表的左括号必须与name紧邻如果两者之间有任何空白存在参数列表就会被解释为stuff的一部分。 在学习了C语言后如果我不带你去回顾这一些那你是否能立马写出一个宏来呢例如写一个用于求和的宏函数那我相信对于很多同学来说都会措手不及答案会像是下面这样千奇百怪 //1. #define Add(int x, int y) return x y; //2. #define ADD(x, y) x y //3. #define ADD(x, y) (x y) //4. #define ADD(x, y) ((x) (y)); //5. #define ADD(x, y) ((x) (y))那究竟哪一个对的呢首先第一个肯定是错误的因为我说了这是【宏】而不是函数这样子是函数的写法。 后面四个的主要区别就在于后面的stuff我们一一来分析一下 首先若是写成下面这样传入1给到x传入2给到y此时去我们去Linux环境下看看预处理之后会发生什么 int ret ADD(1, 2);可以看到很明显进行了一个【宏替换】 若此时我将调用的函数后面再乘上一个3呢这会发生什么 int ret ADD(1, 2) * 3;可以观察到此时在预处理阶段也是直接进行了一个替换不过仔细观察就可以发现由于*的优先级来得高所以2会和后面的3先进行一个运算这也就造成了最后结果的不同 所以我们应该要像下面这样在外层加上一个大括号防止出现优先级的问题 #define ADD(x, y) (x y)可是这样真的就可以了吗若此时我向ADD宏函数传入下面这样的参数呢会发生什么 int a 10; int b 20; int ret ADD(a | b, a b);编译器还是一样会去做傻傻的替换但是这个时候我们又得注意优先级的问题了对于号来说它的优先级高于按位与和|按位或的如果不清楚优先的话可以看看操作符汇总大全所以中间的【b】和【a】会先进行结合然后再去算和| 如果要防止这种表达式的传入而造成的优先级问题可以对内部的形参也加上一个括号()这样就不会出现问题了 #define ADD(x, y) ((x) (y));可是呢有的同学虽然想到了这一点但是却在最后面加了一个;号对于分号来说是我们在写代码的时候作为一条语句结束的标志但是对于宏来说也可以写分号吗将原先的传参继续替换成下面这样来试试 int ret ADD(1, 2) * 3;继续通过预处理之后的结果去进行观察就可以发现在进行宏替换之后原先的语句中出现了;号但是分号后面还有3要乘这个时候其实就不对了所以宏定义后是不可以加分号的 最后这一种才是最正确的写法 #define ADD(x, y) ((x) (y)) //✔2、宏的缺点 看了上面有关【宏】概念的一些回顾我们来聊聊它的缺点所在 ① 宏可能会带来运算符优先级的问题导致程容易出现错。【加括号太麻烦了】 这一点相信你在看了上一小节的叙述之后一定是感同身受只是完成一个两数相加的功能就需要加上这么多括号了若是再复杂一些的功能那岂不是要加很多了 ② 宏是不能调试的【这点比较致命】 可以观察到无论是在哪个平台下对于宏来说都是无法调试的 Windows环境下VS Linux环境下gdb ③ 没有类型安全的检查【直接替换】 可以看到无论我传入何种类型的参数都不会出现问题。这点其实说明了宏对于类型的检查是不严谨的 ④ 有些场景下非常复杂容易出错不容易掌握 之前我有写过一篇文章叫做使用宏将一个整数的二进制位的奇数位和偶数位交换。为了实现这个功能我写了一个这样的宏函数 #define SWAP(n) num (((n 0xaaaaaaaa) 1) | ((n 0x55555555) 1))乍眼一看就感觉非常复杂都是一些二进制的位运算若是没有对这一块很熟悉的话就很容易写错因此说对于一个宏来说其实要实现一些功能的时候会非常繁琐 3、宏的优点 了解了宏的缺点之后我们再来瞧瞧它的优点既然在一些场景被广泛使用那一定也具有它的优点 ① 宏常量提高复用性和可维护性 这一点很好理解也就是我们平常用得最多的一般会将频繁使用到的一些数字定义成一个宏这样就不需要每次写数字了直接写这个宏所定义的变量即可而且在修改的时候也会很方便只需要修改一下宏定义处即可实现一改多改【这一点在项目里面还是比较常用的】 #define n 500② 宏函数类型不固定int、double、float都可以用 这一点其实我们在讲缺点的时候也有提及虽然类型检查得不严谨但真正用起来还是很香的这很像是我们之前说到过的函数重载虽然函数名相同但是可以传入不同的参数实现不同的运算 ③ 宏函数不用建立栈帧因此不会产生消耗 刚才我们有观察过对于【宏】来说是不能调试的因为它根本就不是一个真正意义上的函数只是具备函数的功能罢了。既然不是函数的话也就不会在栈上建立函数栈帧那也就不会有过多的消耗 好以上就是本文所要讲的有关【宏】相关的所有内容希望能唤起读者对这个知识点的回忆 三、inline内联函数 接下去我们就正式来讲讲C中的独有的【内联函数】 1、概念 以inline修饰的函数叫做内联函数编译时C编译器会在调用内联函数的地方展开没有函数调用建立栈帧的开销内联函数提升程序运行的效率 C的祖师爷呢认为【宏】存在一定的缺陷但是呢也有它的好处也就是直接做替换不需要开辟函数栈帧所以在C中就出现了内敛函数这么一个东西 inline int Add(int x, int y) {int z x y;return z; }但是它真不会去进行函数调用吗我们到VS中来看看 那此时就又同学疑惑了这不还是会有call指令吗哪来的不调用一说呢因为我们还需要去做一些配置 然后我们再去观察一下汇编就可以发现不存在call指令了编译器直接将内敛函数Add做了展开 所以C中我们是不推荐用宏的因为有内联函数这个特性即保留了宏的优点无需调用函数建立栈帧而且还修复了宏的缺陷不再需要将内容写得那么复杂写成日常的函数形式即可只需要在前面加上一个inline关键字就可以起到这种效果。非但如此它还可以调试 接下去我们再来介绍一下有关内敛函数的一些特性 2、特性①空间换时间 有同学可能不是很理解空间换时间是什么意思可以看看时空复杂度章节。不过要说明的一点是这里的空间不是内存是编译后的程序而【空间换时间】就会使得编译出来的可执行程序会变大 对于这种空间换时间的做法如果编译器将函数当成内联函数处理在编译阶段会用函数体替换函数调用 【缺陷】可能会使目标文件变大【优势】少了调用开销提高程序运行效率 可以通过下面这幅图来看看此时程序中有一个swap()函数内部有10行代码。在程序的1000个地方都会对其进行调用那请问此时去使用内联函数和不使用内联函数的区别是什么 当不使用内联函数时就是将这个swap()函数当做普通的函数的话我们知道在底层会进行一个call指令再通过jump指令跳转到这个函数所在地址然后执行这个函数那每一个调用的地方都要跳转的话就会跳转1000次所以swap 调用swap指令合计是1000 10条指令当使用内联函数时通过上面的学习可以知道它不会去进行一个函数的调用而是直接将相关的指令拷贝到程序中调用这个swap()函数的每一块地方所以swap 调用swap指令合计是1000 * 10条指令 可以看出在使用内联函数减少函数调用的同时也会增加程序的负担使得目标文件会变得很大 趣味杂谈庞大的游戏更新包 在理解了一个函数设不设置内联函数的区别的时候顺带来谈一谈这个目标文件体积变大的缘故 要知道干我们这一行基本一年到头都是在公司也有逢年过节才能回家一趟而且平常工作忙得也没有时间玩游戏那此刻当你过年回家的时候就看到家里的一些弟弟妹妹拿着手机在打《王者荣耀》这相信是最正常不过了那此时呢你也心里痒痒打开了你尘封已久的王者荣耀然后看到要更新而且一看就是4、5个G突然之间就不想玩了更新的过程中只能眼巴巴地看着别人玩不过呢你有个同事也是很久都没有玩过游戏了也是一样进去更新但是呢她却3、5分钟更新好了因为更新包只有3、500M。这是为什么呢原来她是苹果手机系统是IOS系统其实对于更新的这些内容都是指令只是你们的指令数目不同而已。那我们都知道【苹果】这家公司系统是自己设计的而且对于像芯片、硬件、软件各方面都领先于同行所以说这个性能不一样就会手机系统上使用的软件也存在差异。别人这个性能好在优化各种方面都齐全所以就不会导致这个更新包越变越大相反对比我们平常使用的几千元的安卓手机却越用越卡这也是很多人选择使用苹果手机的原因 3、特性②inline实现机制 好小插曲我们回归正题继续来讲讲内联函数的第二大特性 —— 【inline实现机制】 inline对于编译器而言只是一个建议不同编译器关于inline实现机制可能不同一般建议将函数规模较小(即函数不是很长具体没有准确的说法取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰否则编译器会忽略inline特性 《Cprime》第五版关于inline的建议 我们可以到VS中来观察一下 可以观察到当内联函数的内容增多时程序中去调用这个内联函数依旧会执行call指令而不是将其函数中的内容直接拷贝到程序调用处即使是加了这个inline关键字似乎还是起不到内联函数的作用这是为什么呢 因为编译器在对于这个内联请求的时候发觉不对劲所以选择忽略了这个请求而不是像宏那样无论怎样都会傻傻地进行替换 这其实就是内联函数在替代宏之后很优秀的一个特性假设说现在你这个设置的内联函数有1000多行代码在一个大项目中又有1000个地方调用了这个内联函数。 如果不采用将其展开去调用的话消耗的顶多也就是1000 1000条指令如果采用内联将其展开的话消耗的就是1000 * 1000条指令这就很恐怖了 那上面这个还是个普通的大一点的函数但你再想如果是**··**呢层层地往下调用再一层层地返回来那需要调用的指令就更多了如果全部站开的话就会造成一个灾难性的后果⚠ 所以呢加不加内敛是你的事最终要不要把它真的展开变成内敛编译器说了算所以我们在使用的时候一般是比较短小、调用频繁的函数10几行以内加上内联其他就不加了 4、特性③inline的声明与定义 inline内联函数的第三个特性就是我们要注意内联函数的定义和声明不可以分开导致链接错误。因为inline被展开就没有函数地址了链接就会找不到 // F.h #include iostream using namespace std;inline void f(int i); //-------------------------- // F.cpp #include F.hvoid f(int i) {cout i endl; } //-------------------------- // main.cpp #include F.hint main() {f(10);return 0; }通过去VS中执行这段分文件代码可以发现它不是一个【编译时错误】而是一个【运行时错误】 这种错误一般都是最后链接目标文件的时候除了问题不是很清楚的可以看看程序的编译和链接 对于这些很复杂的符号funcYAXHZ是C中的函数名修饰可以看看探究函数重载的原理函数名修饰 那我们现在就要去思考为什么会出现这种【运行时错误】呢 最主要的一点首先你要知道内联函数的不进符号表的因为对于内联函数来说它在其调用的地方都展开了所以不需要产生一串指令把它放到符号表中再通过一条一条指令去调用 反汇编观察函数调用的流程 对于内联函数来说是这样但是普通函数来说是怎么一步步地进行调用的呢我们可以通过汇编来看看 对于函数的地址来说在指针章节我们有说过它值得就是【函数指针】但是从汇编角度来看它call的到底是谁的地址呢 通过按下F11跳转到了一条叫做【jmp】的指令此时你去观察这个地址其实就是这条【jmp】的地址在英语中jump是跳的意思那你可以将这条指令理解为一个中转站call指令会暂时跳到这条指令所在的地址然后再通过这条指令去找需要调用函数的地址 此刻再按下F10的话就会通过这个函数的地址找到这个函数此时就开始执行函数内部的逻辑 然后当执行完函数内部的逻辑后便回到了call指令的下一条指令这就算是一次函数调用的过程 可以将它们放到一起来观察就可以发现它们之间的逻辑非常紧密 如果对上面过程还是不太理解的可以看看反汇编深挖【函数栈帧】的创建和销毁 内联函数的生成机制【⭐】 上面是对于普通函数的调用机制那对于内联函数呢也是这么去跳转吗 一定要注意内联内联那么这个函数的内容就直接放到调用的地方 它便说也就是我不需要你再通过这么繁琐的步骤一步步地跳转过来了我会将内部的东西做一些优化直接放到你那里你执行这些指令即可 可是呢为什么会出现链接错误❌ 因为在【预编译】的时候就要展开func.h这个头文件但是在主调用接口中包含的头文件中只有函数的声明没有实现此时只能在【链接】的时候展开了但是在链接的时候因为只有声明所以只得到了函数名修饰后的地址。编译器便需要通过这个地址找到函数所在的位置对于普通函数而言在这个时候就可以通过call找过去了但是对于内联函数而言却无法做到因为它并没有【call】和【jmp】这些指令因此就造成了链接错误的现象 那要如何去解决呢 若是这个函数要定义成内联函数的话就不要将定义和声明分开了在头文件中定义出来后就直接对其进行声明便不会造成这样的问题了 // F.h #include iostream using namespace std;inline void f(int i) {cout i endl; } //-------------------------- // main.cpp #include F.hint main() {f(10);return 0; }四、总结与提炼 好最后来总结一下本文所学习的内容 首先我们回顾来一下C语言中所学习的【宏】经过了对宏的优缺点分析以及一些同学的错误案例对照在面试的时候让你写一个宏可不要写错了哦接下去我们就正式地开始介绍内联函数对于内联函数来说它不仅保留了宏的优点没有函数调用建立栈帧的开销而且还修复了宏的缺点将其做成一个函数的形式简洁直观而且便于调试观察提升程序运行的效率但是对于内联函数来说也是存在要注意的地方因为它也是会和宏一样在调用的地方展开不过会进行一定程度的优化可这种空间换时间的思想只适用于小型的函数对于大型的函数不建议定义成【内联函数】会造成程序的过多臃肿但是在看了内联函数的生成机制后其实我们也不用担心在误用内联函数后使得程序变大它会有一个自动判断的机制若是你程序的行数过多的话编译器就会忽略你的这请求对于我们要将一个函数声明为内联函数其实是在向编译器发起一个申请它可以选择接收也可以选择拒绝‍ 以上就是本文要介绍的所有内容感谢您的阅读
http://www.hkea.cn/news/14316432/

相关文章:

  • 秀山网站建设端午节手抄报获奖建设科技期刊官网
  • 前端如何做能切换语言的网站有赞商城网页版
  • 网站建设500错误代码养殖公司网站
  • 网站专业销售团队介绍网站建设有哪些问题
  • 做网站猫要做端口映射吗wordpress 有赞云
  • 网站无法下载视频 怎么做搭建网站基本步骤
  • 做办公家具在哪个网站推销好商城网站制作报价
  • 淘宝优惠劵做网站模版网站建设没有图片
  • 网站图片缩略图在线支付的网站怎么做
  • 网站的标签怎么修改企业网站建设一般考虑哪些因素
  • 网站公司建设个服务号多少钱网站域名在哪里看
  • 颍东网站建设福建省住房和城乡建设厅网站首页
  • 工业设计网站哪家好给网站做外链要注意哪些
  • 怎么建设域名和网站专做美妆的视频网站
  • 网站的功能定位在线绘画网站
  • 同泰公司网站公司查询WordPress对象储存
  • 网站标题怎么做公众号涨粉
  • 山东省建设管理信息网站汝州网站建设
  • wordpress网站的配置文件天津做美缝的网站
  • 服装门户系统网站环保工程 技术支持 东莞网站建设
  • 关键词查询工具软件酒泉网站seo
  • ps做图下载网站有哪些网站建设 公司新闻
  • 建站公司哪家好网站开发的母的目的和意义.
  • 网站建设推广合同书专业精准网络营销推广
  • 健康资讯网站模板网站要什么软件做
  • 山东超越建设集团网站南昌网站建设
  • 深圳建企业网站什么叫网站建设四网合一
  • 可视化网站建设软件有哪些遵义网站建设90kj
  • 青岛黄岛区网站开发零用贷网站如何做
  • 黄骅贴吧招聘2022年seo sem论坛