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

简介: 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的调用约定本来就是从右向左的,对吧。(前面已经说明清楚了,凑一个结论吧:))

相关文章
|
数据可视化 算法 JavaScript
【Python数据挖掘】数据可视化及数据对象的相似性度量算法详解(超详细 附源码)
【Python数据挖掘】数据可视化及数据对象的相似性度量算法详解(超详细 附源码)
845 0
|
移动开发 小程序 API
【产品上新】openURL接口开放,实现在小程序与H5之间“反复横跳”
【产品上新】openURL接口开放,实现在小程序与H5之间“反复横跳”
813 0
|
人工智能 数据中心 芯片
液冷是大模型对算力需求的必然选择?|英伟达 GTC 2024六大亮点
在这个以高性能计算和大模型推动未来通用人工智能时代,算力已成为科技发展的隐形支柱。本文将重点探讨算力的演进,深入分析在不同领域中算力如何成为推动进步的基石;着眼于液冷如何突破算力瓶颈成为引领未来的先锋,对液冷散热的三种方式(冷板式、浸没式和喷淋式)做了详细的对比分析、成本测算和市场空间预测。并为您提供一份“实用教程”,指导如何将普通服务器改装为液冷服务器,以应对越来越复杂的计算需求。
1252 3
|
11月前
|
人工智能 监控 关系型数据库
5 分钟 SAE 极速部署 Dify,赢取户外折叠椅和社区积分
Dify.ai开源后,全球开发者已构建超2.3万个AI应用。阿里云Serverless应用引擎(SAE)提供一键部署Dify方案,支持秒级伸缩与高可用部署,降低运维成本。现部署测试环境可赢取礼品。
5 分钟 SAE 极速部署 Dify,赢取户外折叠椅和社区积分
|
安全 网络协议 Linux
F5 BIG-IP 17.5 LTS - 多云安全和应用交付
F5 BIG-IP 17.5 LTS - 多云安全和应用交付
426 3
F5 BIG-IP 17.5 LTS - 多云安全和应用交付
|
新能源 API 开发者
车辆限行查询API的实战指南:让限行管理从此 “有码可循”
随着全国机动车保有量突破4.53亿辆,交通拥堵与污染问题日益严峻,各城市陆续实施限行政策。探数API推出的车辆限行查询服务覆盖200+城市,提供实时限行数据,包括本地/外地燃油车及新能源车的限行规则、区域和时间等信息。其功能涵盖单个城市限行政策查询与支持城市的全面列表,助力用户精准规划出行。通过HTTP POST请求即可轻松接入,适用于导航平台和个人开发者。在“双碳”目标下,该API推动绿色出行与智能交通发展,为个人、企业和城市治理提供高效解决方案。
964 5
|
存储 数据库
LabVIEW如何修复或重置NI MAX数据库文件
LabVIEW如何修复或重置NI MAX数据库文件
649 0
|
数据采集 缓存 搜索推荐
301重定向:数字世界的永恒路标
301重定向是HTTP协议中用于资源永久迁移的技术,具有持久缓存和SEO权重传递特性。它在网站改版、流量过渡及移动端适配中发挥关键作用,同时可通过多种技术方案实现,具备显著的商业价值和品牌保护效益。
475 0
centos linux内核下载
centos linux内核下载