公司动态

C 语言字符串 内存函数:用法 + 模拟实现全攻略

📅 2026/7/14 7:14:25
C 语言字符串  内存函数:用法 + 模拟实现全攻略
C 语言字符串 内存函数全攻略从用法、坑点到手动模拟实现最近系统学习了 C 语言标准库中最核心的字符操作函数、字符串处理函数、内存操作函数三大模块这些函数是 C 语言开发的基石日常编码、笔试面试几乎处处都会用到。光会调用函数远远不够只有吃透底层逻辑、踩过坑、亲手模拟实现过才能真正做到灵活运用、避开陷阱。这篇博客我会结合课堂重点笔记再加上我自己手写的模拟实现代码和测试用例把这些函数的功能、用法、易错点、底层实现一次性讲透既是自己的学习复盘也希望能帮到同样在学习 C 语言的同学。一、字符操作函数字符操作函数是处理单个字符的工具分为字符分类函数和字符转换函数两大类所有函数都需要包含头文件ctype.h使用起来非常便捷比我们自己写条件判断要简洁、可读性强得多。1.1 字符分类函数这类函数的核心作用是判断传入的字符是否符合指定类型符合则返回非 0 整数不符合则返回 0。这里整理的常用字符分类函数如下表覆盖了绝大多数开发场景函数名符合条件返回真非 0iscntrl任何控制字符isspace空白字符空格、换行 \n、回车 \r、制表符 \t 等isdigit十进制数字字符 0~9isxdigit十六进制数字字符0-9、a-f、A-Fislower小写字母 a~zisupper大写字母 A~Zisalpha字母a~z、A~Zisalnum字母或数字ispunct标点符号非字母、数字的可打印图形字符isgraph任何图形字符isprint任何可打印字符含图形字符和空白字符课上重点讲了islower的用法其他函数的用法完全一致举个最常见的例子判断字符是否为小写字母再配合转换函数实现大小写转换。#include stdio.h #include ctype.h int main () { char str[] Test String.\n; int i 0; while (str[i]) { char c str[i]; // 判断是否为小写字母 if (islower(c)) { printf(%c 是小写字母\n, c); } i; } return 0; }1.2 字符转换函数C 语言标准库提供了两个专门的字符转换函数比我们手动用 ASCII 码加减 32 更规范、可读性更强int tolower ( int c );将大写字母转换为小写字母非字母字符不做修改int toupper ( int c );将小写字母转换为大写字母非字母字符不做修改课堂练习的经典案例将字符串中的小写字母全部转为大写其他字符保持不变用转换函数实现非常简洁#include stdio.h #include ctype.h int main () { char str[] Test String. 123!; int i 0; while (str[i]) { // 小写转大写 if (islower(str[i])) { str[i] toupper(str[i]); } i; } printf(转换结果%s\n, str); return 0; }运行结果转换结果TEST STRING. 123!二、基础字符串函数这部分是字符串处理最核心的基础函数包括求长度、拷贝、追加、比较四大基础操作所有函数都需要包含头文件string.h。2.1 strlen 字符串长度统计函数2.1.1函数核心信息函数原型size_t strlen ( const char * str );核心功能统计str指向的字符串中\0之前的字符个数不包含\0本身关键返回值返回值类型是size_t也就是无符号整数这是最容易踩的坑2.1.2必须注意的坑点字符串必须以\0结尾否则 strlen 会继续向后遍历内存直到找到\0返回的是随机的错误值返回值是无符号数两个 strlen 的结果相减永远不会得到负数。比如strlen(bbb) - strlen(abcdef)数学上是 3-6-3但因为是无符号数运算结果会是一个极大的正数导致判断逻辑出错。错误示例#include stdio.h #include string.h int main() { const char* str1 abcdef; const char* str2 bbb; // 错误strlen返回无符号数相减结果永远0 if(strlen(str2) - strlen(str1) 0) { printf(str2更长\n); // 会错误执行这一行 } else { printf(str1更长\n); } return 0; }正确的写法是直接比较大小而不是相减if(strlen(str2) strlen(str1))2.1.3我的模拟实现strlen 的三种经典模拟实现方式之前写过其中两种下面我将它们整合在一起每一种都有不同的思路能加深对 C 语言指针和循环的理解。方式 1计数器法最直观#include stdio.h #include assert.h // 计数器方式实现strlen int my_strlen(const char * str) { // 断言防止空指针 assert(str); int count 0; // 遍历到\0为止 while(*str) { count; str; } return count; } int main() { char str[] hello world; printf(长度%d\n, my_strlen(str)); return 0; }方式 2递归实现不创建临时变量#include stdio.h #include assert.h // 递归方式实现strlen int my_strlen(const char * str) { assert(str); // 递归终止条件遇到\0返回0 if(*str \0) return 0; // 递归调用每次指针后移一位长度1 else return 1 my_strlen(str1); } int main() { char str[] luminous; printf(长度%d\n, my_strlen(str)); return 0; }方式 3指针 - 指针法最简洁C 语言中两个指向同一块内存的指针相减得到的是两个指针之间的元素个数刚好可以用来求字符串长度。#include stdio.h #include assert.h // 指针-指针方式实现strlen int my_strlen(const char * str) { assert(str); const char *start str; // 遍历到\0的位置 while(*str) { str; } // 末尾指针 - 起始指针 元素个数 return str - start; } int main() { char str[] 123456789; printf(长度%d\n, my_strlen(str)); return 0; }2.2 strcpy 字符串拷贝函数2.2.1函数核心信息函数原型char* strcpy(char * destination, const char * source );核心功能将source源字符串中的内容包括结尾的\0完整拷贝到destination目标空间中返回值返回目标空间的起始地址支持链式访问2.2.2必须注意的坑点源字符串必须以\0结尾否则会拷贝内存中的随机内容造成越界会自动拷贝源字符串的\0到目标空间这是拷贝结束的标志目标空间必须足够大能容纳下源字符串的全部内容否则会造成缓冲区溢出目标空间必须是可修改的不能传入字符串常量只读内存否则会程序崩溃2.2.3我的模拟实现#include stdio.h #include assert.h // 模拟实现strcpy char* my_strcpy(char *dest, const char*src) { // 保存目标空间起始地址用于返回 char *ret dest; // 非空断言 assert(dest ! NULL); assert(src ! NULL); // 核心逻辑*src赋值给*dest然后两个指针都后移直到赋值完\0循环终止 while((*dest *src)) { ; // 空循环体所有操作都在while条件里完成了 } return ret; } // 测试代码 int main() { char arr1[20] {0}; char arr2[] hello, C语言; my_strcpy(arr1, arr2); printf(拷贝结果%s\n, arr1); return 0; }这个实现的核心是while((*dest *src))一行代码就完成了赋值、指针后移、循环终止的全部逻辑非常经典也是面试里常考的写法。2.3 strcat 字符串追加函数2.3.1函数核心信息函数原型char * strcat ( char * destination, const char * source );核心功能找到destination目标字符串结尾的\0从\0的位置开始把source源字符串的内容追加进去最终形成一个完整的新字符串返回值返回目标空间的起始地址支持链式访问2.3.2必须注意的坑点源字符串和目标字符串都必须以\0结尾源字符串保证追加的内容完整目标字符串保证能找到追加的起始位置目标空间必须足够大能容纳下原字符串 源字符串的总长度避免溢出目标空间必须可修改不能传入字符串常量标准库不支持自己给自己追加比如strcat(arr, arr)会因为覆盖\0导致死循环内存重叠场景要避免使用2.3.3我的模拟实现#include stdio.h #include assert.h // 模拟实现strcat char* my_strcat(char *dest, const char*src) { // 保存目标空间起始地址 char *ret dest; // 非空断言 assert(dest ! NULL); assert(src ! NULL); // 第一步找到目标字符串结尾的\0 while(*dest) { dest; } // 第二步从\0的位置开始拷贝源字符串和strcpy的逻辑一致 while((*dest *src)) { ; } return ret; } // 测试代码 int main() { char arr1[20] hello ; char arr2[] world!; my_strcat(arr1, arr2); printf(追加结果%s\n, arr1); return 0; }2.4 strcmp 字符串比较函数2.4.1函数核心信息函数原型int strcmp ( const char * str1, const char * str2 );核心功能逐字符对比两个字符串的 ASCII 码值从第一个字符开始相等就对比下一个直到遇到不相等的字符或者两个字符串都到结尾的\0标准规定的返回值第一个字符串大于第二个字符串返回大于 0的整数第一个字符串等于第二个字符串返回0第一个字符串小于第二个字符串返回小于 0的整数2.4.2必须注意的点很多初学者会写if(strcmp(str1, str2) 1)来判断大于这是错误的标准只规定了返回大于 0、小于 0、等于 0不同编译器的实现里大于 0 不一定是 1只要是正数就符合标准正确的写法是if(strcmp(str1, str2) 0)比较的是 ASCII 码值比如abc和abq第三个字符c的 ASCII 码是 99q是 113所以abc小于abq我的模拟实现#include stdio.h #include assert.h // 模拟实现strcmp int my_strcmp (const char * str1, const char * str2) { // 非空断言 assert(str1 ! NULL); assert(str2 ! NULL); // 字符相等就继续循环 while(*str1 *str2) { // 两个都到\0了说明完全相等返回0 if(*str1 \0) return 0; // 指针后移 str1; str2; } // 遇到不相等的字符返回ASCII码差值 return *str1 - *str2; } // 测试代码 int main() { char arr1[] abcdef; char arr2[] abq; int ret my_strcmp(arr1, arr2); if(ret 0) printf(arr1 arr2\n); else if(ret 0) printf(arr1 arr2\n); else printf(arr1 arr2\n); return 0; }三、长度受限的安全字符串函数上面的strcpy、strcat、strcmp都是不受长度限制的很容易造成缓冲区溢出的安全问题也是很多漏洞的来源。C 语言标准库提供了对应的长度受限版本strncpy、strncat、strncmp通过指定操作的字符个数大大提升了代码的可控性和安全性也是实际开发中更推荐使用的版本。3.1 strncpy 长度受限的字符串拷贝3.1.1库函数核心信息函数原型char * strncpy ( char * destination, const char * source, size_t num );核心功能从source源字符串拷贝最多num个字符到destination目标空间必须牢记的规则课堂重点若源字符串长度小于num拷贝完源字符串的\0后会继续在目标空间补\0直到凑够num个字节若源字符串长度大于等于num只会拷贝num个字符不会主动添加\0这是最容易踩的坑后续访问目标字符串可能会出现乱码目标空间必须足够大避免越界3.1.2我的模拟实现代码#define _CRT_SECURE_NO_WARNINGS #includestdio.h #includeassert.h // 模拟实现strncpy char* my_strncpy(char* destination, const char* source, size_t num) { // 断言避免空指针访问 assert(destination source); // 保存目标空间起始地址用于最终返回 char* dest_start destination; // 第一步拷贝源字符串字符直到num耗尽或源字符串结束 while (num 0 *source ! \0) { *destination *source; destination; source; num--; } // 第二步若num有剩余补全\0 while (num 0) { *destination \0; destination; num--; } return dest_start; } // 测试代码 int main() { char arr[] Hello,luminous!; char arr1[10] { 0 }; size_t num; scanf(%zu,num); // 安全处理避免输入长度超出目标数组容量 if (num sizeof(arr1)) { num sizeof(arr1) - 1; printf(输入的字符数超出范围自动调整为%zu\n, num); } char* p my_strncpy(arr1, arr, num); printf(拷贝结果%s,p); return 0; }3.2 strncat 长度受限的字符串追加3.2.1库函数核心信息函数原型char * strncat ( char * destination, const char * source, size_t num );核心功能从源字符串中最多追加num个字符到目标字符串的末尾追加完成后会自动在结尾添加\0关键规则课堂重点不管追加了多少个字符最终一定会在结尾补\0保证目标空间里是一个合法的字符串若源字符串长度小于num只会追加到\0为止不会额外补\0目标空间必须足够大能容纳原字符串长度 num 1结尾的 \0支持自己给自己追加不会出现 strcat 的死循环问题3.2.2用法示例#include stdio.h #include string.h int main() { char arr1[20] hello ; char arr2[] world!; // 最多追加3个字符 strncat(arr1, arr2, 3); printf(追加结果%s\n, arr1); // 输出 hello wor return 0; }3.3 strncmp 长度受限的字符串比较3.3.1库函数核心信息函数原型int strncmp ( const char * str1, const char * str2, size_t num );核心功能比较两个字符串最多比较num个字符按 ASCII 码值逐字符对比返回值规则和 strcmp 完全一致大于返回正数等于返回 0小于返回负数适用场景只需要对比字符串的前 N 个字符比如对比 URL 的前缀、文件名的开头不用遍历整个字符串效率更高也更安全3.3.2我的模拟实现代码#define _CRT_SECURE_NO_WARNINGS #includestdio.h #includeassert.h // 模拟实现strncmp int my_strncmp(const char* str1, const char* str2, size_t num) { // 非空断言 assert(str1 str2); while (num 0) { // 遇到不相等的字符直接返回差值 if (*str1 ! *str2) { // 转unsigned char避免符号位带来的问题 return (unsigned char)*str1 - (unsigned char)*str2; } // 遇到\0说明两个字符串都提前结束完全相等 if (*str1 \0) { return 0; } // 指针后移继续对比下一个字符 str1; str2; num--; } // 循环结束说明前num个字符完全相等 return 0; } // 测试代码 int main() { char arr[] Hello,luminous!; char arr1[] Hi,Luminous!; size_t num; scanf(%zu, num); int ret my_strncmp(arr, arr1, num); if (ret 0) { printf(前%zu字符比较中str1 str2\n, num); } else if (ret 0) { printf(前%zu字符比较中str1 str2\n, num); } else { printf(前%zu字符比较中str1 str2\n, num); } return 0; }四、字符串查找与分割函数在实际开发中查找子串、分割字符串是非常常见的需求C 语言标准库提供了strstr和strtok两个函数专门解决这类问题。4.1 strstr 字符串查找函数4.1.1库函数核心信息函数原型char * strstr ( const char * str1, const char * str2);核心功能在str1主串中查找str2子串第一次出现的位置返回值规则找到子串返回子串在主串中首次出现的起始地址找不到子串返回NULL特殊情况若子串是空字符串直接返回主串的起始地址4.1.2我的模拟实现代码这里用了最经典的暴力匹配算法逻辑清晰易懂完全符合标准库的功能要求非常适合入门学习。课件里也提到更高效的实现可以用 KMP 算法后续可以进阶学习。#includestdio.h #includeassert.h // 模拟实现strstr暴力匹配算法 char* my_strstr(const char* haystack,const char* needle) { // 非空断言 assert(haystack needle); // cp指针记录当前主串的匹配起始位置 const char* cp haystack; // 特殊情况子串为空直接返回主串起始地址 if (*needle \0) { return (char*)haystack; } // 遍历主串的每一个字符作为匹配起点 while (*cp) { const char* s1 cp; const char* s2 needle; // 逐字符匹配直到字符不相等或其中一个字符串结束 while (*s1 ! 0 *s2 ! 0 *s1 *s2) { s1; s2; } // 子串遍历结束说明完全匹配成功 if (*s2 \0) { return (char*)cp; } // 匹配失败主串匹配起点后移一位 cp; } // 主串遍历完都没匹配到返回NULL return NULL; } // 测试代码 int main() { char arr1[] abbbacd; char arr2[] bac; char* p my_strstr(arr1, arr2); if (p ! NULL) { printf(找到子串后续内容%s\n, p); } else { printf(找不到子串\n); } return 0; }4.2 strtok 字符串分割函数这个函数是 C 语言里专门做字符串分割的工具用法非常特殊也是课堂上的重点有很多必须记住的特性稍不注意就会踩坑。4.2.1库函数核心信息函数原型char *strtok(char *str, const char *delim);核心功能根据delim指定的分隔符集合把str字符串拆分成多个子串必须牢记的关键特性课堂重点破坏性修改strtok会直接修改原字符串把分隔符替换成\0如果需要保留原字符串必须先拷贝一份再对拷贝后的内容操作特殊调用规则首次调用传入待分割的字符串首地址后续继续分割同一个字符串第一个参数必须传NULL函数会内部记住上一次分割的位置连续分隔符处理多个连续的分隔符会被视为一个不会返回空字符串返回值成功返回当前分割出的子串首地址没有可分割的子串时返回NULL4.2.2我的测试代码#define _CRT_SECURE_NO_WARNINGS #includestring.h #includestdio.h int main() { // 待分割的邮箱地址 char arr[] wyhluminous.com; // 备份原字符串避免原内容被修改 char arr2[20] { 0 }; // 分隔符集合 和 . 都是分隔符 const char* delim .; strcpy(arr2, arr); char* res NULL; // 标准用法for循环完成全部分割 for (res strtok(arr2, delim); res ! NULL; res strtok(NULL, delim)) { printf(分割出的子串%s\n, res); } return 0; }这个例子里会把邮箱地址拆分成wyh、luminous、com三个子串非常适合处理格式化的字符串比如日志、网址、IP 地址、配置项等。五、错误信息处理函数写 C 语言代码时尤其是调用文件操作、内存分配这类标准库函数时经常会遇到调用失败的情况。想要快速定位失败原因就离不开strerror和perror这两个错误信息处理函数。5.1 strerror 错误码转字符串函数5.1.1库函数核心信息函数原型char* strerror ( int errnum );核心功能传入错误码errnum返回对应的错误信息字符串的首地址关键知识点C 语言有一个全局变量errno定义在errno.h头文件中程序启动时errno为 0无错误当标准库函数调用失败时会自动把对应的错误码赋值给errno它能把我们看不懂的数字错误码转换成人类可读的错误描述字符串使用时需要包含string.h和errno.h两个头文件5.1.2我的测试代码#define _CRT_SECURE_NO_WARNINGS #include stdio.h #include string.h #include errno.h int main() { // 以只读方式打开文件文件不存在时会打开失败 FILE* pf fopen(data.txt, r); if (pf NULL) { // 打印错误信息和对应的错误码 printf(打开失败失败原因是%s错误码%d,strerror(errno),errno); return 1; } else { printf(文件打开成功); } return 0; }当文件不存在时运行结果会输出打开失败失败原因是No such file or directory错误码2能非常直观地看到失败原因。5.2 perror 直接打印错误信息函数5.2.1库函数核心信息函数原型void perror(const char *s);核心功能可以理解为strerror的 “懒人版”直接完成错误信息的打印。它会先打印我们传入的自定义字符串s然后自动打印冒号、空格再打印当前errno对应的错误信息最后自动换行优势一行代码就能完成错误打印比strerror更简洁适合快速调试5.2.2我的测试代码#include stdio.h #include string.h #include errno.h int main() { FILE* pf fopen(data.txt, r); if (pf NULL) { // 直接打印自定义前缀错误信息 perror(打开文件失败原因是); } else { printf(打开成功); } return 0; }当文件不存在时运行结果会输出打开文件失败原因是: No such file or directory完全不用我们手动处理errno和格式化打印非常方便。5.2.3两个函数的选择建议如果你只需要把错误信息打印出来调试直接用perror最省事如果你需要把错误信息保存到日志文件、或者做二次处理就用strerror获取错误字符串再做后续操作六、内存操作函数前面的字符串函数只能处理以\0结尾的字符串一旦遇到整型数组、结构体、浮点型数组这类数据就无能为力了。而内存操作函数直接对内存的字节进行操作完全不关心内存里存的是什么数据类型通用性极强是 C 语言里的 “万能操作工具”所有内存函数都需要包含头文件string.h。6.1 memcpy 内存拷贝函数6.1.1库函数核心信息函数原型void * memcpy ( void * destination, const void * source, size_t num );核心功能从source指向的内存位置拷贝num个字节的数据到destination指向的内存位置核心特点按字节拷贝不关心数据类型任何类型的数据都能处理标准规定memcpy不处理重叠的内存如果源和目标内存有重叠复制结果是未定义的重叠场景必须用memmove返回目标内存的起始地址6.1.2我的模拟实现代码#includestdio.h #includeassert.h // 模拟实现memcpy void* my_memcpy(void* destination, const void* source,size_t num) { // 非空断言 assert(destination source); // 保存目标内存起始地址用于返回 char* dest (char*)destination; const char* src (const char*)source; // 按字节逐个拷贝 for (size_t i 0; i num; i) { dest[i] src[i]; } return destination; } // 测试代码 int main() { // 拷贝整型数组 int arr1[] { 1,2,3,4,5 }; int arr2[10] { 0 }; // 拷贝3个int元素每个int4字节总共12字节 my_memcpy(arr2, arr1, 12); // 打印拷贝结果 printf(整型数组拷贝结果); for(int i0; i5; i){ printf(%d , arr2[i]); } // 也可以拷贝字符串 char str1[] Hello, World!; char str2[20]; my_memcpy(str2, str1, sizeof(str1)); printf(\n字符串拷贝结果%s\n, str2); return 0; }实现的核心是把void*强转成char*因为char类型刚好占 1 个字节完美适配按字节拷贝的需求这也是 memcpy 能处理任意数据类型的关键。6.2 memmove 内存移动函数memmove是memcpy的 “增强版”功能和memcpy几乎完全一致核心的区别就是memmove 可以安全处理内存重叠的场景这也是课堂上的重点和难点。6.2.1什么是内存重叠举个例子有一个数组int arr[] {1,2,3,4,5,6,7,8,9,10};我们想把前 5 个元素拷贝到arr2的位置也就是从数组下标 2 开始存放。这时候源内存是arr[0]-arr[4]目标内存是arr[2]-arr[6]两块内存有重叠。如果用memcpy从前向后拷贝拷贝arr[0]到arr[2]、arr[1]到arr[3]之后原本的arr[2]已经被覆盖成了 1再拷贝的时候就会拿到错误的数据最终结果不符合预期。而memmove就是专门解决这个问题的。6.2.2库函数核心信息函数原型void * memmove ( void * destination, const void * source, size_t num );核心功能拷贝num个字节的数据支持源与目标内存重叠的场景实现核心根据内存重叠的情况选择不同的拷贝方向当目标地址dest小于源地址src从前向后拷贝和 memcpy 一致不会覆盖当目标地址dest大于源地址src且dest src num内存重叠必须从后向前拷贝先拷贝最后一个字节再往前拷贝避免覆盖未读取的源数据6.2.3我的模拟实现代码#includestdio.h #includeassert.h // 模拟实现memmove void* my_memmove(void* destination, const void* source, size_t num) { // 非空断言 assert(destination source); 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 destination; } // 测试代码内存重叠场景 int main() { int arr[] { 1,2,3,4,5,6,7,8,9,10 }; // 重叠内存拷贝把arr开头的20字节5个int拷贝到arr2的位置 my_memmove(arr 2, arr, 20); // 打印结果 printf(内存重叠拷贝结果); for (int i 0; i 10; i) { printf(%d , arr[i]); } return 0; }测试代码运行后会输出1 2 1 2 3 4 5 8 9 10完全符合预期完美解决了内存重叠的拷贝问题。另外Visual studio也把memcpy内存重叠的拷贝问题解决了但是并不是所有编译器都能把memcpy做到完美。所以日常开发中如果你不确定内存是否会重叠直接用memmove准没错它的兼容性更强风险更低。6.3 memset 内存设置函数6.3.1库函数核心信息函数原型void * memset ( void * ptr, int value, size_t num );核心功能把ptr指向的内存的前num个字节统一设置为value值函数内部会自动转换为unsigned char必须注意的坑点它是按字节设置的这是最容易踩的坑比如给 int 数组设置初始值不能写memset(arr, 1, 20)这样每个字节都会被设为 1一个 int 有 4 个字节最终 int 元素的值会变成0x01010101十进制 16843009而不是我们想要的 1最安全的用法把内存块初始化为 0memset(arr, 0, sizeof(arr))因为每个字节设为 0不管什么类型最终值都是 0完全没问题给字符数组批量设置字符比如memset(str, x, 6)也是完全正确的因为 char 类型刚好占 1 个字节6.3.2用法示例#include stdio.h #include string.h int main () { char str[] hello world; // 把前6个字节设置为x memset(str,x,6); printf(设置结果%s\n, str); // 输出 xxxxxxworld return 0; }6.4 memcmp 内存比较函数6.4.1库函数核心信息函数原型int memcmp ( const void * ptr1, const void * ptr2, size_t num );核心功能比较两块内存的前num个字节按字节逐位对比对比规则和strcmp完全一致返回值规则前num个字节完全相等返回0遇到第一个不相等的字节若ptr1的字节值更大返回大于 0的整数反之返回小于 0的整数核心优势可以比较任意类型的数据比如比较两个结构体、两个整型数组的前 N 个元素比字符串比较函数的通用性强太多6.4.2示例#include stdio.h #include string.h int main() { char buffer1[] DWgaOtP12df0; char buffer2[] DWGAOTP12DF0; // 比较两个字符串的全部字节 int n memcmp(buffer1, buffer2, sizeof(buffer1)); if (n 0) printf(%s 大于 %s.\n, buffer1, buffer2); else if (n 0) printf(%s 小于 %s.\n, buffer1, buffer2); else printf(%s 和 %s 一样.\n, buffer1, buffer2); return 0; }七、学习总结这次系统学习下来我最大的感受就是C 语言的这些标准库函数看似调用简单实则里面藏了超多细节和坑点。光靠背函数用法、记参数很容易在实际开发中踩坑只有自己动手去模拟实现一遍一步步调试才能真正理解底层的设计逻辑知道为什么要这么写使用的时候才能下意识地避开风险。比如strlen返回值的无符号数陷阱、strncpy不自动补\0的坑、strtok的破坏性修改、memset按字节设置的注意事项、memmove的内存重叠处理这些知识点自己写一遍代码、踩一遍坑就彻底记牢了比死记硬背高效太多。这些函数是 C 语言开发的基本功不管是日常写代码还是找工作的笔试面试都是必考的内容。后续我还会继续学习 C 语言的文件操作、结构体、指针进阶、动态内存分配等内容也会持续把学习笔记、模拟实现代码整理成博客既是复盘也希望能和大家一起交流进步。