C语言核心字符串与内存函数详解及模拟实现
深入解析C语言中核心的字符串与内存操作函数,包括strlen、strcmp、strcpy、strcat、strstr以及memcpy、memmove和memcmp。文章不仅阐述了各函数的功能特性与安全使用规范,还通过手写模拟实现代码,剖析底层字节拷贝与地址越界问题,是理解C语言底层机制的实用指南。
一、核心字符串函数
C语言标准库提供了丰富的字符串处理工具,使用它们的前提是必须理解其内部机制,尤其是以 '\0' 作为结束标志的特性。
第1步:获取长度 - strlen
size_t strlen ( const char * str ) 用于计算字符串长度。其返回值为无符号整数 size_t,统计的是从起始地址到第一个 '\0' 之前的字符数量(不包含 '\0' 本身)。
关键注意点:
- 传入的指针必须指向以
'\0'结尾的有效字符串。 - 由于返回类型是无符号类型,计算长度时若涉及减法运算需防止下溢。
模拟实现
size_t my_strlen(const char* str)
{
size_t count = 0;
while(*str) // 遇到 '\0' 时循环停止
{
str++;
count++;
}
return count;
}
第2步:比较大小 - strcmp
int strcmp ( const char * str1, const char * str2 ) 从左至右逐个比较两个字符串的 ASCII 码值,直到发现不相等的字符或其中一个字符串结束。
返回值规则:
- str1 > str2:返回大于 0 的值。
- str1 = str2:返回 0。
- str1 < str2:返回小于 0 的值。
模拟实现
int my_strcmp(const char* str1, const char* str2)
{
while (*str1 == *str2)
{
if (*str1 == '\0') // 两者同时到达末尾,相等
return 0;
*str1++;
*str2++;
}
// 此时 str1 != str2,直接返回差值
return *str1 - *str2;
}
第3步:拷贝字符串 - strcpy
char* strcpy(char * destination, const char * source) 将源字符串连同末尾的 '\0' 一起复制到目标空间,并返回目标字符串的起始地址。
安全约束:
- 目标空间必须足够大,足以容纳源字符串。
- 目标空间必须是可修改的左值(如数组,而非指针常量)。
模拟实现
char *my_strcpy(char *dest, const char*src)
{
char *ret = dest;
while((*dest++ = *src++))
{
; // 利用赋值表达式的返回值进行循环判断,包含复制 '\0'
}
return ret;
}
第4步:追加字符串 - strcat
char * strcat ( char * destination, const char * source ) 将源字符串追加到目标字符串后面。它会先找到目标字符串末尾的 '\0',然后开始覆盖写入源字符串内容(包含最后的 '\0')。
约束:目标空间必须有足够的剩余空间,且必须可修改。
模拟实现
char* my_strcat(char* dest, const char* src)
{
// 定位到dest末尾的 '\0'
char* start = dest;
while (*dest) dest++;
// 开始追加
while ((*dest++ = *src++) != '\0') {
;
}
return start;
}
第5步:查找子串 - strstr
char * strstr ( const char * str2, const char * str1 ) 在 str2(母串)中查找 str1(子串)。若找到,返回子串在母串中的首地址;否则返回 NULL。
**注意**:该算法为暴力匹配,时间复杂度较高,但在日常使用中已足够应对多数场景。
模拟实现
char* my_strstr(const char* haystack, const char* needle)
{
if (!*needle) return (char*)haystack; // 空串是任意串的子串
const char* ret1 = haystack;
while (*ret1)
{
const char* cp = ret1;
const char* ret2 = needle;
// 逐字符匹配
while (*cp && *ret2 && *cp == *ret2) {
cp++; ret2++;
}
if (!*ret2) return (char*)ret1; // 子串遍历完,匹配成功
ret1++; // 失败则母串后移一位重试
}
return NULL;
}
二、内存操作函数
当需要处理非字符串的二进制数据或对象时,内存函数比字符串函数更通用。
第6步:内存复制 - memcpy
void * memcpy ( void * destination, const void * source, size_t num ) 负责从源内存地址复制 num 个字节到目标内存地址。它基于字节进行拷贝,不关心数据类型。
模拟实现:
void * my_memcpy(void * dst, const void * src, size_t count)
{
void * ret = dst;
char * d = (char *)dst;
const char * s = (const char *)src;
while (count--) {
*d++ = *s++; // 按字节移动
}
return(ret);
}
第7步:安全内存复制 - memmove
memmove 同样复制 num 个字节,但设计初衷是为了处理源和目标内存区域重叠的情况。
为什么需要 memmove?
当使用 memcpy 对重叠区域(如数组向前或向后搬移)进行操作时,如果拷贝方向不当,可能会破坏尚未被拷贝的原始数据。
例如:将数组前面的部分搬到后面 memcpy(arr+2, arr, 16):
[1,2,3,4,5,6...] → 前两个字节被改写为 [1,2,1,2,3,4...],导致原本位于位置3,4的数据丢失。
对于这种情况,C语言提供了一个更安全的替代者
memmove。
memmove 的实现策略:根据源地址和目标地址的相对关系决定拷贝方向。
- 若
dest < src:从前向后拷贝。 - 若
dest >= src:从后向前拷贝(避免覆盖未读数据)。
模拟实现
void* my_memmove(void* dest, const void* src, size_t num)
{
char* ret = (char*)dest;
if ((char*)dest <= (const char*)src) {
// 正常顺序拷贝(从前向后)
while (num--)
{
*(char*)dest++ = *(char*)src++;
}
}
else {
// 逆序拷贝(从后向前),防止高地址数据被低地址覆盖
char* d = (char*)dest + num - 1;
const char* s = (const char*)src + num - 1;
while(num--)
{
*d-- = *s--;
}
}
return ret;
}
第8步:内存比较 - memcmp
int memcmp ( const void * ptr1, const void * ptr2, size_t num ) 比较两块内存的前 num 个字节。与 strcmp 类似,ptr1 对应的字节大于 ptr2 则返回正值,相等返回 0,小于返回负值。
三、总结与安全建议
掌握 C 语言字符串与内存函数,关键在于理解指针操作与内存布局。实际开发中需注意以下几点:
- 缓冲区溢出:使用 strcpy、strcat 前务必确保目标空间足够大,优先使用 strncpy、strncat 等 safer 版本。
- 重叠内存:只要涉及同一内存块的操作,一律优先选用 memmove。
- 指针有效性:所有指针在使用前应确保非空,且指向合法的读写权限内存。
以上就是C语言核心字符串与内存函数详解及模拟实现的详细内容,更多关于C语言字符串与内存函数实操的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









