多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

15.字符函数

15.字符函数 一、字符串函数操作以 \0 结尾的字符串大白话前提字符串背后一定藏着一个看不见的标记\0它代表字符串到此结束。所有字符串函数干活看到\0就停下。1. strlen —— 求字符串有效长度功能数出\0前面有多少个字符。\0本身不算长度 返回值size_t本质是无符号正数不能小于 0三种实现方式//方式1计数器法最容易看懂新手首选 size_t My_strlen(char* arr) { size_t i 0; while (arr[i] ! \0) { i; } return i; } //方式2指针‑指针高级指针写法考试高频 size_t My_strlen1(char* arr) { char* p arr; // p记住字符串开头地址 while (*p) // *p不等于\0就往后走 p; return p - arr; // 末尾地址 − 起始地址 字符个数 } //方式3递归写法理解即可实际开发不推荐 size_t My_strlen2(char* arr) { if (*arr \0) return 0; else return 1 My_strlen2(arr 1); }小白大坑 递归里面绝对不能写My_strlen2(arr)。后置 是先把旧地址传进去指针再往后走永远访问同一个位置 → 死循环只能写arr1代表传给下一层下一个字符的地址。strlen 返回无符号数不要写strlen(a)-strlen(b)0。无符号数字永远 ≥ 0永远不会小于零判断失效。推荐写法if(strlen(a) strlen(b))避免减法。2. strcpy 字符串拷贝功能把源字符串完完整整复制到目标数组连看不见的结束符\0也要一起复制过去。最简模拟实现void My_strcpy(char* dest, char* src) { while (*dest *src); }一步一步拆解循环*dest *src把源当前字符赋值给目标位置 判断赋值完的值是不是\0是就结束循环dest、src两个指针同时向后挪一格易错坑 目标数组空间必须足够大如果装不下源字符串 → 越界程序直接崩溃。3. strcat 字符串追加拼接功能找到目标字符串末尾看不见的\0删掉这个\0从这里开始把源字符串贴上去。补充目标数组必须本身自带\0strcat 依靠目标原来的\0找到追加起点没有\0会内存越界。普通版本❌不能自己追加自己 strcat (arr,arr)void My_strcat(char* dest, char* src) { //第一步指针一路走到dest末尾\0的位置 while (*dest) dest; //第二步开始拷贝字符串连带\0 while (*dest *src); }大白话解释为什么自己拼接自己会翻车 举个例子arrabcde一开始末尾有\0。刚开始拷贝第一个字符的时候就直接把末尾\0覆盖掉了。源字符串和目标字符串是同一块空间从此以后源字符串再也找不到结束标记\0无限循环程序卡死。✅安全升级版✅支持自己追加自己void My_strcat_pro(char arr[], char d[]) { //第一步先把源字符串备份一份到临时数组保护原字符串 char temp[1000]; int i 0; while (temp[i] d[i]) i; //第二步指针走到目标字符串末尾 while (*arr) arr; //第三步从备份数组往目标空间追加 i 0; while (*arr temp[i]); }原理先复制一份副本副本不受破坏追加的时候就不怕把\0覆盖掉。4. strcmp — 字符串比较【新增】功能按 ASCII 码逐字符对比遇到不同字符或者\0停止 原型int strcmp(const char *str1, const char *str2);str1 str2 → 返回大于 0 的数str1 str2 → 返回 0str1 str2 → 返回小于 0 的数大坑不能用直接比较字符串数组if(arr1 arr2)比较的是地址不是里面字符模拟实现int My_strcmp(const char* str1, const char* str2) { //两个字符相等且不是\0继续往后比 while(*str1 *str2) { if(*str1 \0) //同时到末尾字符串完全相等 return 0; str1; str2; } //返回差值ascii相减 return *str1 - *str2; }二、内存函数memcpy、memmove大白话内存函数根本不认识\0它就是一台 “字节复印机”。不管里面存的是字符、int 整数、小数只看你要求复制多少个字节。可以复制任意类型的数据。1. memcpy 内存拷贝原型void* memcpy(void* destination, const void* source, size_t num);destination目标地址source源地址num复制多少个字节基础实现标准原版没有重叠保护void* my_memcpy(void* destination, const void* source, size_t num) { //空指针安全检查传入无效地址直接返回 if (destination NULL || source NULL) { return NULL; } void* ret destination; //保存起始地址最后返回 //void*万能指针不能直接解引用强制转为char*按1字节操作 char* dest (char*)destination; const char* src (const char*)source; //从前往后一个字节一个字节复制 for (size_t i 0; i num; i) { dest[i] src[i]; } return ret; }memcpy 致命短板不能处理内存重叠 大白话比喻两本有一部分重合的书你从前往后抄。前面刚抄走的内容还没读完就被覆盖掉读到后面内容就错了。两块内存区域有重合复制结果出错。考试要点部分编译器库 memcpy 可能兼容重叠但做题严格按 C 标准重叠就必须使用 memmove2. memmove 可重叠的安全内存移动memmove memcpy 重叠判断。重叠也不会出错。完整正确模拟实现void* my_memmove(void* destination, const void* source, size_t num) { if (destination NULL || source NULL) { return NULL; } void* ret destination; char* dest (char*)destination; const char* src (const char*)source; //判断重叠目标地址落在源数据的范围内并且目标在源后面 if (dest src dest src num) { //从后往前拷贝先抄最后一个字节防止内容被提前覆盖 for (size_t i num; i 0; i--) { dest[i - 1] src[i - 1]; } } else { //无重叠或者目标地址在源前面 → 正常从前向后复制 for (size_t i 0; i num; i) { dest[i] src[i]; } } return ret; }复制方向口诀 目标地址 源地址 并且两块内存重叠 → 从后往前拷 剩下所有别的情况 → 从前往后拷memcpy vs memmove vs strcpy vs strcat vs strcmp 完整对比表表格函数特点遇到 \0 是否停下重叠内存是否安全适用场景strcpy字符串拷贝✅停下❌不安全只拷贝字符串strcat字符串追加✅停下❌不能自追加字符串拼接strcmp字符串逐字符比较✅停下-比较两个字符串内容memcpy按字节拷贝❌不会停下❌出错两块完全分开、互不重合的内存memmove安全字节拷贝❌不会停下✅安全不确定是否重叠 / 已知内存重叠三、小白做题必背区分口诀操作字符串依靠看不见的结束标记\0停止 → strlen、strcpy、strcat、strcmp字符串函数 复制 int 数组、结构体不在乎有没有\0指定复制字节数量 → memcpy /memmove内存函数 两块复制区域有可能重合打架 → 用 memmove 自己拼接自己strcat (arr,arr)→ 先用临时数组备份源字符串 字符串相等判断别用 strcmp 才对刷题坑点汇总strlen不算\0sizeof算上\0例char arr[]abcstrlen3sizeof4内存函数拷贝单位是字节拷贝 int 数组num 参数要写元素个数 * sizeof(int)void*万能指针不能解引用、不能自增必须强转 char * 逐字节操作strcat 追加前提目标数组有足够空间 目标字符串自带\0strlen 返回无符号数减法比较长度会踩坑
返回列表