【C语言】内联函数总结
内联函数定义
inline关键字是C99标准的型关键字,其作用是将函数展开,把函数的代码复制到每一个调用处。这样调用函数的过程就可以直接执行函数代码,而不发生跳转、压栈等一般性函数操作。可以节省时间,也会提高程序的执行速度。
为什么需要内联函数
在C语言中,如果一些函数被频繁的调用,不断地用函数入栈,即函数栈,则会造成栈空间或者栈内存的大量消耗,为了解决这个问题,特别的引入了inline关键字,表示为内联函数。
栈空间指的是函数内数据的内存空间,在一个系统下,栈空间的资源是有限的,假如频繁大量的使用就会因栈空间的不足而导致出错,函数的死循坏递归调用的最终结果就是导致栈内存空间的枯竭。
#include <stdio.h>
//函数定义为inline即:内联函数
inline char* dbtest(int a) {return (i % 2 > 0) ? "奇" : "偶";
} int main()
{int i = 0;for (i=1; i < 100; i++) {printf("i:%d 奇偶性:%s /n", i, dbtest(i)); }
}
上面的例子就是标准的内联函数的用法,使用inline修饰带来的好处我们表面看不出来,其实,在内部的工作就是在每个for循环的内部任何调用dbtest(i)的地方都换成了(i % 2 > 0) ? "奇" : "偶"
,这样就避免了频繁调用函数对栈内存重复开辟所带来的消耗。
内联函数注意事项
- 关键字inline必须与函数的定义体放在一起,才能使函数成为内联函数,仅仅将inline放在函数声明前面不起作用
如下风格的函数fun则成为内联函数:
void fun(int x, int y);
inline void fun(int x, int y) //inline与函数的定义放在一起
{}
- 关键字inline的使用是有所限制的
inline只适合函数体内代码比较简单的函数使用,不能包含复杂的结构控制语句,例如while、switch,并且内联函数本身不能是直接递归函数(函数内部调用自己的函数)。
- inline仅是一个对编译器的建议
inline函数仅仅是一个对编译器的建议,所以最后能否真正内联,看编译器的意思,它如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就会内联,声明内联只是一个建议而已。
- 建议:inline函数的定义放在头文件中
其次,因为内联函数要在调用点展开,所以编译器必须随处可见内联函数的定义,要不然就成了非内联函数的调用了。所以,这要求每个调用了内联函数的文件都出现了该内联函数的定义。
因此,将内联函数的定义放在头文件里实现是合适的,省却你为每个文件实现一次的麻烦。
声明跟定义要一致:如果在每个文件里都实现一次该内联函数的话,那么,最好保证每个定义都是一样的,否则,将会引起未定义的行为。如果不是每个文件里的定义都一样,那么,编译器展开的是哪一个,那要看具体的编译器而定。所以,最好将内联函数定义放在头文件中。
- static和inline联合使用
static是静态修饰符,由其关键字修饰的变量会保存到全局数据区,对于普通的局部变量或者全局变量,都是由系统自动分配内存的,并且当变量离开作用域的时候释放掉,而使用static关键字来修饰,只有当程序结束时候才会释放掉,使用static inline修饰时,函数仅在文件内部可见,不会污染命名空间,另外,函数在运行过程中也会分配内存空间,但是由于static的存在,就和修饰变量类似,它只会开辟一块内存空间。
内联函数优缺点
-
普通函数在调用过程中,会对寄存器中内容进行上下文切换(push和pop操作),而内联函数则不需要,所以普通函数相比内联函数,耗时要多一些。
-
当函数使用次数比较多的时候,内联函数在每个调用的地方都会被展开,所以导致固件大小会变大,同一段代码会多次重复出现在固件中。而普通函数则没有此问题,不管调用的函数的次数多少,函数在固件中均只占用一处,空间利用率较高。inline函数其实就是空间换时间
inline 和宏的区别
虽然inline函数和带参数的宏很像,但是在使用方法上和宏还是有很大区别的:
inline()函数 | 带参数的宏 | |
---|---|---|
展开的时机 | 在编译的时候展开,因此inline关键字是一个编译关键字 | 在预处理时展开,因此#define关键字是一个预处理关键字 |
参数类型检查 | inline()函数是一中函数,会进行严格的参数类型检查 | 不会检查参数类型,只是做简单的字符串替换,因此在使用带参数的宏时会有一些副作用,编写程序是要人为预防 |
是否允许有复杂语句 | 不允许出现复杂语句,如果出现复杂语句,该函数将不会展开,例如递归,大型循环等 | 对此不做要求。宏只是做字符串替换操作,而不了解语句的含义 |
是否一定被展开 | 不一定,是否展开由编译器决定 | 一定,只要使用了宏就可以保证被展开 |
接口封装 | 是 | 否 |
是否支持调试 | 是 | 否 |
总结
-
内联函数相比宏函数,会进行语法检查。宏函数是在预处理阶段生效,内联函数是在编译阶段进行语法检查然后替换。
-
内联函数相比普通函数,少了上下文切换的步骤所以执行会更快一些。
-
内联函数被多次调用,会使固件大小膨胀,内联函数的高速是以空间来换时间。
-
内联函数不可递归。
-
如果函数内容太过于复杂,编译器会忽略inline关键字,把他当成普通函数来处理。
本文参考
https://zhuanlan.zhihu.com/p/448262183
https://zhuanlan.zhihu.com/p/50812510
https://cloud.tencent.com/developer/article/2224955
相关文章:
【C语言】内联函数总结
内联函数定义 inline关键字是C99标准的型关键字,其作用是将函数展开,把函数的代码复制到每一个调用处。这样调用函数的过程就可以直接执行函数代码,而不发生跳转、压栈等一般性函数操作。可以节省时间,也会提高程序的执行速度。 …...

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之MenuItemGroup组件
鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之MenuItemGroup组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、MenuItemGroup组件 该组件用来展示菜单MenuItem的分组。 子组件 无 接…...
【Linux多线程编程】互斥锁及其使用
1、互斥锁 用于解决竞争问题的一种机制。 什么是竞争,竞争就是多个实体同时获取一个资源,例如多个线程写一个全局变量。 2、Linux如何使用互斥锁 以pthread为例,锁的创建和使用如下: /* 创建锁 */ pthread_mutex_t lock PTHR…...

