✨字符函数和字符串函数讲解和模拟实现(优化)
❤️字符串函数讲解及模拟实现
📕长度不受限制的字符串函数:
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),这里是想要告诉大家,库函数只是一个给定的方式,仅仅是一种方式(当然也是比较好的方式),但我们要根据所需进行自己的更改!
-------------------------✨萌新初上道,大佬多指教!!!----------------------------------------