C语言函数不同个数、大小形参对执行速度的影响:以Cortex-M3为例从汇编角度分析原因

0 资料&工具

Cortex M3权威指南(中文).pdf
keil5(用于仿真查看汇编代码、栈变化)

1 C语言函数不同个数、大小形参对执行速度的影响:以Cortex-M3为例从汇编角度分析原因

C语言中有条不成文的规定:不建议函数的形参数量超过4个。为什么会有这样的规定呢,本文以Cortex-M3为例,分析C语言函数不同个数、大小形参对执行速度的影响。

1.1 理论分析

ARM架构的处理器使用R0-R3寄存器传递函数参数,假如有4个参数,则按照顺序依次将参数4写入R3、参数3写入R2、参数2写入R1、参数1写入R0。这是因为访问寄存器的速度要比访问内存(堆栈)要快。如果参数数量超过4个,或者参数太大不足以通过4个寄存器传递参数,则超出部分的参数将通过堆栈传递。使用堆栈传递参数时,参数按照从右往左的顺序压入堆栈,即第一个参数(超出寄存器的部分)会先被压入堆栈。

1.2 举例分析

1.2.1 函数参数个数不超过4个且参数大小均小于寄存器大小(32bit)

示例程序:

typedef unsigned long long int u64;
typedef unsigned int u32;

u32 fun1(u32 p1, u32 p2, u32 p3, u32 p4)
{
    return p1 + p2 + p3 + p4;
}

u32 fun2(u32 p1, u32 p2, u32 p3, u32 p4, u32 p5)
{
    return p1 + p2 + p3 + p4 + p5;
}

u64 fun3(u64 p1, u64 p2, u64 p3)
{
    return p1 + p2 + p3;
}


/**
 *   主函数
 */
int main(void)
{
    fun1(1, 2, 3, 4);
}

对应的汇编代码:
在这里插入图片描述
在跳转fun1函数前依次将参数4-1压入R3-R0,没有使用到堆栈。

1.2.2 函数参数个数超过4个但总大小不超过4x32bit

示例程序:

typedef unsigned long long int u64;
typedef unsigned int u32;

u32 fun1(u32 p1, u32 p2, u32 p3, u32 p4)
{
    return p1 + p2 + p3 + p4;
}

u32 fun2(u32 p1, u32 p2, u32 p3, u32 p4, u32 p5)
{
    return p1 + p2 + p3 + p4 + p5;
}

u64 fun3(u64 p1, u64 p2, u64 p3)
{
    return p1 + p2 + p3;
}


/**
 *   主函数
 */
int main(void)
{
    fun2(1, 2, 3, 4, 5);
}

对应的汇编代码:
在这里插入图片描述
这里关注一下进入mian函数的压栈指令(PUSH),PUSH之后堆栈指针的值为0x200003F8
操作如下:
(1)将参数5保存到R0
(2)将参数4保存到R3
(3)将参数3保存到R2
(4)将参数2保存到R1
(4)将参数5的值保存到堆栈
(5)将参数1保存到R0
我们再将函数参数大小fun2修改为u16,这样下来函数参数总大小为5x16=80bit没超过4x32=128bit,看看是否同样需要使用堆栈传递形参。对应代码如下:

typedef unsigned long long int u64;
typedef unsigned short int u16;
typedef unsigned int u32;

u32 fun1(u16 p1, u16 p2, u16 p3, u16 p4)
{
    return p1 + p2 + p3 + p4;
}

u32 fun2(u16 p1, u16 p2, u16 p3, u16 p4, u16 p5)
{
    return p1 + p2 + p3 + p4 + p5;
}

u64 fun3(u64 p1, u64 p2, u64 p3)
{
    return p1 + p2 + p3;
}


/**
 *   主函数
 */
int main(void)
{
	  fun2(1, 2, 3, 4, 5);
}

对应的汇编代码如下:
在这里插入图片描述
可以看到同样需要使用到堆栈传递参数。

1.2.3 函数参数大小超过4x32bit但参数个数不超过4个

示例程序:

typedef unsigned long long int u64;
typedef unsigned int u32;

u32 fun1(u32 p1, u32 p2, u32 p3, u32 p4)
{
    return p1 + p2 + p3 + p4;
}

u32 fun2(u32 p1, u32 p2, u32 p3, u32 p4, u32 p5)
{
    return p1 + p2 + p3 + p4 + p5;
}

u64 fun3(u64 p1, u64 p2, u64 p3)
{
    return p1 + p2 + p3;
}


/**
 *   主函数
 */
int main(void)
{
    fun3(0x1ffffffff, 0x2ffffffff, 0x3ffffffff);
}

对应的汇编代码:
在这里插入图片描述
这里关注一下进入mian函数的压栈指令(PUSH),PUSH之后堆栈指针的值为0x200003F4
操作如下:
(1)将R1设置为0xffffffff
(2)将R0设置为0x03
(3)将R2的值设置为0xffffffff
(4)将R3的值设置为0x02
(5)将R0和R1依次压入堆栈
(6)将R0设置为0xffffffff
(7)将R1设置为0x1
此时R0、R1组成参数1:0x1ffffffff,R2、R3组成参数2:0x2fffffff,加上堆栈中的0x3ffffff便组成了函数的3个参数值。

2 结论

(1)当函数形参数量大于4个或函数形参总大小超过4x32bit(R0-R3寄存器总大小)则会使用堆栈来传递形参,降低函数执行效率。
(2)当我们调用的函数形参数量超过4个时,建议使用指针传递参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NW嵌入式开发

感谢您的支持,让我们一起进步!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值