字符/字符串函数
01.求字符串长度
1.1strlen()
函数原型
size_t strlen(const char *str);
参数: str:要计算长度的字符串的首地址
功能: 从给定内存地址开始扫描,直到遇到第一个'\0'
结束符为止,统计这之间的字符数量。
返回值: 返回一个 size_t 类型的值,表示字符串中的字符数。(不包括空字符’\0’)
1.2模拟实现my_strlen()
//非递归求字符串长度
int My_Strlen(char* str){
int count = 0;//临时变量
while (*str!='\0'){//传递的是字符串首地址,,解引用即可
count++;
str++;//找下一个字符
}return count;
}
//递归
int My_Strlen(char* str){
if (*str!='\0'){
return 1 + My_Strlen(str + 1);//加一个字符串的长度
}return 0;
}
int main(){
char arr[] = "zbc";//['z','b','c','\0']包括'\0'
int len = My_Strlen(arr);//传递的是数组的首地址过去
return 0;
}
1.3 sizeof()
简述
sizeof
操作符在编译时求值,不会引起运行时开销sizeof
可以用于任何数据类型,包括用户自定义类型sizeof
的返回值类型是 size_t(无符号整数类型)sizeof
可以使用两种语法形式:sizeof(类型名)
或sizeof 表达式
1.4 深入解析 strlen()
与 sizeof ()
的区别
一般情况下数组名表示首地址的地址,但是有特殊情况sizeof(数组名)
和&数组名
(取出整个数组的地址,是一个随机值)表示的是整个数组的大小。strlen()计算字符数组的字符数,以"\0"为结束判断,不计算为’\0’的数组元素。 而sizeof计算数据(包括数组、变量、类型、结构体等)所占内存空间,用字节数表示。
示例代码:
char text[100] = "Programming";
printf("strlen: %zu\n", strlen(text)); // 11
printf("sizeof: %zu\n", sizeof(text)); // 100
-----
char *ptr = "Hello World";
printf("strlen: %zu\n", strlen(ptr)); // 11
printf("sizeof: %zu\n", sizeof(ptr)); // 4或8(指针大小)
内存布局:
示例字符串"Hello"的内存表示:
+-----+-----+-----+-----+-----+-----+
| 'H' | 'e' | 'l' | 'l' | 'o' | '\0'|
+-----+-----+-----+-----+-----+-----+
0x00 0x01 0x02 0x03 0x04 0x05
strlen结果:5(0x00-0x04)
sizeof结果:6(整个数组大小)
02.长度不受限制的字符串函数
char *strcpy(char *dest, const char *src);//将源字符串复制到目标串中,并在目标串末尾添加空字符'\0'。
2.1 strcpy()
字符串拷贝
参数: dest
: 目标字符串地址 src
: 源字符串地址
功能: 将 src
指向的字符串(包括空字符 \0
)复制到 dest
指向的内存中。
返回值: 返回 dest
的地址
2.1.1 模拟实现my_strcpy()
char* My_Srecpy(char* dest, const char* src){
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while(*dest != *src){
dest++;
src++;
}
return ret;
}
char* strcat(char* dest, const char* src);
2.2 strcat ()
字符串拼接
参数: dest
: 目标字符串地址src
: 源字符串地址
功能: 将 src
指向的字符串追加到 dest
的末尾(覆盖 dest
的 \0)。
返回值: 返回 dest
的地址
注意: 两常量字符串不能拼接说明:src
和dest
所指内存区域不可以重叠且dest
必须有足够的空间来容纳src
的字符串。在str为NULL
的情况下调用strcpy
和strcat
,试图往空指针指向的内存空间写入数据,这会导致程序崩溃。
2.2.1 模拟实现My_Strcat()
char* My_Strcat(char* dest, const char* src){
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while(*dest !=NULL){
dest++;
}
while((*dest++ = *src++)){
;
}
return ret;
}
int strcmp(const char* str1, const char* str2);
2.3 strcmp()
字符串比较
参数: str1
: 字符串1地址;str2
: 字符串2地址
功能: 比较 str1
和 str2
的字典顺序(按ASCII值逐字符比较)。
返回值: 返回整数:(str1 < str2
),返回负值,(str1 == str2
),返回0,若 (str1 > str2
),返回正值
2.3.1 模拟实现My_Strcmp()
int strcmp(const char* str1, const char* str2){//比较的是ascii码值,需要转换,逐字符比较
int ret = 0;
assert(dest != NULL);
assert(src != NULL);
while(*str1 != '\0' && *str2 != '\0' && *str1 == *str2){
srt1++;
str2++;
}
return (*str1 - *str2)
}
03.长度受限制的字符串介绍
char *strncpy(char *dest, const char *src, size_t n);
3.1 strncpy ()
字符串n拷贝
参数:
dest
:目标字符串的地址,它需要有足够空间容纳源字符串n字符和空字符’\0’
src
: 源字符串的地址,以’\0’结束。
n
: 需要复制的字节数
功能: strncpy
将 src
的前 n
个字符复制到 dest
:若 src
长度不足 n
,剩余部分用 \0
填充;若 src
长度 ≥ n
,则只复制前 n
个字符且不自动补 \0
,需手动添加以确保字符串合法。
返回值: 返回指向目标字符串的dest
指针。
注意:
dest
必须足够大(至少n
字节),否则可能溢出。- 不会自动补
\0
(当n ≤ strlen(src)
时),需手动处理: - 性能较低(如果
n
远大于strlen(src)
,会填充大量\0
)。 - 不适合直接用于字符串操作(除非手动处理
\0
)。
代码示例:
int main() {
char dest[10];
const char *src1 = "Hello";
const char *src2 = "World12345";
// 当 src < n(自动补 \0)
strncpy(dest, src1, 10);
printf("Case 1: %s\n", dest); // 输出 "Hello"(后面填充 \0)
// 当 src ≥ n(不补 \0)
strncpy(dest, src2, 5);
dest[5] = '\0'; // 手动补余 \0
printf("Case 2: %s\n", dest); // 输出 "World"
return 0;
}
char *strncat(char *dest, const char *src, size_t n);
3.2 strncat ()
字符串n拼接
参数:
dest
:目标字符串的地址,它需要有足够空间容纳拼接后的字符串和空字符’\0’
src
:源字符串的指针的首地址
n
:最多追加的字符数(不包括自动添加的 \0
)
功能: 将 src
的前 n
个字符(或遇到 \0
时停止)追加到 dest
的末尾,并自动在最终结果后补 \0。能够防止dest
溢出,安全拼接字符串(比 strcat
更安全)。
**返回值:**返回指向目标字符串的dest
指针。
注意:
dest
必须有足够的剩余空间(至少strlen(dest) + n + 1
)。dest
必须以\0
开头(否则行为未定义)。n
仅限制src
的字符数,strncat
始终会补\0
(即使n
个字符后没有\0
)。- 不会检查
dest
缓冲区是否足够大,需程序员自行确保。
代码示例:
int main(){
char dest[20] = "Hello";
const char *src = ", World!";
strncat(dest, src, 5); // 只追加 ", Wor"(5 个字符)
printf("%s\n", dest); // 输出 "Hello, Wor"(自动补 \0)
return 0;
}
int strncmp(const char *s1, const char *s2, size_t n);
3.3 strncmp ()
字符串n比较
参数:
s1
:要比较的第一个字符串
s2
:要比较的第二个字符串
n
:最多比较的字符数(比较前 n
个字符)
功能: 逐字符比较 str1
和 str2
的前 n
个字符(或直到遇到 \0
)。
返回值:
- 前
n
个字符相同 → 返回0 - 比较中
s1
字符 <s2
字符 → 返回负值 - 比较中
s1
字符 >s2
字符 → 返回正值
注意:
- 不会自动检查字符串长度,
n
不可超过缓冲区大小。 - 遇到 \0 会提前终止比较(即使
n
未用完)。 - 不适用于非字符串数据(如二进制数据,应使用
memcmp
)。
代码示例:
int main() {
const char *s1 = "Hello";
const char *s2 = "Hellx";
const char *s3 = "Ha";
printf("strncmp(s1, s2, 4): %d\n", strncmp(s1, s2, 4)); // 0
printf("strncmp(s1, s2, 5): %d\n", strncmp(s1, s2, 5)); //-11 <0('o' < 'x')
printf("strncmp(s1, s3, 2): %d\n", strncmp(s1, s3, 2)); // 4 >0('e' > 'a')
return 0;
}
04.字符串查找
char *strstr(const char *main_str, const char *sub_str);
4.1 strstr()字符串匹配
参数:
功能: 在主串中查找子串首次出现的位置,返回指向该位置的指针;若未找到,返回NULL
。 具体实现可采用暴力算法(逐字符匹配)和kmp
算法(优化查找)
返回值:
- 找到子串时:返回子串在
haystack
中的起始地址,后续字符可通过指针访问。 - 未找到子串时:返回
NULL
。
char str = "abcdefg";
char *result = strstr(str, "cde");
printf("%s\n", result); // 输出:cdefg
char *strtok(char *str, const char *sep);
4.2 strtok()字符串切割
参数: str
是原字符串,sep
是分隔符字符串(集合)
功能: 字符串切割,按照 sep
字符串(可称为集合)指向的字符串中的字符,切割 指向的字符串。其实就是在 str
指向的字符串中发现了 sep
字符串中的字符,就将其变成\0
调用一次strtok
只切割一次,切割一次之后,再去切割的时候strtok
的第一个参数传NULL
,意思是接着上次切割的位置继续切下去。
注意: 如果 str
字符串中出现了连续的几个 sep
,中的字符,则只将第一个字符变成”\0
返回值: 返回的是切出来的字符串的首地址
charstr[100]="lucy:yuxin,hanbing,xiaoyang:xueyang,zhanghu:yongheng";
char *p[8];
int i=0,j;
printf("str:号s\n",str);
p[i]= strtok(str,",:;");//首次切割返回的是lucy\0,把:替换成了'\0'
while (p[i]!-NULL){
i++:
p[i]=strtok(NULL,".:;");//第二次切割(比较特殊)开始第一个参数传NULL告诉它我从上一个被置为'\0'的位置开始切割
}
for (j=0 ;j<i;j++)
printf("p[各d]:号s\n",j,p[j]);
05.错误信息报告
char *strerror(int errnum);
5.1 strerror()
参数: errnum
错误码
这个错误码一般传递的是 errno
这个变量的值,在C语言有一个全局的变量叫: errno
,当库函数的调用发生错误的时候,就会把本次错误的错误码存放在 errno
这个变量中,使用这个全局变量需要包含一个头文件errno.h
。
功能:
- C 标准库中用于将错误码转换为可读错误信息的函数。
strerror
函数可以通过参数部分的errnum
表示错误码,得到对应的错误信息,并且返回这个错误信息字符串首字符的地址。 strerror
函数只针对标准库中的函数发生错误后设置的错误码的转换。
返回值: 函数返回通过错误码得到的错误信息字符串的首字符的地址,
06.字符操作
…
07.内存操作函数
void *memcpy(void *dest, const void *src, size_t count);
7.1 memcpy()内存拷贝
参数: dest
目标地址,src
源地址,count
拷贝字节数
功能: 将src
指向的内存块逐字节复制count
个字节到dest
注意:
- 禁止内存重叠:
dest
和src
所指空间重叠会导致未定义行为。 - 目标空间足够大
模拟实现:
void* my_memcpy(void* dest, const void* src, size_t count) {
assert(dest && src);
while(count--){
*(char *)dest=*(char *)src;
dest=*(char *)dest+1;
src=*(char *)src+1;
}
return dest;
}
使用场景:
int src[5] = {1, 2, 3, 4, 5};
int dest[5];
memcpy(dest, src, sizeof(src)); //拷贝20字节
void *memmove(void *dest, const void *src, size_t count);
7.2 memmove() 安全拷贝
参数: dest
目标地址,src
源地址,count
拷贝字节数
功能: 与memcpy
类似,但允许源和目标内存块重叠,通过判断 src
和 dest
的位置关系,决定拷贝方向(从前向后或从后向前)
模拟实现:
void* my_memmove(void* dest, const void* src, size_t n) {
if (dest == NULL || src == NULL) return NULL;
char* d = (char*)dest;
const char* s = (const char*)src;
if (d < s) { // 目的串小于原串,从前向后拷贝
for (size_t i = 0; i < n; i++) {
d[i] = s[i];
}
} else {
for (size_t i = n; i > 0; i--) {
d[i-1] = s[i-1];
}
}
return dest;
}
使用场景:
char str[20] = "hello, world!";
memmove(str + 7, str + 12, 5); // 将 ", world!" 的 "world!" 前移
// 结果:str 变为 "hello, !"
void *memset(void *dest, int value, size_t count);
7.3 memset() 内存填充
参数: dest
目标地址,value
填充值,count
拷贝字节数
功能: 将 src
指向的内存块的前 n
个字节设置为 value
的值
模拟实现:
void* my_memset(void* dest, int value, size_t count) {
return ptr;
}
使用场景:
//数组元素置位0
int arr[10];
memset(arr, 0, sizeof(arr));
int memcmp(const void *ptr1, const void *ptr2, size_t count);
7.4 memcmp()内存比较
参数: ptr1
内存快1,ptr2
内存快2,count
拷贝字节数
功能: 比较(逐字节比较,遇到\0
不终止)两个内存块的前count
个字节
返回值: 相等返回0
,否则返回首个不匹配字节的差值
模拟实现:
int my_memcmp(const void* ptr1, const void* ptr2, size_t count) {
const char* p1 = (const char*)ptr1;
const char* p2 = (const char*)ptr2;
for (size_t i = 0; i < count; i++) {
if (p1i != p2i) {
return p1i - p2i;
}
}
return 0;
}
使用场景:
// 比较两个字符串
unsigned char buf1[] = "dsahihdaoDD";
unsigned char buf2[] = "dsahihdaoDd";
int result = memcmp(buf1, buf2, sizeof(buf1)); //返回负数