RabbitMQ_00000
MQ的相关概念 RabbitMQ官网地址:https://www.rabbitmq.com RabbitMQ API地址:https://rabbitmq.github.io/rabbitmq-java-client/api/current/ 什么是MQ? MQ(message queue)本质是个队列,FIFO先入先出,只不过队列中…...
【linux】docker下homeassistant和nodered安装及配置
1、homeassistant安装 从 Docker Hub 上拉取 Home Assistant 的镜像文件 docker pull homeassistant/home-assistant 是运行 Home Assistant 容器 docker run -id --name"homeassistant" --privileged --restart always -p 8123:8123 -e TZAisa/Shanghai --nethost…...

Qt扩展-muParser数学公式解析
muParser数学公式解析 一、概述1. 针对速度进行了优化2. 支持的运算符3. 支持的函数4. 用户定义的常量5. 用户定义的变量6. 自定义值识别回调7. 其他功能 二、内置函数三、内置二元运算符四、三元运算符五、内置常量六、源码引入1. 源码文件2. 编译器开关1. MUP_BASETYPE2.MUP_…...

【Matplotlib】figure方法之图形的保存
🎈个人主页:甜美的江 🎉欢迎 👍点赞✍评论⭐收藏 🤗收录专栏:matplotlib 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进…...
数据库管理-第142期 DBA?DBA!(20240131)
数据库管理142期 2024-01-31 数据库管理-第142期 DBA?DBA!(20240131)正文总结 数据库管理-第142期 DBA?DBA!(20240131) 作者:胖头鱼的鱼缸(尹海文)…...

