从汇编角度理解C语言篇 (三) —— C语言函数的实现

发布时间:2026/9/18 22:18:06

从汇编角度理解C语言篇 (三) —— C语言函数的实现 1. C语言函数组成// 返回类型 函数名 参数列表 int add (int a, int b) { // 函数体 int ret a b; // 返回值 return ret; }在C语言中函数是执行特定任务的独立代码块。一个函数可以接收参数如果有的话执行一系列操作然后可选地返回一个值。C语言中的函数组成主要包括以下几个部分‌返回类型‌函数的返回类型指定了函数执行完成后返回值的类型。例如int、void、float、char等。void表示该函数不返回任何值。‌函数名‌这是函数的标识符用于在程序中调用该函数。函数名必须是合法的标识符且在程序中是唯一的。‌参数列表‌参数列表定义了函数执行时可以接收的数据。参数可以有零个或多个每个参数都有一个类型和一个名称。参数之间用逗号分隔。如果函数不需要参数则参数列表为空写成void例如int add(int a, int b)。‌函数体‌函数体是包含了一组执行特定任务的语句的大括号{}内的部分。这是函数的实际代码实现。‌返回值‌在函数体内部可以使用return语句返回一个值。返回值的类型必须与函数的返回类型匹配。如果函数的返回类型是void则不能使用return语句返回一个值但可以使用return;仅表示提前退出。2. C语言函数传入传出参数在读下面内容之前可以先思考一个问题为什么使用变量传入参数给函数在函数里面无法直接改变传入变量的值还是以 add 函数为例。c语言代码int add(int a, int b) { return a b; } int main(void) { add(1, 2); return 0; }对应ARM汇编代码add: 0x080001AE 4602 MOV r2, r0 ; 把r0的值给 r2寄存器 0x080001B0 1850 ADDS r0, r2, r1 ; r2和r1的值相加给r0, 用于传出参数 0x080001B2 4770 BX lr ; 跳转回函数调用地址 main: 0x080001B6 2102 MOVS r1, #0x02 ; 立即数赋值. 把 数字2 写入 r1寄存器 0x080001B8 2001 MOVS r0, #0x01 ; 把 数字1 写入 r0寄存器 0x080001BA F7FFFFF8 BL.W add (0x080001AE) ; 调用/进入 add 函数 0x080001BE 2000 MOVS r0, #0x00 ; return 0从汇编代码可以看出传入参数对函数传入参数时需要把参数传给 r 寄存器进入函数后函数再从 r 寄存器里获取数据。传出参数函数传出或者返回值时同样通过 r 寄存器向外传入参数。也就是说 r 寄存器可以作为函数传入传出的介质。以下为ARM寄存器组说明寄存器别名主要用途特殊说明R0A1函数第 1 个参数 / 函数返回值ARM 标准调用约定下函数返回值必须通过R0返回R1A2函数第 2 个参数-R2A3函数第 3 个参数-R3A4函数第 4 个参数-R4V1通用变量寄存器需被调用者保存若函数修改了R4-R11需手动保存到栈R5V2通用变量寄存器需被调用者保存-R6V3通用变量寄存器需被调用者保存-R7V4通用变量寄存器 / 帧指针FPThumb 指令集中常作为栈帧指针R8V5通用变量寄存器需被调用者保存-R9V6通用变量寄存器 / 平台寄存器部分操作系统中用作全局变量指针SBR10V7通用变量寄存器需被调用者保存-R11V8通用变量寄存器需被调用者保存-R12IP内部过程调用寄存器用于动态链接、库函数调用时的临时中转R13SP栈指针Stack Pointer指向栈顶硬件自动维护Cortex-M 为满递减栈R14LR链接寄存器Link Register保存函数 / 中断的返回地址BL指令会自动写入R15PC程序计数器Program Counter指向当前执行指令的下一条指令地址修改PC会直接跳转ARM约定R0-R3 寄存器传递函数前 4 个参数。那么如果函数大于四个参数呢聪明的你应该已经想到了内存也可以用于保存参数。ARM正是这样做的超过4个参数则进行压栈处理。操作规则传参R0-R3传递前 4 个参数超过部分压入栈返回值32 位 / 64 位结果通过R0R1返回寄存器保存R0-R3, R12由调用者保存R4-R11, LR由被调用者保存当然不同芯片架构不同编译器也会有区别这里不展开讨论。回到上面的问题为什么使用变量传入参数给函数在函数里面无法直接改变传入变量的值简单来说因为数据经过了拷贝在函数里是改变的并不是原来的变量里的内容。3. 函数调用和返回我们还是看上面的例子但只看调用和返回部分。add: 0x080001B2 4770 BX lr ; 跳转回函数调用地址 0x080001BE main: 0x080001BA F7FFFFF8 BL.W add (0x080001AE) ; 调用/进入 add 函数 0x080001BE 2000 MOVS r0, #0x00 ; return 0我们在第一篇讲过汇编代码组织方式可以按功能流程组织代码块执行不同功能时只需要跳转到对应的代码块。C语言进入到子函数也是通过跳转方式实现的跳转前BL会自动将下一条指令的地址放入LR寄存器子函数通过LR寄存器的地址返回到原来的地址。我们再看看多层嵌套函数function_2: 0x080001B8 4770 BX lr function_1: 0x080001BA B500 PUSH {lr} 0x080001C0 F7FFFFF8 BL.W function_2 (0x080001B4) 0x080001C4 BD00 POP {pc} main: 0x080001CA F7FFFFF6 BL.W function_1 (0x080001BA) 0x080001CE 2000 MOVS r0, #0x00function_1 进入子函数 function_2 前需要把 LR寄存器 PUSH压栈返回的时候POP 值直接给到PCCPU立即跳回调用者的下一行。更多层的函数嵌套也是一样的。这里有一个小技巧在调试时我们可以使用调试器的调用栈功能如果代码出现fault就能很直观的看到函数调用关系方便解bug。4. C语言代码优化经过上面的内容我们能明显发现一个问题每次调用函数都需要进行参数拷贝不仅需要消耗拷贝时间参数多的时候也消耗内存空间。另外函数调用的越深栈使用的也会越多。在使用内存资源比较小的芯片我们需要尽可能的减少资源消耗。1. 对于参数拷贝问题我们可以使用以下方法优化用宏替代简单的函数。inline 内联函数。需要注意的是内联函数代码及变量需要尽可能的少否则在内联函数展开后可能会适得其反。但其实一般情况下编译器已经能做的足够好只要我们提高编译优化等级就能很好的优化掉此类问题。2. 对于函数调用深度问题可以考虑以下方法避免过度封装函数定时器等回调函数可以设置触发标志在另一处进行具体函数处理使用事件驱动型架构降低调用层级
延伸阅读

