C语言常用字符串与内存函数详解及模拟实现
C语言开发中,字符串与内存操作是基础且高风险的领域。本文详细分析 strlen、strcpy、strcat、strcmp 等字符串函数的行为特征,以及 memcpy、memmove、memset 等内存操作函数的底层逻辑。重点解析无符号数比较陷阱、内存重叠拷贝的安全处理,并提供各函数的模拟实现源码,助你规避常见Bug。
一. strlen:长度计算与无符号数陷阱
size_t strlen ( const char * str ) 返回字符串中 '\0' 之前的字符个数。返回值类型为 size_t,即无符号整数。
这里存在一个经典陷阱:当计算两个不同长度字符串的长度差时,如果前一个较短,结果会因无符号数的下溢特性变成一个极大的正数。
#include <stdio.h>
#include <string.h>
int main() {
// strlen("abc") = 3, strlen("abcdef") = 6
// 3 - 6 = -3,但在无符号运算中变为极大值
if (strlen("abc") - strlen("abcdef")) {
printf(">\n");
} else {
printf("<\n");
}
return 0;
}模拟实现:
size_t my_strlen(const char* str) {
if (!str) return 0;
size_t len = 0;
while (*str++) {
len++;
}
return len;
}二. strcpy:不可逆的字符串拷贝
char* strcpy(char* destination, const char* source) 负责将源字符串拷贝到目标缓冲区,包括结尾的 '\0'。
- 风险点:它不检查目标缓冲区大小,极易导致缓冲区溢出。使用前提是目标地址足够大且可写。
模拟实现:
char* my_strcpy(char* dest, const char* source) {
assert(dest != NULL && source != NULL);
char* ret = dest;
while ((*dest++ = *source++) != '\0');
return ret;
}三. strcat:追加与边界检查
char* strcat(char* destination, const char* source) 将源字符串追加到目标字符串末尾。它会覆盖目标字符串原本的 '\0',并在追加结束后在末尾添加新的 '\0'。
注意:必须保证目标缓冲区有足够剩余空间。首先找到目标串的结束符,再进行拼接。
模拟实现:
char* my_strcat(char* dest, const char* src) {
assert(dest && src);
char* ret = dest;
// 1. 定位到目标字符串末尾
while (*dest) {
dest++;
}
// 2. 进行拷贝
while (*dest++ = *src++);
return ret;
}四. strcmp:逐字符比较规则
int strcmp(const char* s1, const char* s2) 并不是比较长度,而是从前往后逐个比较对应位置的 ASCII 码。
- 相等继续下一位;遇到
'\0'停止。 - 若全部相等,返回 0。
- 若
s1[i] > s2[i],返回大于 0 的值。 - 若
s1[i] < s2[i],返回小于 0 的值。
模拟实现:
int my_strcmp(const char* s1, const char* s2) {
assert(s1 && s2);
while (*s1 == *s2) {
if (*s1 == '\0') {
return 0;
}
s1++;
s2++;
}
// 根据差值的正负返回结果
return (*(unsigned char*)s1) - (*(unsigned char*)s2);
}五. strncpy 与 strncat:长度受限的操作
1. strncpy:有限拷贝
char* strncpy(char* dest, const char* src, size_t count) 仅拷贝前 count 个字符。若源串长于 count,则不会在末尾补 '\0',这在后续使用时可能引发错误,需手动确保终止。
2. strncat:有限追加
char* strncat(char* dest, const char* src, size_t count) 追加最多 count 个字符,并强制在末尾添加一个 '\0'。这比 strcpy 更安全,但依然需要确保 dest 有足够的总空间容纳原内容+count+'\0'。
strncat 模拟实现:
char* my_strncat(char* dest, const char* src, size_t count) {
assert(dest && src);
char* ret = dest;
// 找到末尾
while (*dest) dest++;
// 拷贝指定数量
while (count-- && (*dest++ = *src++));
// 补充终止符
*dest = '\0';
return ret;
}六. strstr:子串查找
const char* strstr(const char* str1, const char* str2) 用于查找 str2 作为子串在 str1 中首次出现的位置。若未找到,返回 NULL;找到则返回指向该位置的指针。
七. strtok:字符串分割器
char* strtok(char* str, const char* sep) 利用分隔符集合 sep 切割字符串。其内部维护了一个静态状态变量。
- 第一次调用:传入待处理的
str,它在第一个分隔符处插入'\0'并返回片段起始地址。 - 后续调用:第一个参数传
NULL,函数将从上次断点继续向后扫描。 - 完成:若无更多片段,返回
NULL。
循环分割示例:
char a[] = "A@nice.boy";
const char sep[] = "@.";
char* token = strtok(a, sep);
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, sep); // 下次用 NULL 继续
}八. strerror:错误码转换
char* strerror(int errnum) 接受系统错误代码(通常来自全局变量 errno),返回对应的可读英文错误描述字符串。主要用于调试或向用户展示具体错误原因。
九. memcpy vs memmove:内存移动的核心差异
1. memcpy:普通拷贝
void* memcpy(void* dest, const void* src, size_t count) 按字节拷贝。它假设源和目标区域不重叠。执行方向通常是从低地址向高地址。
模拟实现:
void* my_memcpy(void* dest, const void* src, size_t count) {
assert(dest && src);
void* ret = dest;
for (size_t i = 0; i < count; ++i) {
((char*)dest)[i] = ((const char*)src)[i];
}
return ret;
}2. memmove:处理重叠
void* memmove(void* dest, const void* src, size_t count) 专门用于处理源与目标内存区域重叠的情况,保证数据不被提前覆盖。
- 目标在源右侧(dest > src):从后往前拷贝(高地址到低地址),防止数据覆盖。
- 目标在源左侧(dest <= src):从前往后拷贝即可。
模拟实现:
void* my_memmove(void* dest, const void* src, size_t count) {
assert(dest && src);
char* d = (char*)dest;
const char* s = (const char*)src;
if (d < s || d == s) {
// 正常顺序拷贝
while (count--) {
*d++ = *s++;
}
} else {
// 倒序拷贝,防止重叠区域数据被覆盖
d += count - 1;
s += count - 1;
while (count--) {
*d-- = *s--;
}
}
return dest;
}十. memcmp 与 memset
1. memcmp:按字节比较
int memcmp(const void* s1, const void* s2, size_t count) 比较前 count 个字节的二进制内容。常用于比较结构体实例是否相等。
2. memset:内存填充
void* memset(void* dest, int c, size_t count) 将目标内存的前 count 个字节设置为指定值 c。
注意:由于是按字节赋值,设置非零值(如 1)时,每个字节都会变成 0x01,整型变量的值将是 0x01010101,而非数字 1。清零(设为 0)是唯一安全的通用操作。
总结
掌握这些函数的模拟实现,不仅能应对面试考察,更能让我们在编写生产代码时,清晰理解内存管理的细节。在处理字符串和内存块时,始终优先考虑缓冲区边界检查和数据重叠情况,以保障程序的稳定性。
以上就是C语言字符串与内存函数详解及模拟实现的详细内容,更多关于C语言函数安全使用的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