react 之 zustand
zustand可以说是redux的平替 官网地址:https://zustand-demo.pmnd.rs/ 1.安装 npm i zustand2.基础使用 // zustand import { create } from zustand// 1. 创建store // 语法容易出错 // 1. 函数参数必须返回一个对象 对象内部编写状态数据和方法 // 2. set是用来…...
leetcode-回文链表
234. 回文链表 在此对比的值,不是节点 # Definition for singly-linked list. # class ListNode: # def __init__(self, val0, nextNone): # self.val val # self.next next class Solution:def isPalindrome(self, head: Optional[ListNod…...
Pinia:一个Vue的状态管理库
Pinia的使用方法包括以下步骤: 安装Pinia:通过yarn或npm进行安装: yarn命令: yarn add pinianpm命令: npm install pinia创建根存储:在main.ts中引入Pinia插件,并创建一个根存储。这可以通过创建…...

2024 Flutter 重大更新,Dart 宏(Macros)编程开始支持,JSON 序列化有救
说起宏编程可能大家并不陌生,但是这对于 Flutter 和 Dart 开发者来说它一直是一个「遗憾」,这个「遗憾」体现在编辑过程的代码修改支持上,其中最典型的莫过于 Dart 的 JSON 序列化。 举个例子,目前 Dart 语言的 JSON 序列化高度依…...

云计算概述(云计算类型、技术驱动力、关键技术、特征、特点、通用点、架构层次)(二)
云计算概述(二) (云计算类型、技术驱动力、关键技术、特征、特点、通用点、架构层次) 目录 零、00时光宝盒 一、云计算类型(以服务的内容或形态来分) 二、云计算的12种技术驱动力 三、云计算的关键技术 四、云计…...

物流平台架构设计与实践
随着电商行业的迅猛发展,物流行业也得到了极大的发展。从最初的传统物流到现在的智慧物流,物流技术和模式也在不断的更新与升级。物流平台作为连接电商和物流的重要媒介,其架构设计和实践显得尤为重要。 一、物流平台架构设计 1. 前端架构设…...

RedHat8.4安装邮件服务器
一、配置发件服务器 1.1 根据现场IP,配置主机名 vim /etc/hosts 192.168.8.120 mail.test.com 将主机名更改为邮件服务器域名mail.test.com 1.2 关闭防火墙,禁止开机启动 systemctl stop firewalld systemctl disable firewalld 1.3 关闭selinux v…...

Linux Shell系列--dirname 去除基本文件名
一、目的 上一篇中我们介绍了basename命令的使用,本篇我们介绍dirname命令,dirname 命令与 basename 互补,它负责删除路径中的基本文件名部分(包括扩展名),只保留目录部分。 二、介绍 dirname首先去除字符…...

池化技术的总结
文章目录 1.什么是池化技术2.池化技术的应用一、连接池二、线程池三、内存池 3.池化技术的总结 1.什么是池化技术 池化技术指的是提前准备一些资源,在需要时可以重复使用这些预先准备的资源。 在系统开发过程中,我们经常会用到池化技术。通俗的讲&am…...

H5简约星空旋转引导页源码
H5简约星空旋转引导页源码 源码介绍:一款带有星空旋转背景特效的源码,带有四个按钮 下载地址: https://www.changyouzuhao.cn/11655.html...
前端学习之路(4) vue2和vue3的区别
一. 根节点不同 vue2中必须要有根标签vue3中可以没有根标签,会默认将多个根标签包裹在一个fragement虚拟标签中,有利于减少内存。 二. 组合式API和选项式API 在vue2中采用选项式API,将数据和函数集中起来处理,将功能点切割了当…...

