当前位置: 首页 > news >正文

C重要库实现

一.strlen实现

str的原理:它的作用是计算字符串长度,根据这个知识我们来写代码吧。

int MyStrlen(const char* s) { int count = 0;//用于计算长度 while(*s != '\0') { count++; s++; } return count; }

其实strlen的模仿是比较简单的,只要死抓strlen是以'\0'结尾的即可,下面我用图带着大家理解一下吧。

其实就是这样的,s每次++其实就是往上移动一格,直到碰到'/0'才会停止,我们就轻松搞定了strlen的模拟实现。

二.strcpy

strcpy的原理:将源字符串src(包括结束符'\0')复制到目标地址dest指向的内存空间中。

strcpy(char* dest,char* src)

根据strcpy的定义其实也能清楚它到底要干嘛,将src拷到dest你可以理成把src的字符一个一个拷到dest即可。

下面我们就来实现它吧!

char* strcpy(char* des, const char* src) { assert(des != NULL);//看是不是NULL assert(src != NULL); char* str = des;//保留des用于返回 while (*src != '\0') { *str = *src; str++; src++; } *str = '\0'; return des; }

但是这个代码其实是有槽点的很且很不安全,下面我们用图来分析这个槽点是什么
你发现了没,如果真的要把src内容全部塞给dest那么就会导致dest访问越界,这也C语言中strcpy的一个安全隐患

三.strcmp

strcmp的原理:按字典顺序依次比较两个字符串的字符大小(即:从第一个字符开始依次往下比)前者大返回一个任意整数(我将用1代替)相等返回0,后者大返回任意一个负数(我用-1代替)

int strcmp(const char* str1, const char* str2) { assert(str1); assert(str2); const char* s1 = str1; const char* s2 = str2; while (*s1 != '\0' && *s2 != '\0') { if (*s1 > *s2) { return 1; } else if (*s1 < *s2) { return -1; } else { s1++; s2++; } } if (*s1 == '\0' && *s2 =='\0') { return 0; } else if (*s1 != '\0') { return 1; } else { return -1; } }

循环未终止时,结果只能是1或-1,因为还有字符没有比。若循环终止时,则存在三种可能情况:前者字符串到达终止符'\0'、后者字符串到达'\0',或双方同时到达'\0'。

四.strstr

strstr的原理:在一个字符串(主串)中找另一个字符串(子串)首次出现的位置

char* strstr(const char* str1, const char* str2) { assert(str1 != NULL); assert(str2 != NULL); if (!*str2) return((char*)str1);//这里强转是因为C的一个特殊的情况,就是参数是const char*返回的确实char* const char* cp = str1; const char* s1, * s2; while (*cp) { s1 = cp; s2 = str2; while (*s1 == *s2 && *s2 != '\0') { s1++; s2++; } if(*s2 == '\0' ) return (char*)cp; cp++; } return(NULL); }

防止野指针与空字符串处理
assert用于确保str1str2非空指针,避免野指针问题。若str2为空字符串,直接返回str1,因为空串是任何字符串的子串。

循环核心逻辑
外层循环通过cp遍历str1的每个字符作为匹配起点。内层循环通过s1s2同步移动比较字符,若完全匹配(*s2 == '\0'),则返回当前cp的位置。若中途不匹配,则外层循环跳到下一个字符重新开始匹配。

可优化点说明

  1. 循环边界优化
    外层循环可限制为strlen(str1) >= strlen(str2)的情况,避免无效比较。例如str1="ab"str2="abc"无需比较。

  2. 两层循环的必要性
    str1="bbac"str2="bac"为例:

  • 第一次从str1的第一个'b'开始匹配,内层比较到第二个字符'b' != 'a'失败。
  • 外层循环跳到第二个'b'重新匹配,此时内层完整匹配"bac",返回正确结果。
  1. 终止条件*s2 != '\0'的作用
    防止比较越界。例如str1="abc\0xxx"str2="abc",当s2指向'\0'时匹配成功,若未检查可能导致s1s2继续向后访问,引发越界。

五.memcpy

memcpy的原理:其核心是按字节,逐个复制内存数据,不关心数据的类型(因此是 “内存拷贝”)

//des:目的地,src:来源,count:拷贝几个字节 void* memcpy1(void* dst, const void* src, size_t count) { assert(dst); assert(src); char* s1 = (char*)dst; char* s2 = (const char*)src; while (count--) { *s1 = *s2; s1++; s2++; } return dst; }

其实memcpy和strcpy原理都是差不多的,不过memcpy要实现的是任意类型,而strcpy只能实现字符类型,其实要理解这个代码只需要认识内存即可,下面我通过一张图来展示

 这里我们以整数类型为例(int n = 4)。整数类型在内存中占4个字节空间,其地址取最低位的那个字节地址。只要保证这4个字节空间连续存储即可。接下来我们重点讨论内存移动的问题。

为什么使用void指针?可以将其理解为无类型属性(就像五行中的"无"),而其他类型都有特定属性(金、木、水、火、土)。要实现所有类型的拷贝,就需要一个能承载各种类型的通用载体,void*(无类型)显然是最合适的选择。

但为什么又要转为char类型呢?因为void*不能直接解引用操作,只能作为载体使用。选择char类型是因为:char类型大小为1字节,因此char指针每次移动1字节这样就能确保精确控制内存移动。如果使用short(每次移动2字节),就无法正确处理char类型数据(1字节),会导致产生垃圾数据。

理解了这些,memcpy的工作原理就清晰了。相信聪明的你已经明白了其中的原理。

但是这个代码是可能有问题的,至于什么问题下面我用图来展示。

你看第一张图是符合memcpy(不会出现问题)但是你看第二张图很明显出了问题,为什么呢?

其实我们根据代码可以知道,我们是用src取赋值给dst,但是如果你dst在src前面就会导致一个问题,我src把第一格的内容给了dst(src下一格)那么我在取下一格不就是src上一格的内容吗,因此就会导致问题,而这个问题就叫内存重叠。

我们知道了memcpy当dst在src前面的时候会出现内存重叠,但是在前面多少不会出现内存重叠呢?

其实就是保证dst不会触碰到src的地址,即dst起点>src终点。

六.memmove

memmove的出现就是为了解决内存重叠的问题的

void* memmove(void* dst, const void* src, size_t count) { void* ret = dst; char* dst1 = (char*)dst; const char* src1 = (const char*)src; if (dst1 < src1 || dst1 > src1 + count)//确保元素修改后不在使用 { while (count--) { *dst1 = *src1; dst1 ++; src1 ++; } } else { dst1 = dst1 + count - 1; src1 = src1 + count - 1; while (count--) { *dst1 = *src1; dst1 --; src1 --; } } return ret; }

从代码中可以看出,memmove 的解决方案体现在 else 语句块中。让我们分析这段代码的具体作用吧。

memcpy 之所以会出现内存重叠问题,是因为 src 可能将自身资源复制到 src+n 的位置。这样后续操作时,src 可能会再次访问这个位置。要解决这个问题,关键在于避免 src 将资源复制到自己将要访问的位置。

解决方案很简单:让 src 把已复制的内容全部放在目标位置之后。这样就能确保 src 不会重复处理同一个内容。

这次我讲的是面试高频率考的函数实现,希望能给大家带来帮助,如果有误可以评论我,我们抓紧修改的,希望能大家一起进步,我的一些解释采用了AI润色,希望和它的帮助下能给大家带来简洁明了的解释

http://www.cnnetsun.cn/news/1282388.html

相关文章:

  • 使用GitHub Actions 安全部署到阿里云 ECS
  • 尝试用openclaw完成一个复杂的开发任务(持续更新)
  • “是我!”庆祝马里奥40年来始终坚持的匠心精神
  • 2026高职大数据技术专业考什么证书有用?
  • OpenClaw 超级 AI 实战专栏【基础操作与核心概念】(九)工程结构:目录、文件、模型、数据组织
  • PPT小白必看:从Word到PPT的5分钟高效转换技巧(附字体版权避坑指南)
  • 企业必看:Confluence远程命令执行漏洞(CVE-2022-26134)防御指南与修复方案
  • Hardhat 3测试框架终极选择指南:Node Test Runner vs Mocha实战对比
  • Coordinate Attention: Revolutionizing Lightweight Mobile Networks with Spatial-Channel Synergy
  • 金融风控领域的深度学习模型训练环境实践
  • Qwen2.5-VL-7B-Instruct效果展示:低资源语言(如泰语/越南语)图文理解实测
  • DDR5内存上电初始化全解析:从RESET信号到稳定工作的完整流程(附时序图)
  • 5G网络切片:如何为垂直行业打造定制化虚拟专网
  • 腾讯优图AI解析实测:上传图片自动识别文字、表格、公式、印章
  • Java数据结构|String类(二)+反射枚举Lambda+泛型的进阶
  • 告别TeamViewer?在Ubuntu上使用VNC Viewer实现轻量级远程控制的3种方法
  • 基于微信小程序的优购电商的设计与实现+ssm毕业论文
  • Hbuilder X最新版真机调试全攻略:从安卓到iOS的避坑指南
  • ESP32-H2安全架构解析:寄存器控制、硬件加速与可信启动
  • Swift面试必备:深入解析高频技术点与实战应用
  • OpenWrt UCI 命令行实战:从网络配置到Luci管理界面部署
  • CasRel模型在固件分析报告生成中的应用:自动化提取漏洞与组件关系
  • all-MiniLM-L6-v2多场景落地:客服问答匹配、合同条款相似性分析、简历筛选
  • C# 异步编程太难?一文搞懂回调、轮询、async/await 三种模式
  • 字节:早阶段视觉令牌剪枝EvoPrune
  • Debian安装openclaw
  • yz-女生-角色扮演-造相Z-Turbo与SpringBoot集成实战
  • 优化网络体验:如何手动调整有线与WiFi的跃点数设置
  • 从DCU到SoC:解析汽车电子架构演进中的核心计算单元
  • MP4文件格式深度剖析:从Box结构到媒体流解析