C语言常用字符串与内存函数详解及实现
C语言标准库提供了丰富的字符串与内存操作接口。理解这些函数的底层实现原理、参数约束及潜在风险(如缓冲区溢出、重叠内存处理),是编写高质量C代码的关键。本文将详细讲解`strlen`、`strcpy`等字符串函数及`memcpy`、`memmove`等内存函数的用法与核心逻辑。
长度不受限制的字符串函数
第1步:使用 strlen 获取字符串长度
目的:计算以 '\0' 结尾的字符串中字符的数量。
入口:#include <string.h>
动作:调用 size_t strlen(const char *str)。
关键参数:str 必须指向以 '\0' 结束的合法字符串。
完成状态:返回无符号整数,表示 '\0' 之前的字符个数。
注意:若字符串不含 '\0',函数将越界访问导致未定义行为。返回值类型为 size_t,参与算术运算时需注意符号问题。
第2步:使用 strcpy 进行字符串复制
目的:将源字符串完整复制到目标缓冲区。
入口:char* strcpy(char *dest, const char *src)
动作:src 指向的内容被逐字节拷贝至 dest,包含终止符 '\0'。
关键约束:
1. dest 空间必须足够大以容纳 src(含 '\0')。
2. dest 必须是可修改的左值。
3. src 必须以 '\0' 结束。
char * strcpy(char *dest, const char*src)
{
char *ret = dest;//记录下字符串dest的起始地址
assert(dest != NULL);
assert(src != NULL);
while((*dest++ = *src++))//copy
{
;
}
return ret;//返回起始地址
}第3步:使用 strcat 拼接字符串
目的:将源字符串追加到目标字符串末尾。
入口:char* strcat(char *dest, const char *src)
动作:找到 dest 的 '\0' 位置,从该处开始拷贝 src 的内容(包括 src 自身的 '\0')。
关键约束:dest 必须有足够剩余空间;src 不得覆盖 dest 自身有效内容的前段(通常要求非重叠)。
char * strcat(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
//找到目标空间
while(*dest)
{
dest++;
}
//拷贝
while((*dest++ = *src++))
{
;
}
return ret;
}第4步:使用 strcmp 比较字符串
目的:按字典序比较两个字符串。
入口:int strcmp(const char *s1, const char *s2)
动作:逐字符比较 s1 和 s2,直到字符不同或遇到 '\0'。
结果判断:
- 返回 >0:第一个不同字符中 s1 的值大于 s2。
- 返回 0:两字符串相等。
- 返回 <0:第一个不同字符中 s1 的值小于 s2。
int strcmp(const char* st1, const char* st2)
{
int ret = 0;
while (*st1 == *st2)
{
st1++;
st2++;
if(*st1 == *st2 && *st1 == '\0')
return ret;//相等的时候返回0
}
return *st1 - *st2;
}长度受限制的字符串函数
上述函数存在缓冲区溢出风险,标准库提供了带长度限制的安全变体:
- strncpy:
strncpy(char *dest, const char *src, size_t n)。最多拷贝n个字符。若src长度小于n,dest剩余部分将用'\0'填充。注意:strncpy不保证dest以'\0'结尾(除非n大于src长度),需手动检查。 - strncat:
strncat(char *dest, const char *src, size_t n)。最多从src拷贝n个字符追加到dest,并自动添加终止符'\0'。 - strncmp:
strncmp(const char *s1, const char *s2, size_t n)。仅比较前n个字符,若未提前结束则继续比较直至发现差异或到达n。
字符串查找与分割函数
第5步:使用 strstr 查找子串
目的:在字符串中寻找首次出现的子串。
入口:char* strstr(const char *haystack, const char *needle)
动作:返回指向 haystack 中第一次出现 needle 位置的指针;若未找到则返回 NULL。
逻辑:遍历主串,尝试匹配子串起始点。
char* strstr(const char* st1, const char* st2)
{
//st1,st2标注起始位置
char* s1 = st1;
char* s2 = st2;
char* cur = s1;
while (*cur)
{
s1 = cur;
while (*s1 == *s2 && *s1 && *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cur;
}
cur++;
}
return NULL;//找不到
}第6步:使用 strtok 分割字符串
目的:根据分隔符集合将字符串分解为标记(Token)。
入口:char* strtok(char *str, const char *sep)
动作:
1. 首次调用传入待分割字符串 str。
2. 后续调用传入 NULL 以继续分割同一字符串。
3. 函数会修改原字符串,将分隔符替换为 '\0'。
4. 返回当前标记的指针,若无更多标记则返回 NULL。
注意:由于函数具有内部静态状态且修改输入,不可用于多线程环境,也不适用于常量字符串。
第7步:使用 strerror 获取错误信息
目的:将系统错误码转换为可读字符串。
入口:char* strerror(int errnum)
头文件:#include <errno.h>
动作:根据 errnum(通常由全局变量 errno 设置)返回对应的标准错误描述字符串。
内存操作函数
第8步:使用 memcpy 复制内存块
目的:通用内存区域的数据拷贝。
入口:void* memcpy(void *dest, const void *src, size_t n)
动作:从 src 位置向后复制 n 个字节至 dest。
关键区别:不识别 '\0',严格按字节数拷贝。
风险:若源和目标内存区域重叠,结果未定义。此类场景应使用 memmove。
void* memcpy(void* dest, const void* src, size_t sz)
{
assert(dest && src);
void* ret = dest;
size_t count = sz;
while (count--)
{
*(char*)dest = *(char*)src;//拷贝
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}第9步:使用 memmove 处理重叠内存
目的:安全地复制可能重叠的内存区域。
入口:void* memmove(void *dest, const void *src, size_t n)
逻辑:检测源和目标的重叠关系。若发生重叠且目标起始地址高于源起始地址,则从后向前拷贝以防止数据覆盖;否则从前向后拷贝。
void* memmove(void* dest, const void* src, size_t sz)
{
assert(dest && src);//断言
void* ret = dest;
size_t count = sz;
//判断是否重叠及方向
if ((char*)dest > (char*)src && (char*)dest < (char*)src + count)
{
//后->前拷贝,防止覆盖未读取的源数据
while (count--)
{
*((char*)dest + count) = *(char*)src + count;
}
}
else
{
//前->后拷贝
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
return ret;
}第10步:使用 memcmp 比较内存块
目的:比较两块内存区域的内容。
入口:int memcmp(const void *ptr1, const void *ptr2, size_t n)
动作:比较 ptr1 和 ptr2 前 n 个字节。
结果:同 strcmp,返回差值指示大小关系。
以上就是C语言字符串函数和内存函数详解的详细内容,更多关于C语言底层内存操作技巧的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