网络原理-TCP/IP(5)
TCP协议 延迟应答 它也是基于滑动窗口,提高效率的一种机制,结合滑动窗口以及流量控制,能够以延迟应答ACK的方式,把反馈的窗口,搞大.核心在于允许范围内,让窗口尽可能大. 如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小. 1.假设接收端缓冲区为1M.一次收到了5…...

如何使用DAXStudio将PowerBI与Excel连接
如何使用DAXStudio将PowerBI与Excel连接 之前分享过一篇自动化文章:PowerBI链接EXCEL实现自动化报表,使用一个EXCEL宏工作薄将PowerBI与EXCEL连接起来,今天分享另一个方法:使用DAX Studio将PowerBI与EXCEL连接。 下面是使用DAX S…...
使用ArcPy生成地图系列
设置地图布局 在生成地图系列之前,需要先设置地图布局。这包括定义地图的页面大小、地图框的位置和大小、标题、图例等元素。ArcPy提供了arcpy.mp.ArcGISProject方法来加载ArcGIS Pro项目文件(.aprx),并操作其中的地图布局。 Py…...

解决Vditor加载Markdown网页很慢的问题(Vite+JS+Vditor)
1. 引言 在上一篇文章《使用Vditor将Markdown文档渲染成网页(ViteJSVditor)》中,详细介绍了通过Vditor将Markdown格式文档渲染成Web网页的过程,并且实现了图片格式居中以及图片源更换的功能。不过,笔者发现在加载这个渲染Markdown网页的时候…...

STM32外部中断(EXTI)以及旋转编码器的简介
一、外部中断机制概述 中断是指当主程序执行期间出现特定触发条件(即中断源)时,CPU将暂停当前任务,转而执行相应的中断服务程序(ISR),待处理完成后恢复原程序的运行流程。该机制通过事件驱动…...

【操作系统原理08】文件管理
文章目录 零.大纲一.文件管理0.大纲1.文件管理1.1 **文件属性**1.2 文件内部数据组织1.3 文件之间的组织1.4操作系统提供功能1.5 文件在外存存放 二.文件的逻辑结构0.大纲1.无结构文件2.有结构文件 三.文件目录0.大纲1.文件控制块2.目录结构3.索引节点(FCB改进) 四.文件共享0.大…...
6.3 day 35
知识点回顾: 三种不同的模型可视化方法:推荐torchinfo打印summary权重分布可视化进度条功能:手动和自动写法,让打印结果更加美观推理的写法:评估模式 可视化 理解深度学习网络最重要的2点: 1.了解损失如何定…...

Rust 学习笔记:Cargo 工作区
Rust 学习笔记:Cargo 工作区 Rust 学习笔记:Cargo 工作区创建工作区在工作区中创建第二个包依赖于工作区中的外部包向工作区添加测试将工作区中的 crate 发布到 crates.io添加 add_two crate 到工作区总结 Rust 学习笔记:Cargo 工作区 随着项…...

vSOME/IP与ETAS DSOME/IP通信的问题解决方案
✅ 一、服务版本不匹配导致 Handover 问题 —— 需要更新 VSOMEIP 代码逻辑 📌 问题描述: 在 SOME/IP 通信中,发布者(offer)与订阅者(subscribe)之间存在服务版本不一致的问题,导致 Handover(切换)失败。 ✅ 解决方案: 需要在 offer_service 和 subscribe 接口中…...
git怎么合并两个分支
git怎么合并分支代码 注意: 第一步你得把当前分支合到远程分支去才能有下面的操作 另外我是将develop分支代码合并到release分支去 git 命令 查看本地所有分支 git branch切换分支 例如切换到release分支 git checkout release拉取代码 git pull up release 合并分支 …...

实现一个免费可用的文生图的MCP Server
概述 文生图模型为使用 Cloudflare Worker AI 部署 Flux 模型,是参照视频https://www.bilibili.com/video/BV1UbkcYcE24/?spm_id_from333.337.search-card.all.click&vd_source9ca2da6b1848bc903db417c336f9cb6b的复现Cursor MCP Server实现是参照文章https:/…...