打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
UC头条:C语言进阶之字符串函数和内存函数的介绍及部分函数的模拟实现
userphoto

2023.08.03 山西

关注

点击加载图片

1.字符串函数介绍

1.1strlen

头文件

获取字符串长度

返回C字符串str的长度。

C字符串的长度由终止空字符确定:C字符串的长度与字符串开头和终止空字符之间的字符数一样长(不包括终止空字符本身)。

这不应与保存字符串的数组的大小混淆。例如:

intmain{charmystr[100]='teststring';printf('%d',strlen(mystr));return0;}

点击加载图片

定义一个大小为100个字符的字符数组,但初始化mystr时使用的C字符串的长度仅为11个字符。因此,当sizeof(mystr)的计算结果为100时,strlen(mystr)返回11。

库中的声明如下:

_Check_return_size_t__cdeclstrlen(_In_z_charconst*_Str);

1.字符串已经'\0’作为结束标志,strlen函数返回的是在字符串中'\0’前面出现的字符个数(不包含'\0’)。

2.参数指向的字符串必须要以'\0’结束。

3.注意函数的返回值为size_t,是无符号的(易错)

1.2strcpy

头文件

复制字符串

将源指向的C字符串复制到目标指向的数组中,包括终止的null字符(并在该点停止)。

为避免溢出,目标指向的数组的大小应足够长,以包含与源相同的C字符串(包括终止空字符),并且不应在内存中与源重叠。

intmain{charstr1[]='Samplestring';charstr2[40];charstr3[40];strcpy(str2,str1);strcpy(str3,'copysuccessful');printf('str1:%s\nstr2:%s\nstr3:%s\n',str1,str2,str3);return0;}

点击加载图片

库中声明如下:

__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1(char*,__RETURN_POLICY_DST,__EMPTY_DECLSPEC,strcpy,_Out_writes_z_(_String_length_(_Source)+1),char,_Destination,_In_z_charconst*,_Source)

源字符串必须以'\0’结束。

会将源字符串中的'\0’拷贝到目标空间。

目标空间必须足够大,以确保能存放源字符串。

目标空间必须可变。

1.3strcat

头文件

追加字符串

将源字符串的副本追加到目标字符串。目标中的终止空字符被源的第一个字符覆盖,并且在目标中由两者串联形成的新字符串的末尾包含一个空字符。

目标字符串和源字符串不得重叠。

intmain{charstr[80];strcpy(str,'these');strcat(str,'strings');strcat(str,'are');strcat(str,'concatenated.');puts(str);return0;}

点击加载图片

库中声明如下:

__DEFINE_CPP_OVERLOAD_STANDARD_FUNC_0_1(char*,__RETURN_POLICY_DST,__EMPTY_DECLSPEC,strcat,_Inout_updates_z_(_String_length_(_Destination)+_String_length_(_Source)+1),char,_Destination,_In_z_charconst*,_Source)

源字符串必须以'\0’结束。

目标空间必须有足够的大,能容纳下源字符串的内容。

目标空间必须可修改。

1.4strcmp

头文件

比较两个字符串

将C字符串str1与C字符串str2进行比较。

此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续以下对,直到字符不同或达到终止空字符。

此函数执行字符的二进制比较。

返回一个整数值,该值指示字符串之间的关系:

点击加载图片

intmain{charkey[]='apple';charbuffer[80];do{printf('Guessmyfavoritefruit?');fflush(stdout);scanf('%79s',buffer);}while(strcmp(key,buffer)!=0);puts('Correctanswer!');return0;}

点击加载图片

库中声明如下:

_Check_return_int__cdeclstrcmp(_In_z_charconst*_Str1,_In_z_charconst*_Str2);

第一个字符串大于第二个字符串,则返回大于0的数字(vs中为固定值1)

第一个字符串等于第二个字符串,则返回0

第一个字符串小于第二个字符串,则返回小于0的数字(vs中为固定值-1)

1.5strncpy

头文件

从字符串中复制字符

将源的第一个字符数复制到目标。如果在复制num个字符之前找到源C字符串的末尾(由null字符表示),则目标将填充零,直到总共写入num个字符为止。

如果源长度超过num,则不会在目标末尾隐式附加空字符。因此,在这种情况下,不应将目标视为以空结尾的C字符串(这样读取它会溢出)。

目标字符串和源字符串不得重叠

intmain{charstr1[]='Tobeornottobe';charstr2[40];charstr3[40];strncpy(str2,str1,sizeof(str2));strncpy(str3,str2,5);str3[5]='\0';puts(str1);puts(str2);puts(str3);return0;}

点击加载图片

库中声明如下:

__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(char*,__RETURN_POLICY_DST,_ACRTIMP,strncpy,strncpy_s,_Out_writes_z_(_Size)char,_Out_writes_(_Count)_Post_maybez_,char,_Destination,_In_reads_or_z_(_Count)charconst*,_Source,_In_size_t,_Count)

1.拷贝num个字符从源字符串到目标空间。

2.如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

1.6strncat

头文件

从字符串追加字符

将源的第一个数字字符追加到目标,外加一个终止空字符。

如果源中C字符串的长度小于num,则仅复制终止空字符之前的内容。

intmain{charstr1[20];charstr2[20];strcpy(str1,'Tobe');strcpy(str2,'ornottobe');strncat(str1,str2,6);puts(str1);return0;}

点击加载图片

库中声明如下:

__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(char*,__RETURN_POLICY_DST,_ACRTIMP,strncat,strncat_s,_Inout_updates_z_(_Size)char,_Inout_updates_z_(_Count),char,_Destination,_In_reads_or_z_(_Count)charconst*,_Source,_In_size_t,_Count)

1.7strncpy

头文件

从字符串中复制字符

将源的第一个字符数复制到目标。如果在复制num个字符之前找到源C字符串的末尾(由null字符表示),则目标将填充零,直到总共写入num个字符为止。

如果源长度超过num,则不会在目标末尾隐式附加空字符。因此,在这种情况下,不应将目标视为以空结尾的C字符串(这样读取它会溢出)。

目标字符串和源字符串不得重叠

intmain{charstr1[]='Tobeornottobe';charstr2[40];charstr3[40];strncpy(str2,str1,sizeof(str2));strncpy(str3,str2,5);str3[5]='\0';puts(str1);puts(str2);puts(str3);return0;}

点击加载图片

库中声明如下:

__DEFINE_CPP_OVERLOAD_STANDARD_NFUNC_0_2_EX(char*,__RETURN_POLICY_DST,_ACRTIMP,strncpy,strncpy_s,_Out_writes_z_(_Size)char,_Out_writes_(_Count)_Post_maybez_,char,_Destination,_In_reads_or_z_(_Count)charconst*,_Source,_In_size_t,_Count)

1.8strstr

头文件

查找子字符串

返回指向str2中第一次出现的str1的指针,如果str2不是str1的一部分,则返回一个空指针。

匹配过程不包括终止空字符,但它到此为止。

intmain{charstr[]='Thisisasimplestring';char*pch;pch=strstr(str,'simple');puts(pch);if(pch!=NULL)strncpy(pch,'sample',6);puts(str);return0;}

点击加载图片

库中声明如下:

_VCRTIMPchar_CONST_RETURN*__cdeclstrstr(_In_z_charconst*_Str,_In_z_charconst*_SubStr);

1.9strtok

头文件

将字符串拆分为标记

对此函数的一系列调用将str拆分为标记,这些标记是由分隔符中的任何字符分隔的连续字符序列。

在第一次调用时,该函数需要一个C字符串作为str的参数,其第一个字符用作扫描令牌的起始位置。在后续调用中,该函数需要一个空指针,并使用最后一个令牌末尾之后的位置作为扫描的新起始位置。

为了确定标记的开头和结尾,该函数首先从起始位置扫描分隔符中未包含的第一个字符(该字符将成为标记的开头)。然后从令牌的开头开始扫描分隔符中包含的第一个字符,该字符将成为令牌的末尾。如果找到终止空字符,扫描也会停止。

令牌的此结尾将自动替换为空字符,并且令牌的开头由函数返回。

一旦在对strtok的调用中找到str的终止空字符,则对此函数的所有后续调用(以空指针作为第一个参数)都将返回空指针。

找到最后一个令牌的点由要在下一次调用中使用的函数在内部保留(不需要特定的库实现来避免数据争用)。

intmain{charstr[]='-This,asamplestring.';char*pch;printf('Splittingstring\'%s\'intotokens:\n',str);pch=strtok(str,',.-');while(pch!=NULL){printf('%s\n',pch);pch=strtok(NULL,',.-');}return0;}

点击加载图片

库中声明如下:

_Check_return__CRT_INSECURE_DEPRECATE(strtok_s)_ACRTIMPchar*__cdeclstrtok(_Inout_opt_z_char*_String,_In_z_charconst*_Delimiter);

返回值

1.如果找到令牌,则指向令牌开头的指针。

2.否则为空指针。

3.当在正在扫描的字符串中到达字符串的末尾(即空字符)时,始终返回空指针。

1.10strerror

头文件

获取指向错误消息字符串的指针

解释errnum的值,生成一个字符串,其中包含描述错误条件的消息,就像由库的函数设置为errno一样。

返回的指针指向静态分配的字符串,程序不应修改该字符串。对此函数的进一步调用可能会覆盖其内容(不需要特定的库实现来避免数据争用)。

strerror生成的错误字符串可能特定于每个系统和库实现。

intmain{FILE*pFile;pFile=fopen('unexist.ent','r');if(pFile==NULL)printf('Erroropeningfileunexist.ent:%s\n',strerror(errno));return0;}

点击加载图片

我们可以给下面这段代码来看看vs的库中前20个错误信息提示是什么

intmain{for(inti=0;i<20;i++){printf('%d:%s\n',i,strerror(i));}return0;}

点击加载图片

1.11字符分类函数

函数如果他的参数符合下列条件就返回真
iscntrl任何控制字符
isspace空白字符:空格'’,换页'\f’,换行’\n’,回车'\r’,制表符’\t’或者垂直制表符’\v’
isdigit十进制数字0~9
isxdigit十六进制数字,包括所有十进制数字,小写字母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任何可打印字符,包括图形字符和空白字符

字符转换示例:

#include#includeintmain{inti=0;charstr[]='TestString.\n';charc;while(str[i]){c=str[i];if(isupper(c))c=tolower(c);putchar(c);i++;}return0;}

点击加载图片

2.内存函数

2.1memcpy

头文件

复制内存块

将字节数的值从源指向的位置直接复制到目标指向的内存块。

源指针和目标指针指向的对象的基础类型与此函数无关;结果是数据的二进制副本。

该函数不检查源中的任何终止空字符-它总是准确地复制字节数。

为避免溢出,目标和源参数指向的数组大小应至少为字节数,并且不应重叠

struct{charname[40];intage;}person,person_copy;intmain{charmyname[]='PierredeFermat';/*usingmemcpytocopystring:*/memcpy(person.name,myname,strlen(myname)+1);person.age=46;/*usingmemcpytocopystructure:*/memcpy(&person_copy,&person,sizeof(person));printf('person_copy:%s,%d\n',person_copy.name,person_copy.age);return0;}

点击加载图片

库中声明如下:

void*__cdeclmemcpy(_Out_writes_bytes_all_(_Size)void*_Dst,_In_reads_bytes_(_Size)voidconst*_Src,_In_size_t_Size);

1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。

2.这个函数在遇到'\0’的时候并不会停下来。

3.如果source和destination有任何的重叠,复制的结果都是未定义的。

2.2memmove

头文件

移动内存块

将字节数的值从源指向的位置复制到目标指向的内存块。复制就像使用了中间缓冲区一样,允许目标和源重叠。

源指针和目标指针指向的对象的基础类型与此函数无关;结果是数据的二进制副本。

该函数不检查源中的任何终止空字符-它总是准确地复制字节数。

为避免溢出,目标参数和源参数指向的数组的大小应至少为字节数。

intmain{charstr[]='memmovecanbeveryuseful......';memmove(str+20,str+15,11);puts(str);return0;}

点击加载图片

库中声明如下:

_VCRTIMPvoid*__cdeclmemmove(_Out_writes_bytes_all_opt_(_Size)void*_Dst,_In_reads_bytes_opt_(_Size)voidconst*_Src,_In_size_t_Size);

1.和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。

2.如果源空间和目标空间出现重叠,就得使用memmove函数处理。

2.3memcmp

头文件

比较两个内存块

将ptr1指向的内存块的前num字节数与ptr2指向的第一个字节数进行比较,如果它们都匹配,则返回零,如果不匹配,则返回一个不同于零的值,表示哪个值更大。请注意,与strcmp不同,该函数在找到空字符后不会停止比较。

intmain{charbuffer1[]='DWgaOtP12df0';charbuffer2[]='DWGAOTP12DF0';intn;n=memcmp(buffer1,buffer2,sizeof(buffer1));if(n>0)printf(''%s'isgreaterthan'%s'.\n',buffer1,buffer2);elseif(n<0)printf(''%s'islessthan'%s'.\n',buffer1,buffer2);elseprintf(''%s'isthesameas'%s'.\n',buffer1,buffer2);return0;}

点击加载图片

库中声明如下:

int__cdeclmemcmp(_In_reads_bytes_(_Size)voidconst*_Buf1,_In_reads_bytes_(_Size)voidconst*_Buf2,_In_size_t_Size);

1.比较从ptr1和ptr2指针开始的num个字节

2.返回值

返回一个整数值,该值指示内存块内容之间的关系:

点击加载图片

3.函数的模拟实现

3.1模拟实现strlen

三种方式:

方式1:

//计数器方式intmy_strlen(constchar*str){intcount=0;while(*str){count++;str++;}returncount;}

方式2:

//不能创建临时变量计数器intmy_strlen(constchar*str){if(*str=='\0')return0;elsereturn1+my_strlen(str+1);}

方式3:

//指针-指针的方式intmy_strlen(char*s){char*p=s;while(*p!='\0’)p++;returnp-s;}

3.2模拟实现strcpy

参考代码:

char*my_strcpy(char*dest,constchar*src){char*ret=dest;assert(dest!=NULL);assert(src!=NULL);while((*dest++=*src++)){;}returnret;}

3.3模拟实现strcat

参考代码:

char*my_strcat(char*dest,constchar*src){char*ret=dest;assert(dest!=NULL);assert(src!=NULL);while(*dest){dest++;}while((*dest++=*src++)){;}returnret;}

3.4模拟实现strstr

char*strstr(constchar*str1,constchar*str2){char*cp=(char*)str1;char*s1,*s2;if(!*str2)return((char*)str1);while(*cp){s1=cp;s2=(char*)str2;while(*s1&&*s2&&!(*s1-*s2))s1++,s2++;if(!*s2)return(cp);cp++;}return(NULL);}

3.5模拟实现strcmp

参考代码:

intmy_strcmp(constchar*src,constchar*dest){intret=0;assert(src!=NULL);assert(dest!=NULL);while(!(ret=*(unsignedchar*)src-*(unsignedchar*)dest)&&*dest)++src,++dest;if(ret<0)ret=-1;elseif(ret>0)ret=1;return(ret);}

3.6模拟实现memcpy

参考代码:

void*my_memcpy(void*dst,constvoid*src,size_tcount){void*ret=dst;assert(dst);assert(src);while(count--){*(char*)dst=*(char*)src;dst=(char*)dst+1;src=(char*)src+1;}return(ret);}

3.7模拟实现memmove

参考代码:

void*my_memmove(void*dst,constvoid*src,size_tcount){void*ret=dst;if(dst<=src||(char*)dst>=((char*)src+count)){while(count--){*(char*)dst=*(char*)src;dst=(char*)dst+1;src=(char*)src+1;}}else{dst=(char*)dst+count-1;src=(char*)src+count-1;while(count--){*(char*)dst=*(char*)src;dst=(char*)dst-1;src=(char*)src-1;}}return(ret);}

结语

有兴趣的小伙伴可以关注作者,如果觉得内容不错,请给个一键三连吧,蟹蟹你哟!!!

制作不易,如有不正之处敬请指出

感谢大家的来访,UU们的观看是我坚持下去的动力

在时间的催化剂下,让我们彼此都成为更优秀的人吧!!!

点击加载图片

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
嵌入式大杂烩周记 | 第 15 期
浅析C语言中关于字符串的操作 - C/C++ - 课堂 - 话题 - 张剑 - CSDN学...
彻底解密C 宽字符:3、利用C运行时库函数转换
strcpy与memcpy以及strncpy
一文讲解C语言字符串
C语言
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服