❤️字符串函数讲解及模拟实现
📕长度不受限制的字符串函数:
1.strcpy函数:
库函数strcpy
char * strcpy ( char * destination, const char * source );
- 将源指向的C字符串复制到目标指向的数组中,包括终止的空字符(并在此时停止)。
注意:
- 源字符串必须以 ‘\0’ 结束。(尤其注意arr[ ]={‘a’,‘b’,‘c’};这种’ \0 '不确定的;)
- 会将源字符串中的 ‘\0’ 拷贝到目标空间。(会以source首元素会替代destination末尾’\0’)
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变。
目标空间必须可修改(强调)
#include<stdio.h> #include<string.h> int main() { char* arr = "sadkajfjlsjaldj"; char arr1[] = "hello!"; strcpy(arr, arr1); printf("%s", arr); return 0; }
这个代码一运行就会崩掉,原因就是目标空间为字符串,无法被修改,所以无法拷贝;
模拟实现:
#include <stdio.h> void my_strcpy(char* dest,char* src); int main() { char arr1[20]="asdfghjkl"; char arr2[]="hello!"; my_strcpy(arr1,arr2); //把arr2拷贝到arr1中; printf("%s",arr1); return 0; }
- 模拟实现1:
char* my_strcpy(char* dest,char* src) { while(src!='\0') char* ret=dest; { *dest=*src; dest++; src++; } //成功拷贝hello! *dest=*src;//拷贝'\0' return ret; }
- 优化:
#include<assert.h> char* my_strcpy(char* dest,const char* src) //我们是要src拷贝到dest,const让无需更改的来源地址变为常变量,无法修改。可防止后面*dest=*src写反; { assert(src!=NULL)//断言 assert(dest!=NULL)//断言 //提高可调式性 char* ret=dest; while(*dest++=*src++) { ; //直接打印hello!\0 } return ret; }
2.strcat函数
库函数strcat
char * strcat ( char * destination, const char * source );
- 将源字符串的副本附加到目标字符串中。目标中的终止空字符将被源的第一个字符覆盖,并且在目标中两者的连接形成的新字符串的末尾包含一个空字符。
注意:
- 源字符串必须以 ‘\0’ 结束。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
模拟实现:
char* my_strcat(char* dest, const char* src) { assert(dest && src); char* ret = dest; //查找\0; while (*dest) dest++; //追加; while (*src) { *dest++ = *src++; } return ret; }
3.strcmp函数
库函数strcmp
int strcmp ( const char * str1, const char * str2 );
- 这个函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续使用以下的对,直到字符不同或达到终止的空字符。
注意:
- 标准规定: 第一个字符串大于第二个字符串,则返回大于0的数字 第一个字符串等于第二个字符串,则返回0 第一个字符串小于第二个字符串,则返回小于0的数字
- 库函数中一般相等返回0;大于返回1;小于返回-1。
#include<stdio.h> #include<string.h> int main() { char* p1 = "abcdef"; char* p2 = "abcdef"; char* p3 = "abcd"; char* p4 = "bcde"; printf("%d\n", strcmp(p1,p2 )); printf("%d\n", strcmp(p1,p3 )); printf("%d\n", strcmp(p3,p4 )); }
- 这里稍微补充下:我们不能进行*arr>*arr1或者“asdsfa”>“asw”比较来比较字符串大小,要借助函数。
- 模拟实现:
int my_strcmp(const char* p1,const char* p2) { assert(p1 && p2); while (*p1 == *p2) { if (p1 == '\0') //这里是表达当他们同时为'\0'就表明相等 { return 0; } //如果都相等且都不等于'\0';进行++比较下一位; p1++; p2++; } //比较不相同的字符,返回差值(这里有整形提升) return *p1 - *p2; } //再借助判断即可;
📙长度受限制的字符串函数:
1.strncpy函数
库函数strncpy
char * strncpy ( char * destination, const char * source, size_t num );
- 将源文件的第一个数个字符复制到目标文件。如果在复制num字符之前找到源C字符串的结尾(由空字符表示的信号),则将用零填充目标,直到写入了全部num字符为止。
注意:
- 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加\0,直到num个
VS2019实现:
char * __cdecl strncpy (char * dest,const char * src,size_t count) { assert(dest&&src); char *start = dest; while (count && (*dest++ = *src++) != '\0') //拷贝到dest中,并且用count限制 count--; if (count) while (--count) *dest++ = '\0'; //完成第二步:长度不够,补'\0'; return(start); }
2.strncat函数
库函数strncat
char * strncat ( char * destination, const char * source, size_t num );
- 将源代码的第一个num字符附加到目标代码中,再加上一个终止的空字符。如果源代码中的C字符串的长度小于num,则只复制直到终止的空字符的内容。
- VS2019实现:
char * __cdecl strncat(char * front,const char * back,size_t count) { char *start = front; while (*front++) ; front--; while (count--) if ((*front++ = *back++) == 0) return(start); *front = '\0'; return(start); }
3.strncmp函数
库函数strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
- 这个函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续使用以下的对,直到num个数。
注意:
- 比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
- VS2019实现:
int __cdecl strncmp(const char *first,const char *last,size_t count) { size_t x = 0; if (!count) { return 0; } /* * This explicit guard needed to deal correctly with boundary * cases: strings shorter than 4 bytes and strings longer than * UINT_MAX-4 bytes . */ if( count >= 4 ) { /* unroll by four */ for (; x < count-4; x+=4) { first+=4; last +=4; if (*(first-4) == 0 || *(first-4) != *(last-4)) { return(*(unsigned char *)(first-4) - *(unsigned char *)(last-4)); } if (*(first-3) == 0 || *(first-3) != *(last-3)) { return(*(unsigned char *)(first-3) - *(unsigned char *)(last-3)); } if (*(first-2) == 0 || *(first-2) != *(last-2)) { return(*(unsigned char *)(first-2) - *(unsigned char *)(last-2)); } if (*(first-1) == 0 || *(first-1) != *(last-1)) { return(*(unsigned char *)(first-1) - *(unsigned char *)(last-1)); } } } /* residual loop */ for (; x < count; x++) { if (*first == 0 || *first != *last) { return(*(unsigned char *)first - *(unsigned char *)last); } first+=1; last+=1; } return 0; }
📘求字符串长度:
1.strlen函数:
库函数strlen
size_t strlen ( const char * str );
- 读取字符串长度,并返回长度。
- 注意:
- 字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包
含 ‘\0’ )。
- 参数指向的字符串必须要以 ‘\0’ 结束。
- 注意函数的返回值为size_t,是无符号的( 易错 )因为统计字符串长度都为正数。
易错点补充:
思考:输出的结果是大于还是小于?
#include<stdio.h> #include<string.h> int main() { char arr[] = "asdasf"; char arr1[] = "uqiwhdqio"; if (strlen(arr) - strlen(arr1) < 0) { printf("小于"); } else { printf("大于"); } return 0; }
- 为什么结果是大于?难道不是我们理解的6-9=-3吗?别急听我为你解释。
- 在库函数<string.h>中strlen返回的是size_t(也就是我们理解的无符号类型)
- 我们说(有符号数-有符号数)得到有符号数,同样的(无符号数-无符号数)得到的也是无符号数(如果计算机没有别的格式化要求);格式化要求比如printf,用%d来打印这就是一种格式化要求;
#include<stdio.h> #include<string.h> int main() { char arr[] = "asdasf"; char arr1[] = "uqiwhdqio"; printf("%u", strlen(arr) - strlen(arr1)); //为了展现我们就以%u打印(计算机中也默认为无符号数,上述打印大于就能证明);结果为4294967293 return 0; }``` ==>具体的计算过程:<== //6----->00000000 00000000 00000000 00000110 原码--->补码 //9----->00000000 00000000 00000000 00001001 原码--->补码 //-9---->10000000 00000000 00000000 00001001 原码 //-9---->11111111111111111111111111111110111 补码 //-3---->11111111 11111111 11111111 11111101 无符号数打印
模拟实现:
- 优化方式和strcpy相近,若没看上述,建议先看更好,观感更佳,这就直接代码体现:
- 主函数:
int main() { char arr[]='ashduhewu'; printf("%d",my_strlen(arr)); return 0; }
- 1.常规实现:
#include<stdio.h> #include<assert.h> size_t my_strlen(const char* str) //size_t相当于unsigned int,因为字符串长度一定为正整数,并且传入的字符串无需更改,所以加上const修饰*str; { assert(str); //直接写str,若str为NULL,返回的是0,判断为假,报错提醒; int count = 0; while(str!='\0') { count++; str++; } return (unsigned int) count; }
- 2.递归实现:
int my_strlen(const char* str) { if (*str == '\0') return 0; else return 1 + my_strlen(str + 1); //先遍历,后返回,总返回:return 【(*str=='\0';遍历结束,开始返回)0+1+1+1+···+1】 }
- 3.指针实现:
int my_strlen(const char* str) { char* p = str; while (*p != '\0') p++; //得到末尾地址 return (p - str); //返回得到两地址间数组元素个数的绝对值 //数组元素是低地址到高地址排列,所以建议用末尾地址减去初始地址,得到正数; }
Tips:细心的小伙伴已经发现了,下面两个函数模拟,我们返回类型是int而并非size_t(unsigned int),这里是想要告诉大家,库函数只是一个给定的方式,仅仅是一种方式(当然也是比较好的方式),但我们要根据所需进行自己的更改!