更多相关文章

2026/9/18 22:18:06

数据治理平台与数据运营体系融合建设方案:从指标口径到最小闭环

简介:面向企业数据治理与数据运营体系建设需求,这份PPT方案按总体解决方案、平台建设方案、运营实施方案三层框架展开,适合数据治理工程师、数据平台架构师及数字化转型项目负责人参考。开篇厘清数据治理的狭义与广义定义,进而梳理…

2026/9/18 23:18:08

oh-my-hermes:统一 Hermes 配置管理,让 React Native 性能优化更简单

1. 项目缘起:为什么我会折腾一套 Hermes 配置管理方案先交代一下背景。我一直在做 React Native 相关的性能优化工作,接触 Hermes 引擎其实比大多数人要早。Hermes 是 Facebook 专门为 React Native 打造的那套 JavaScript 引擎,它在 Android…

2026/9/18 23:18:08

UE高级运动系统实战:数据流、动画融合与性能优化

1. 先把"高级运动系统"的边界划清楚1.1 高级运动系统究竟在解决什么问题刚接触 UE高级运动系统 的人,十有八九是被那种"转身会甩腿、跑动会压身、上下坡脚步能贴地"的角色手感吸引过来的。但真把工程拖进编辑器跑起来,往往第一反应是…

2026/9/18 14:13:01

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/18 0:01:09

Google Colab 实战:运行模型、数据加载与报错排查

1. 为什么我劝你先搞懂 Colab 的运行模型1.1 Colab 到底是什么,跟本地跑代码差在哪Google Colab 简单说就是一台跑在浏览器里的 Linux 虚拟机,你打开一个 Notebook,背后就连上了一台带 GPU 的远程机器。你在单元格里敲的每一行 Python&#x…

2026/9/18 0:01:09

C语言数据类型与表达式详解

1. C语言数据与数据类型概述在C语言编程中,数据是程序处理的核心对象。理解数据的分类和特性是掌握C语言的基础。C语言中的数据主要分为四大类:常量、变量、表达式和函数。这些数据类型构成了C语言程序的基本元素,每种类型都有其独特的特性和…

2026/9/18 0:01:09

SQL时间字段指定时间段查询:区间语义、索引与时区避坑

上周排查一个线上问题&#xff0c;用户反馈"昨天的订单一条都没查到"&#xff0c;但数据库里明明躺着两千多条。最后定位下来&#xff0c;不是数据丢了&#xff0c;也不是接口挂了&#xff0c;而是那个查询条件把时间段写成了> 2024-05-20 00:00:00 AND < 2024…

2026/9/18 14:13:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行&#xff0c;Type-C接口算是典型的“看着简单&#xff0c;做起来全坑”的东西。光引脚就24个&#xff0c;高低速信号、电源、控制线全部塞在一个小小的连接器里&#xff0c;如果PCB布局不做规划&#xff0c;打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/18 14:13:02

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/18 14:13:02

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码