3 函数的升级-上

news2024/11/28 11:31:44

常量与宏回顾

C++中的const常量可以替代常数定义,如:
"Const int a = 8; --> 等价于 #define a 8 " 宏在预编译阶段处理,而c++ const常量则在编译阶段处理,比宏 更为安全。
C中,我们可以用宏代码片段去实现某个函数,容易出现副作用。在C++中,是否有解决方案替代代码宏,消除宏的副作用?

  • C++ 推荐使用内联函数替代宏代码片段,关键字 inline
  • 内联函数声明时 inline 关键字必须和函数定义结合在一起,否则编译器会直接忽略内联请求。
inline int func(int a, int b)
{
	return a < b ? a : b;
}
//----------------------------
//下面这段代码内联请求会被编译器忽略
inline int func(int a, int b);
int func(int a, int b)
{
	return a < b ? a : b;
}

内联函数在最终生成的可执行文件中,是没有定义的,因为C++编译器直接将函数体插入函数调用的地方。故内联函数没有普通函数调用时的额外开销(压栈、跳转、返回)。
既然内联函数这么好用,那么是否可以将每个函数都定义为内联函数呢?答案时否定的。因为C++编译器不一定准许函数的内联请求。如果请求失败,C++编译器会将内联函数变成普通函数处理。

内联函数与宏区别

宏:宏代码片段由预编译器处理,只是进行简单的文本替换,没有任何编译过程。
内联函数:内联函数是一种特殊的函数,具有普通函数的特征,如参数检查,返回类型等。内联函数是对编译器的一种请求,编译器可能会拒绝这种请求。内联函数由编译器处理,直接将编译后的函数体插入调用的地方。
下面用比较大小的范例来说明宏的副作用

#include <stdio.h>

#define FUNC(a, b) ((a) < (b) ? (a) : (b))

inline int func(int a, int b)
{
	return a < b ? a : b;
}

int main(int argc, char *argv[])
{
	int a = 6;
	int b = 8;
	int c = func(++a, b);
	int c = FUNC(++a, b);
	printf("a = %d, b = %d, c = %d\n", a, b, c);
    return 0;
}

函数本意是想比较两个数的大小,但是由于宏的简单文本替换的逻辑,FUNC(++a, b); ->(++a) < (b) ? (++a) : (b),如果调用宏FUNC,则最终a被自加了两次。但是如果是用inline func函数,则可以很好的消除这种文本替换带来的副作用。

现代C++编译器会对编译进行优化,因此一些函数即使没有inline声明,也可能被编译器内联编译。现代C++编译器也提供了扩展语法,能够对函数进行强制内联。
gnu g++: attribute((always_inline)) 属性.
写法:

inline int func __attribute__((always_inline)) (int a, int b) {
}

接下来我们看下编译器对内联函数的处理行为。

#include <stdio.h>

#define FUNC(a, b) ((a) < (b) ? (a) : (b))

inline int inline_func(int a, int b)
{
	return a < b ? a : b;
}

int on_inline_func(int a, int b)
{
	 return a < b ? a : b;
}

int main(int argc, char *argv[])
{
	int a = 6;
	int b = 8;
	int c = inline_func(a, b);
	c = on_inline_func(a, b);
	//int c = FUNC(++a, b);
	printf("a = %d, b = %d, c = %d\n", a, b, c);
    return 0;
}

利用g++编译器进行汇编,g++ -S 3-2.c, 生成.s文件。我们查询.s文件,发现发现inline_func 和 on_inline_func都被处理成了普通函数。
在这里插入图片描述
如果是处理成内联函数,那么代码段会直接插入到main中,并没有call的动作,此时,如果我们这样写

inline int inline_func __attribute__((always_inline))(int a, int b) {
	return a < b ? a : b;
}

再用反汇编,发现.s文件中没有了inline_func 字符。说明已经插入到了main函数中。
对于g++扩展关键字,一般情况下不使用。但是如果是对算法速度要求比较严格的地方,可以使用扩展关键字强制内联,用于加速算法执行速度。

C++内联编译的限制:

  • 不能存在任何形式的循环语句
  • 不能存在过多的条件判断语句
  • 函数体不能过于庞大
  • 不能对函数进行取地址操作(如果对一个内联函数进行取地址操作,那么编译器会对拒绝内联的请求。)
  • 函数内联声明必须在调用语句之前
    编译器对于内联函数的限制并不是绝对的,内联函数相对于普通函数的优势只是省去了函数调用时压栈、跳转和返回的开销。因此当函数执行开销远大于这些开销时,内联将变得无意义。

c++中内联函数的实现机制
在这里插入图片描述
符号表:C++在编译程序过程当中,生成的一张表,存放文件里面出现的一些名字。不会生成在程序当中。

函数默认参数

C++可以在函数声明时为参数提供一个默认值,当函数调用时没有指定这个参数的值,编译器会自动用默认值替代。

#include <stdio.h>
//声明时指定默认参数值
int square(int x = 8);

int main(int argc, char *argv[])
{
	printf("square(-2)=%d\n", square(-2));
	//没有提供实参,用函数声明时候定义的默认参数。
	printf("square()=%d\n", square());
    return 0;
}

