由浅入深C系列四:memset/memcpy源码分析,为什么这二个函数的目的字符串在前面,源字符串在后面?

困惑起源

memset/memcpy是我们常用的库函数, 有没有想过,为什么都是dest在前面,src在后面?:)

/* Copy N bytes of SRC to DEST.  */
extern void *memcpy (void *__restrict __dest, const void *__restrict __src,
		     size_t __n) __THROW __nonnull ((1, 2));
/* Set N bytes of S to C.  */
extern void *memset (void *__s, int __c, size_t __n) __THROW __nonnull ((1));

读源码,解困惑

精彩的部分都在源码里面加了说明,先总体说下。在以下源码中,ax, dx,分别对应void *__restrict __dest, const void *__restrict __src,而cx对应size_t __n。这样就比较明显了:
1、ax进了di(目的地址寄存器),dx进了si(源地址寄存器),movsl从si向di复制内容
2、cx是用于rep的程序计数器。当rep重复 movsl指令时,自动从cx中减1。

聪明的同学可能要问,我把ax/dx换一下行不行,是不是就可以把src放在前面,dest放在后面了?理论上是这样的,不过从C89到C11这么多年了,就不要去改了吧!:)

其实真相只有一个:遵循fastcall的原则和调用约定,从右开始不大于4字节的参数放入CPU的ecx,edx,eax寄存器,其余参数从右向左入栈,从汇编实现上来看,也是遵循了这样一个调用约定。

以下是memcpy的代码,里面有2个精彩的部分,同学们可以好好体会一下。

SYM_FUNC_START_NOALIGN(memcpy) // 以下代码来源于\linux-6.1-rc1\arch\x86\boot\copy.S
	pushw	%si       // 保存好调用前别人的现场,不添麻烦
	pushw	%di       // 同上
	movw	%ax, %di  // void *__restrict __dest
	movw	%dx, %si  // const void *__restrict __src
	pushw	%cx       // 先存起来,后面有交代
	shrw	$2, %cx   // size_t __n,注意!精彩的来了,由于是movsl是4个字节,所以,对cx右移2次,相当于除4
	rep; movsl        // 第一次循环,发挥最大性能,4字节一起上,执行movsl并循环cx次(上面做了整除4,精彩吧!)
	popw	%cx       // 没有和4个字节对齐的剩余的部分也要处理,不然就漏掉了:)
	andw	$3, %cx   // andw求和取余,(3)=0x0011,为啥是3?(4)=0x0100,明白了吧,只要后面的就行
	rep; movsb        // 再来一次循环,这次movsb,按1字节复制。
	popw	%di       // fastcall,规矩做事,清理干净,恢复现场
	popw	%si       // 同上
	retl
SYM_FUNC_END(memcpy)

以下是memset的代码,其逻辑和上面的差不多。可以参考上面的批注理解。

SYM_FUNC_START_NOALIGN(memset)
	pushw	%di
	movw	%ax, %di
	movzbl	%dl, %eax   // 1个字节太慢,把eax用起来,先把dl的低位字节放到eax,等下面imull后,4个字节一起飞
	imull	$0x01010101,%eax   //注意,也是精彩的地方,把入参中用于设置的1个字节copy4份,为stosl准备加速复制
	pushw	%cx
	shrw	$2, %cx
	rep; stosl
	popw	%cx
	andw	$3, %cx   // 和memcpy一样,不多说,主要为了收尾处理剩下的不能补4整除的部分
	rep; stosb
	popw	%di
	retl
SYM_FUNC_END(memset)

结论

符合fastcall的调用约定本来就是从右向左的,对吧。(前面已经说明清楚了,凑一个结论吧:))

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

招财猫_Martin

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值