3 函数的升级-上

常量与宏回顾

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语言函数调用的不规范写法

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

0006Java安卓程序设计-ssm基于Android的校园二手商品交易平台

文章目录 **摘** **要****目** **录**系统设计开发环境 编程技术交流、源码分享、模板分享、网课教程 &#x1f427;裙&#xff1a;776871563 摘 要 随着毕业季的来临以及当代大学生的消费力购买力的不断增强&#xff0c;我们的寝室中囤积了很多二手商品&#xff0c;有很多是…

正点原子嵌入式linux驱动开发——Linux CAN驱动

CAN是目前应用非常广泛的现场总线之一&#xff0c;主要应用于汽车电子和工业领域&#xff0c;尤其是汽车领域&#xff0c;汽车上大量的传感器与模块都是通过CAN总线连接起来的。CAN总线目前是自动化领域发展的热点技术之一&#xff0c;由于其高可靠性&#xff0c;CAN总线目前广…

阿里云安全恶意程序检测

阿里云安全恶意程序检测 赛题理解赛题介绍赛题说明数据说明评测指标 赛题分析数据特征解题思路 数据探索数据特征类型数据分布箱型图 变量取值分布缺失值异常值分析训练集的tid特征标签分布测试集数据探索同上 数据集联合分析file_id分析API分析 特征工程与基线模型构造特征与特…

【前端周报11.03】

前端周汇报11.03 那我们接着上一周的继续往下进行推进上周总结本周工作下周内容 那我们接着上一周的继续往下进行推进 上周总结 上一周的话我其实最主要的工作还是进行了一系列的调研主要的话是针对于我们未来要做的小程序的项目的&#xff0c;为未来开发这个小程序做好一系列…

leetcode:26. 删除有序数组中的重复项(python3解法)

难度&#xff1a;简单 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数…

多目标跟踪算法 实时检测 - opencv 深度学习 机器视觉 计算机竞赛

文章目录 0 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习多目标跟踪 …

【入门Flink】- 04Flink部署模式和运行模式【偏概念】

部署模式 在一些应用场景中&#xff0c;对于集群资源分配和占用的方式&#xff0c;可能会有特定的需求。Flink为各种场景提供了不同的部署模式&#xff0c;主要有以下三种&#xff1a;会话模式&#xff08;Session Mode&#xff09;、单作业模式&#xff08;Per-Job Mode&…

Ubuntu20.04下安装Redis环境

apt安装Redis环境 更新apt-get安装镜像源 安装Redis sudo apt-get install -y redis-server设置密码 # 编辑Redis的配置文件redis.conf&#xff0c;如果不知道配置文件的位置可以执行whereis redis.conf查看 sudo vim /etc/redis/redis.conf取消文件中的requirepass注释&am…

设计模式(22)享元模式

一、介绍&#xff1a; 1、定义&#xff1a;享元模式&#xff08;Flyweight Pattern&#xff09;主要用于减少创建对象的数量&#xff0c;以减少内存占用和提高性能。这种类型的设计模式属于结构型模式&#xff0c;它提供了减少对象数量从而改善应用所需的对象结构的方式。 2、…

memcpy()之小端模式

函数原型 void memcpy(voiddestin, const void *src, size_t n); 功能 由src指向地址为起始地址的连续n个字节的数据复制到以destin指向地址为起始地址的空间内。 头文件 #include<string.h> 返回值 函数返回一个指向dest的指针。 例1&#xff1a;如果用来复制字…

FPGA高端项目:图像采集+GTP+UDP架构,高速接口以太网视频传输,提供2套工程源码加QT上位机源码和技术支持

目录 1、前言免责声明本项目特点 2、相关方案推荐我这里已有的 GT 高速接口解决方案我这里已有的以太网方案 3、设计思路框架设计框图视频源选择OV5640摄像头配置及采集动态彩条视频数据组包GTP 全网最细解读GTP 基本结构GTP 发送和接收处理流程GTP 的参考时钟GTP 发送接口GTP …

【计算机网络】运输层

概述运输层服务 运输层协议为运行在不同主机上的应用程序提供了逻辑通信功能。 运输层协议是在端系统中而不是在路由器中实现的。 运输层和网络层的关系&#xff1a; 网络层提供主机之间的逻辑通信&#xff0c;而运输层为**运行在不同主机上的应用程序&#xff08;进程&#…

做读书笔记时的一个高效小技巧

你好&#xff0c;我是 EarlGrey&#xff0c;一名双语学习者&#xff0c;会一点编程&#xff0c;目前已翻译出版《Python 无师自通》、《Python 并行编程手册》等书籍。 在这里&#xff0c;我会持续和大家分享好书、好工具和高效生活、工作技巧&#xff0c;欢迎大家一起提升认知…

【CesiumJS】(1)Hello world

介绍 Cesium 起源于2011年&#xff0c;初衷是航空软件公司(Analytical Graphics, Inc.)的一个团队要制作世界上最准确、性能最高且具有时间动态性的虚拟地球。取名"Cesium"是因为元素铯Cesium让原子钟非常准确&#xff08;1967年&#xff0c;人们依据铯原子的振动而对…

Android Studio打包AAR

注意 依赖的Android Studio版本为4.2.2 更高的Android Studio版本使用方法可能有所不同&#xff0c;gradle的版本和gradle plugins的版本都会影响使用方式。 基于此&#xff0c;本文只能作为参考&#xff0c;而不能作为唯一答案&#xff0c;如果要完全依赖本文&#xff0c;则…

GPT与人类共生:解析AI助手的兴起

随着GPT模型的崭新应用&#xff0c;如百度的​1​和CSDN的​2​&#xff0c;以及AI助手的普及&#xff0c;人们开始讨论AI对就业市场和互联网公司的潜在影响。本文将探讨GPT和AI助手的共生关系&#xff0c;以及我们如何使用它们&#xff0c;以及使用的平台和动机。 GPT和AI助手…

Linux | 如何保持 SSH 会话处于活动状态

在远程服务器管理和安全数据传输中&#xff0c;SSH&#xff08;Secure Shell&#xff09;是不可或缺的工具。然而&#xff0c;它的便利性和安全性有时会因常见的问题而受到损害&#xff1a;冻结 SSH 会话。 此外&#xff0c;session 的突然中断可能会导致工作丢失、项目延迟和无…

LIME低亮度图像增强

LIME低亮度图像增强 main.cpp #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> #include <opencv2/imgproc/imgproc.hpp> #include "lime.h"int main() {cv::Mat img_in cv::imread("…

使用Postman工具做接口测试 —— 环境变量与请求参数格式!

引言 在上一篇笔记我们主要介绍了接口测试的基础知识与基本功能&#xff0c;本章主要介绍如何使用postman做接口测试。 配置环境变量和全局变量 环境变量和全局变量 环境管理中还可以点击“Global”添加全局变量&#xff0c;环境变量只有当选择了该环境时才生效&#xff0c;…

电脑出现emp.dll文件缺失的错误提示怎么办,教你一键解决dll丢失问题

今天&#xff0c;我想和大家分享一下关于emp.dll文件丢失的4个解决方法&#xff0c;希望能对大家有所帮助。 首先&#xff0c;我们要明确emp.dll文件的作用。emp.dll是一个动态链接库文件&#xff0c;这个文件对于许多程序的正常运行至关重要&#xff0c;一旦丢失&#xff0c;…