int square(int x)
{
	return x*x;
}

在这里插入图片描述

函数默认参数规则

只有参数列表后面部分的参数才可以提供默认参数值,一旦在一个函数调用中开始使用默认参数值,那么这个参数后的所有参数都必须提供默认参数值;如果不提供,则会报错。–编译器规则

//函数声明与定义,b提供了默认参数,b之后都要提供,假如c不提供默认参数,则会报错。
int add(int a, int b = 2, int c = 3)
{
    return a + b + c;
}
//-------------------
//函数使用
printf("add(2) = %d\n", add(2));
printf("add(2) = %d\n", add(2, ,1));//编译报错。

函数占位参数

占位参数:只有参数类型声明,而没有参数名声明

int func(int a, int b, int)//最后一个int 就是占位参数
{
    return a + b;
}

一般情况下,在函数体内部无法使用占位参数,因为没有名字,所以无法使用占位参数。
那么c++支持这样的函数占位参数有什么意义呢

  • 可以将占位参数与默认参数结合起来使用,为以后程序的扩展留下线索
//当程序员看到这种定义时,应该知道作者要提醒你,函数可能会被改动,项目如果要扩展参数,那么占位参数此时就可以变成正在的参数。
int func(int a, int b, int = 0)
{
    return a + b;
}
  • 兼容c程序中可能出现的不规范写法
//定义一个无参的函数
int func() {
    return 1;
}

//在c中调用地方,传参数可以不受限制,
func();
//在c中可以编译通过,在c++中强类型检查会报错。
func(1, 2 ,3);
///-----------
//此时,如果将定义改成func(1, 2 ,3);能编译通过。
int func(int = 0, int = 0, int = 0) {
    return 1;
}
func(1, 2 ,3);

总结

  • c++中可以通过inline关键字声明内联函数
  • inline关键字只是一种请求,编译器不一定运行这种请求
  • 内联函数省去了普通函数调用时压栈、跳转、返回的操作
  • c++中在声明函数的时候指定参数的默认值
  • c++可以声明占位符参数,占位符参数一般用于程序扩展和兼容c语言函数调用的不规范写法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1172505.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

时序分解 | Matlab实现FEEMD快速集合经验模态分解时间序列信号分解

时序分解 | Matlab实现FEEMD快速集合经验模态分解时间序列信号分解 目录 时序分解 | Matlab实现FEEMD快速集合经验模态分解时间序列信号分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现FEEMD快速集合经验模态分解时间序列信号分解 算法新颖小众&#xff0c…

企业级SpringBoot单体项目模板 —— 使用 AOP + JWT实现登陆鉴权

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;SpringBoot、企业级、项目模板☀️每日 一言&#xff1a;没学会走就学跑从来都不是问题&#xff0c;要问问自己是不是天才&#xff0c;如果不是&#xff0c;那就要一步步来 文章目录 使用JWT实现…

docker 常用

系统 Ubuntu 20.04 64位 安装文档 ubuntu&#xff1a;https://docs.docker.com/engine/install/ubuntu/ centos&#xff1a;https://docs.docker.com/engine/install/centos/ debian&#xff1a;https://docs.docker.com/engine/install/debian/ 常用命令 查看镜像 docke…

NeRF-SLAM部署运行(3060Ti)

记录在部署运行期间遇到的一些问题&#xff0c;分享给大家~ 一、环境 RTX 3060 Ti、8G显存、Ubuntu18.04 二、部署 1. 下载代码 git clone https://github.com/jrpowers/NeRF-SLAM.git --recurse-submodules git submodule update --init --recursive cd thirdparty/insta…

Nacos 的底层实现原理 注册中心的两种调用方式

目录 1. Nacos 的底层实现原理 1.1 配置中心自动刷新实现原理 1.2 注册中心底层实现原理 2. Nacos 注册中心的两种调用方式 2.1 RestTemplate Spring Cloud LoadBalancer 的调用方式 2.2 使用 OpenFeign Spring Cloud LoadBalancer 1. Nacos 的底层实现原理 1.1 配置中心…

Python测试之Pytest详解

概要 当涉及到python的测试框架时&#xff0c;pytest是一个功能强大且广泛应用的第三方库。它提供简洁而灵活的方式来编写和执行测试用例&#xff0c;并具有广泛的应用场景。下面是pytest的介绍和详细使用说明&#xff1a; pytest是一个用于python单元测试的框架&#xff0c;它…

node插件express(路由)的插件使用(二)——body-parser和ejs插件的基本使用

文章目录 前言一、express使用中间件body-parser获取请全体的数据1. 代码2. 效果 二、express使用ejs&#xff08;了解即可&#xff09;1.安装2.作用3.基本使用&#xff08;1&#xff09;代码&#xff08;2&#xff09;代码分析和效果 4.列表渲染&#xff08;1&#xff09;代码…

Canvas 实现进度条展示统计数据示例

canvas可以画柱状图&#xff0c;如下就是一个例子&#xff0c;主要用到了lineWidth&#xff0c;beginPath&#xff0c;lineCap等知识点。 效果图 源代码 <!DOCTYPE Html> <html> <head><title>Line Chart Demo</title><meta http-equiv&quo…

