功能
计算字符串长度,返回 \0
前的字符个数(不包含 \0
)。
关键特性
以 \0
为结束标志
字符串必须包含 \0
,否则会越界访问内存。
char arr[] = {'a', 'b', 'c'}; // 无 `\0`
printf("%zu\n", strlen(arr)); // 输出随机值
参数合法性
指针需指向以 \0
结尾的有效字符串,传入 NULL
会触发段错误。
char* ptr = NULL;
strlen(ptr); // 段错误
返回值为 size_t
无符号类型,与负数比较可能导致逻辑错误。
if (strlen("abc") - 5 < 0) { // 永远为假
printf("Less than 0\n"); // 不会执行
}
size_t strlen(const char* str)
{
assert(str);
int num = 0;
while (*str != '\0')
{
num++;
str++;
}
return num;
}
size_t strlen(const char * str)
{
if(*str == '\0')
return 0;
else
return 1+my_strlen(str+1);
}
size_t strlen(char *s)
{
char *p = s;
while(*p != ‘\0’ )
p++;
return p-s;
}
功能
复制源字符串到目标空间,包含 \0
。
关键特性
源串需包含 \0
若源串无\0,strcpy会持续复制直至遇到内存中的\0,导致缓冲区溢出。
char src[] = {'a', 'b', 'c'}; // 无 `\0`
char dest[10];
strcpy(dest, src); // 越界复制
目标空间需足够大
目标数组长度需至少为源串长度 + 1(含\0)。若空间不足,会覆盖相邻内存。
char dest[3];
strcpy(dest, "abcdef"); // 缓冲区溢出
目标空间必须可写
目标指针不能指向常量字符串或只读内存。
错误示例:
char* dest = "hello"; // 只读内存
strcpy(dest, "world"); // 段错误
char* strcpy(char* destination, const char* source)
{
assert(destination&&source);
char* tmp = destination;
while (*tmp++ = *source++)
{
;
}
*tmp = '\0';
return destination;
}
功能
将源字符串追加到目标字符串末尾。
关键特性
源串和目标串需包含 \0
源串必须以\0结尾,否则会导致越界复制。
示例:
char src[] = {'a', 'b', 'c'}; // 无 `\0`
char dest[10] = "hello";
strcat(dest, src); // 越界复制
目标空间需足够大
目标数组需容纳原内容 + 源串 + \0
。
计算示例:
char dest[10] = "abc";
strcat(dest, "defgh"); // 需 4 + 5 + 1 = 10 字节
char* strcat(char* destination, const char* source)
{
assert(destination&&source);
char* tmp= destination;
while (*tmp!='\0')
{
tmp++;
}
strcpy(tmp, source);
return destination;
}
功能
逐字符
比较两字符串,返回比较结果。
返回值规则
> 0
:首个不匹配字符中,s1
的 ASCII 值大于 s2
。= 0
:两字符串完全相同。 (长度和内容均一致)< 0
:首个不匹配字符中,s1
的 ASCII 值小于 s2
。比较逻辑:
示例
strcmp("abc", "abd"); // 返回负值('c' < 'd')
strcmp("abc", "abc"); // 返回0
strcmp("abc", "ab"); // 返回正值('\0' < 'c')
strcmp("123", "12"); // 返回正值('3' > '\0')
strcmp("A", "a"); // 返回负值(65 < 97)
int strcmp(const char * str1, const char * str2)
{
assert(str1&&str2);
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
return (*str1 - *str2);
}
功能
复制源串的前 n
个字符到目标空间。
特殊规则
n
:补 \0
至 n
字节。char dest[5];
strncpy(dest, "abc", 4); // "abc\0\0"
n
:不自动补 \0
,需手动处理。char dest[3];
strncpy(dest, "abcdef", 3); // dest内容:"abc"(无`\0`)
printf("%s\n", dest); // 可能输出乱码(继续读取后续内存)
char * strncpy(char * destination, const char * source, size_t num)
{
assert(destination&&source);
char* tmp = destination;
while (*source&&num>0)
{
*tmp++ = *source++;
num--;
}
while (num>0)
{
*tmp++ = '\0';
num--;
}
return destination;
}
功能
追加源串的前 n
个字符到目标串末尾。
特殊规则
追加后自动补\0:
无论n
是否大于源串长度,strncat都会在追加后添加\0。`
char dest[10] = "abc";
strncat(dest, "def", 5); // "abcdef\0"
源串长度< n
:
仅追加至\0
,不补多余字符。
示例:
char dest[10] = "abc";
strncat(dest, "de", 5); // 追加"de",结果:"abcde\0"
目标空间需足够大:
需容纳原内容 + 源串前n字节(或全部) + \0
。
char * strncat(char * destination, const char * source, size_t num)
{
assert(destination&&source);
char* tmp = destination;
while (*tmp != '\0')
{
tmp++;
}
while (*source&&num>0)
{
*tmp++ = *source++;
num--;
}
*tmp = '\0';
return destination;
}
功能
比较两字符串的前 n
个字符。
返回值规则:
同strcmp
,但仅比较至n
字节或\0
。
示例
strncmp("abcdef", "abcxyz", 3); // 返回0(前3字节相同)
strncmp("abc", "abcd", 4); // 返回负值(`\0` < 'd')
strncmp("123", "12a", 2); // 返回0(仅比较前2字节)
与strcmp
的区别:
strncmp
最多比较n
字节,而strcmp
会比较至\0
。
示例对比:
strcmp("abc", "abcdef"); // 返回0(`abc`与`abc`相等)
strncmp("abc", "abcdef", 6); // 返回负值(`abc\0\0\0` < `abcdef`)
int strncmp(const char * str1, const char * str2, size_t num)
{
assert(str1&&str2);
while ((*str1 == *str2)&&num>0)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
num--;
}
if (num == 0)
return 0;
return (*str1 - *str2);
}
功能
查找子串在主串中的首次出现位置。
返回值
NULL
。匹配规则:
strstr(s, "")
始终返回s
(空串是任何串的子串)。示例
char* p = strstr("abcdef", "cde"); // p指向"cdef"
char* q = strstr("abc", "acd"); // q为NULL(未找到)
char* strstr(const char *str1, const char * str2)
{
assert(str1&&str2);
if (*str2 == '\0') {
return (char*)str1;
}
const char* ps1 = str1;
while (*ps1 != '\0')
{
const char* begin = ps1;
const char* ps2 = str2;
while (*ps1 != '\0' && *ps2 != '\0' && *ps1 == *ps2)
{
ps1++;
ps2++;
}
if (*ps2 == '\0')
return (char*)begin;
ps1 = begin+1;
}
if (*ps1 == '\0')
return NULL;
}
功能
按分隔符分割字符串。
特性
\0
。 并保存后续位置。char str[] = "hello,world";
char* token = strtok(str, ","); // str变为"hello\0world",返回"hello"
状态保存:
首次调用需传入原串,后续传NULL继续分割。
示例:
char str[] = "a,b,c";
char* t1 = strtok(str, ","); // 返回"a"
char* t2 = strtok(NULL, ","); // 返回"b"
char* t3 = strtok(NULL, ","); // 返回"c"
char* t4 = strtok(NULL, ","); // 返回NULL(已无分隔符)
模拟实现原理:
char * strtok(char* str, const char * sep) {
static char* last = NULL; // 静态变量保存上次处理的位置
// 首次调用或显式传入NULL继续处理
if (str != NULL) {
last = str;
} else if (last == NULL) {
return NULL; // 没有更多标记
}
// 跳过前导分隔符
while (*last != '\0' && strchr(sep, *last) != NULL) {
last++;
}
if (*last == '\0') {
last = NULL; // 没有更多标记
return NULL;
}
// 找到当前标记的结束位置
char* token = last;
while (*last != '\0' && strchr(sep, *last) == NULL) {
last++;
}
if (*last != '\0') {
*last = '\0'; // 替换分隔符为字符串结束符
last++; // 移动到下一个位置
} else {
last = NULL; // 处理完毕,下次调用返回NULL
}
return token;
}
功能
将错误码(errno)
转换为错误信息字符串。
关键细节:
errno
:errno
,需包含
。FILE* fp = fopen("nonexistent.txt", "r");
if (!fp) {
printf("Error: %s\n", strerror(errno)); // 输出:"No such file or directory"
}
char* err1 = strerror(1); // "Operation not permitted"
char* err2 = strerror(2); // "No such file or directory"
printf("%s\n", err1); // 可能输出"No such file or directory"(被覆盖)
char * strerror(int errnum) {
// 错误信息数组(实际实现可能更大)
static const char* const error_messages[] = {
[0] = "No error",
[1] = "Operation not permitted",
[2] = "No such file or directory",
[3] = "No such process",
[4] = "Interrupted system call",
[5] = "Input/output error",
[6] = "Device not configured",
[7] = "Argument list too long",
[8] = "Exec format error",
[9] = "Bad file descriptor",
[10] = "No child processes",
// 更多错误码...
};
// 静态缓冲区用于返回错误信息
static char unknown_error[32];
// 检查错误码是否在预定义范围内
size_t max_errors = sizeof(error_messages) / sizeof(error_messages[0]);
if (errnum >= 0 && (size_t)errnum < max_errors && error_messages[errnum] != NULL) {
return (char*)error_messages[errnum];
}
// 处理未知错误码
snprintf(unknown_error, sizeof(unknown_error), "Unknown error %d", errnum);
return unknown_error;
}
函数 | 判断条件(返回真的情况) |
---|---|
iscntrl | 控制字符(如\t, \n, ASCII 0-31, 127) |
isspace | 空白字符(, \t, \n, \r, \f, \v) |
isdigit | 十进制数字(0-9) |
isxdigit | 十六进制数字(0-9, a-f, A-F) |
islower | 小写字母(a-z) |
isupper | 大写字母(A-Z) |
isalpha | 字母(a-z, A-Z) |
isalnum | 字母或数字(a-z, A-Z, 0-9) |
ispunct | 标点符号(非字母、数字、空白的可打印字符) |
isgraph | 图形字符(非空白的可打印字符) |
isprint | 可打印字符(包括空白) |
功能
内存复制 (按字节),不处理重叠区域。
原型:
void* memcpy(void* dest, const void* src, size_t n);
关键特性:
\0
不停止。int src[5] = {1, 2, 3, 4, 5};
int dest[5];
memcpy(dest, src, 20); // 复制5个int(20字节)
/
目标区域重叠,结果未定义(可能数据覆盖)。int arr[10] = {1, 2, 3, 4, 5};
memcpy(arr + 2, arr, 12); // 重叠区域,结果不可预测
void * memcpy(void* destination, const void* source, size_t num)
{
assert(destination != NULL || num == 0);
assert(source != NULL || num == 0);
if (destination == source) {
return destination;
}
void *orig_dest = destination;
while (num--) {
*(char *)destination = *(char *)source;
destination = (char *)destination + 1;
source = (char *)source + 1;
}
return orig_dest;
}
功能:
特性详解:
memmove
能够安全处理源内存(src
)和目标内存(dest
)重叠的情况。通过检查目标地址是否位于源地址的后半段,自动选择从后向前或从前向后复制,避免数据覆盖。示例代码
int arr[10] = {1, 2, 3, 4, 5};
memmove(arr + 2, arr, 20); // 复制前5个int(20字节)到位置2
// 结果:arr = {1, 2, 1, 2, 3, 4, 5, 0, 0, 0}
模拟实现
void * memmove(void * destination, const void * source, size_t num)
{
assert(destination != NULL || num == 0);
assert(source != NULL || num == 0);
void *orig_dest = destination;
if (destination < source)
{
memcpy(destination, source, num);
}
else
{
// ��ȫ�������
char *dst = (char *)destination + num - 1;
const char *src = (const char *)source + num - 1;
while (num--) {
*(char *)dst = *(char *)src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}
return orig_dest;
}
**功能:**按字节比较内存区域。
按字节比较内存区域
memcmp
逐字节比较两块内存区域,返回值的符号由首个不匹配字节的差值决定:
ptr1
的字节值大于 ptr2
。ptr1
的字节值小于 ptr2
。示例代码
char s1[] = {1, 2, 3};
char s2[] = {1, 2, 4};
memcmp(s1, s2, 2); // 返回0(前2字节相同)
memcmp(s1, s2, 3); // 返回负值(3 < 4)
与 strcmp 的区别
memcmp
严格比较指定字节数,不关心 \0
。strcmp
遇到 \0
停止比较。对比示例
char a[] = "abc\0def";
char b[] = "abc\0xyz";
memcmp(a, b, 4); // 返回0(前4字节均为 `abc\0`)
strcmp(a, b); // 返回0(因 `\0` 终止比较)
模拟实现逻辑
int memcmp(const void * ptr1, const void * ptr2, size_t num)
{
assert(ptr1 && ptr2);
const char *p1 = (const char *)ptr1;
const char *p2 = (const char *)ptr2;
while (num-- > 0)
{
if (*p1 != *p2)
return (char)*p1 - (char)*p2;
p1++;
p2++;
}
return 0;
}
功能:将内存块的前n个字节设置为指定的字符值(按字节填充)。
函数原型
void* memset(void* ptr, int value, size_t n);
ptr
:指向要填充的内存块的起始地址(可修改);value
:要设置的字符值(虽然类型是int,但实际只使用低 8 位,即unsigned char范围);n
:要设置的字节数;返回值
:指向ptr的指针(方便链式操作)。关键特性
memset
都会逐个字节设置为value
的低 8 位。char str[10];
memset(str, 'a', 5); // 前5字节设为'a',结果:"aaaaa????"(?为未初始化值)
int、long
等多字节类型,可能导致非预期结果(因每个字节都被设置为相同值)。int arr[5];
memset(arr, 1, 20); // 错误:每个字节设为1,每个int为0x01010101(十进制16843009)
// 期望:arr全为1 → 实际:arr元素为16843009
n
大于ptr
指向的内存块长度,会导致缓冲区溢出(覆盖相邻内存)。典型用法
char buf[100];
memset(buf, 0, 100); // 清空缓冲区(所有字节设为0)
typedef struct {
int a;
char b[20];
} Stu;
Stu s;
memset(&s, 0, sizeof(Stu)); // 结构体所有成员清零
void * memset(void * ptr, int value, size_t num)
{
assert(ptr);
void *orig_ptr = ptr;
while (num--)
{
*(char*)ptr = (char)value;
ptr = (char*)ptr + 1;
}
return orig_ptr;
}