C语言常见字符函数和字符串函数

字符/字符串函数

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()简述

  1. sizeof 操作符在编译时求值,不会引起运行时开销
  2. sizeof 可以用于任何数据类型,包括用户自定义类型
  3. sizeof 的返回值类型是 size_t(无符号整数类型)
  4. 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的地址

注意: 两常量字符串能拼接说明:srcdest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。在str为NULL的情况下调用strcpystrcat,试图往空指针指向的内存空间写入数据,这会导致程序崩溃。

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地址

功能: 比较 str1str2 的字典顺序(按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: 需要复制的字节数

功能: strncpysrc 的前 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 个字符)

功能: 逐字符比较 str1str2 的前 n 个字符(或直到遇到 \0)。

返回值:

  1. n个字符相同 → 返回0
  2. 比较中s1字符 < s2字符 → 返回负值
  3. 比较中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

注意:

  • 禁止内存重叠:destsrc所指空间重叠会导致未定义行为。
  • 目标空间足够大

模拟实现:

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类似,但允许源和目标内存块重叠,通过判断 srcdest 的位置关系,决定拷贝方向(从前向后或从后向前)

模拟实现:

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)); //返回负数

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值