一个球从100m高度自由落下,每次落地后反弹回原高度的一半,再落下,再反弹。求它在第10次落地时共经过多少米,第10次反弹多高

一.思路分析 这是一个简单的物理题目&#xff0c;解题思路比较明确。程序使用 for 循环来模拟球的下落和反弹过程&#xff0c;通过多次计算得到最终结果&#xff0c;最后使用 printf 函数将结果输出。 定义初始高度 height 和总共经过的米数 distance 的变量&#xff0c;初始化…

【密评】商用密码应用安全性评估从业人员考核题库(十九)

商用密码应用安全性评估从业人员考核题库&#xff08;十九&#xff09; 国密局给的参考题库5000道只是基础题&#xff0c;后续更新完5000还会继续更其他高质量题库&#xff0c;持续学习&#xff0c;共同进步。 4501 判断题 依据《信息系统密码应用高风险判定指引》&#xff0c…

服务器感染了.locked勒索病毒,如何确保数据文件完整恢复?

引言&#xff1a; 网络安全威胁的不断演变使得恶意软件如.locked勒索病毒成为当今数字时代的一大挑战。.locked勒索病毒能够加密您的文件&#xff0c;然后要求支付赎金以解锁它们。本文将深入探讨.locked勒索病毒的特点&#xff0c;以及如何应对感染&#xff0c;以及预防这种类…

数据可视化:动态柱状图

终于来到最后一个数据可视化的文章拿啦~~~ 在这里学习如何绘制动态柱状图 我先整个活 (๑′ᴗ‵๑)&#xff29; Lᵒᵛᵉᵧₒᵤ❤ 什么是pyecharts&#xff1f; 答&#xff1a; Python的Pyecharts软件包。它是一个用于Python数据可视化和图表绘制的库&#xff0c;可用于制作…

安装 2023最新版本的Tableau Desktop 时出现“0x80070643”错误

安装失败的原因&#xff1a; “0x80070643”错误是Microsoft错误。 必需的安装组件无法启动&#xff0c;通常是C库&#xff0c;或者使用了无效的操作系统版本。 通过控制面板——程序与功能可以查看到自己电脑Microsoft Visual C的版本&#xff0c;像我的话是比较低的&…

第四章IDEA操作Maven

文章目录 创建父工程开启自动导入配置Maven信息创建Java模块工程创建 Web 模块工程 在IDEA中执行Maven命令直接执行手动输入 在IDEA中查看某个模块的依赖信息工程导入来自版本控制系统来自工程目录 模块导入情景重现导入 Java 类型模块 导入 Web 类型模块 创建父工程 开启自动导…

【GitHub】Watch、Star、Fork、Follow 有什么区别?

目录 一、前言二、区别1. Watch2. Star3. Fork4. Follow 一、前言 GitHub 是最受欢迎的代码托管平台之一&#xff0c;拥有大量的开源代码可供学习。 Github 中也有类似 “点赞”、“收藏”、“加关注” 的功能。 下面介绍下&#xff0c;GitHub 中 Watch、Star、Fork、Follow 有…

uni-app华为审核被拒,驳回原因:您的应用在运行时,未见向用户告知权限申请的目的

华为审核被拒&#xff1a; 您的应用在运行时&#xff0c;未见向用户告知权限申请的目的&#xff0c;向用户索取(相机存)等权限&#xff0c;不符合华为应用市场审核标准。 <uni-popup ref"perpopup" type"center" :mask-clickfalse><view class&qu…

幂等性(防重复提交)

文章目录 1. 实现原理2.使用示例3. Idempotent注解4. debug过程 主要用途&#xff1a;防止用户快速双击某个按钮&#xff0c;而前端没有禁用&#xff0c;导致发送两次重复请求。 1. 实现原理 幂等性要求参数相同的方法在一定时间内&#xff0c;只能执行一次。本质上是基于red…

Leetcode-509 斐波那契数列

使用循环 class Solution {public int fib(int n) {if(n 0){return 0;}if(n 1){return 1;}int res 0;int pre1 1;int pre2 0;for(int i 2; i < n; i){res pre1 pre2;pre2 pre1;pre1 res;}return res;} }使用HashMap class Solution {private Map<Integer,Int…

E-Office(泛微OA)前台任意文件读取漏洞复现

简介 泛微E-Office是一款企业级的全流程办公自动化软件&#xff0c;它包括协同办公、文档管理、知识管理、工作流管理等多个模块&#xff0c;涵盖了企业日常工作中的各个环节。在该产品前台登录页存在文件读取漏洞。 officeserver.php文件存在任意文件读取漏洞&#xff0c;通…

[100天算法】-有序矩阵中第K小的元素(day 58)

题目描述 给定一个 n x n 矩阵&#xff0c;其中每行和每列元素均按升序排序&#xff0c;找到矩阵中第 k 小的元素。 请注意&#xff0c;它是排序后的第 k 小元素&#xff0c;而不是第 k 个不同的元素。示例&#xff1a;matrix [[ 1, 5, 9],[10, 11, 13],[12, 13, 15] ], k …