常用字符串函数模拟
目录
4:字符分类与操作函数(分类函数判断条件为真时,返回真(1))
1: 拷贝函数memcpy、memmove、strcpy和strncpy
0、常用字符串汇总
C语言的字符串都是放在常量字符串或字符数组中。
常用字符串函数如下:
1:字符串信息提取函数
(1)strlen :求字符串长度(模拟实现)
(2)strstr:字符串查找(模拟实现)
2:内存操作函数
(1)memcpy:内存拷贝函数(模拟实现)
(2)memmove:内存拷贝函数(模拟实现)
(3)memset:内存初始化函数(模拟实现)
(4)memcmp:内存比较函数(模拟实现)
3:字符串操作函数
(1)strcpy:字符串复制函数(长度不受限制)(模拟实现)
(2)strcat:字符串连接函数(长度不受限制)(模拟实现)
(3)strcmp:字符串比较函数(长度不受限制)(模拟实现)
(4)strncpy:字符串复制函数(长度受限制)(模拟实现)
(5)strncat:字符串连接函数(长度受限制)(模拟实现)
(6)strncmp:字符串比较函数(长度受限制)(模拟实现)
4:字符分类与操作函数(分类函数判断条件为真时,返回真(1))
(1)iscntrl:判断字符是否为控制字符
(2)isspace:判断字符是否为空白字符
(3)isdigit:判断字符是否为十进制数字字符
(4)isxdigit:判断字符是否为十六进制数字字符
(5)islower:判断字符是否为小写字母
(6)isupper:判断字符是否为大写字母
(7)isalpha :判断字符是否为字母(不分大小写)
(8)isalnum:判断字符是否为字母(不分大小写)或数字字符
(9)ispunct:判断字符是否为标点符号(不属于数字字符、字母和图像字符的字符都叫标点字符)
(10)isgraph:判断字符是否为图像字符
(11)isprint:判断字符是否可打印
(12)tolower:将大写字母转化为小写字母(如果字符不是大写字母,则不转换)
(13)toupper:将小写字母转化为大写字母(如果字符不是小写字母,则不转换)
本文对部分字符串信息提取函数、内存操作函数和字符串操作函数进行原理讲解和模拟实现。
1、字符串信息提取函数
1:strlen
1. 原理
函数作用:计算字符串长度(不包括字符串结束符:'\0')
函数结构:size_t strlen( const char *string );
函数参数 参数 介绍(源于MSDN) string 目标字符串起始地址。数据类型:const char* 返回值 字符串长度。数据类型:int 需要注意的点:strlen碰到'\0'则停止计数。
2. 模拟代码
这里给出递归和计数器方法模拟strlen的代码
计数器模式:
int Str_len(const char *arr) { char* p = arr; while (*(p++) != '\0'); return p - arr - 1; }
递归方式:
unsigned int Str_len(char* strs) { if (*strs == '\0') { return 0; } else { return 1 + Str_len(strs + 1); } } }
3. 测试与结果分析
测试代码如下:
int main() { char arr[] = "abscjdksuslaj asdl"; printf("%d", Str_len(arr)); return 0; }
程序运行结果:
2:strstr
1. 原理
函数作用:查找子字符串
函数结构:char *strstr( const char *string, const char *strCharSet );
函数参数 参数 介绍 string 目标字符串首地址。参数类型:const char * strCharSet 子字符串首地址。参数类型:const char * 返回值 如果目标字符串中存在子字符串,则返回子字符串第一次出现时目标字符串的地址。 函数概述:如果目标字符串中不存在子字符串,则返回NULL;如果子字符串为NULL,则返回目标字符串的首地址。 说白了,strstr就是求解子串的问题。(假设只有一个目标字符串和一个子字符串)
求解思路:(1)暴力求解法(2)KMP算法
(1)暴力求解法
废话不说,直接上图:
这里需要说明的是:这种方法比较简单,其时间复杂度为O(M*N)。
(2)KMP算法求解
KMP是专门求解子串包含问题的一种算法,其优势在于KMP对子字符串信息挖掘较多。
本人也找过KMP算法的介绍,大多给出一堆公式和大段文字进行讲解,感觉把人将蒙了。这是因为:KMP算法原理不难,但是叙述起来比较麻烦。所以,这里就不叙述了,直接上图。
如果还是感觉过于复杂或看不懂,推荐直接看代码,代码不长,把判断条件看明白就行。另外,本人网上查资料时碰见几个将KMP的博客,这里给出链接。
https://www.cnblogs.com/zhangboy/p/7635627.html
2. 暴力求解法模拟代码和结果测试分析
//暴力求解法 char* my_strstr1(const char* string, const char* strcharset) { int i = 0;//目标字符串下标 int j = 0;//子字符串下标 if (*strcharset == '\0') { return string; } else { int num = 0; char* s = strcharset; while (*s++) { num++; } while (1) { if (*(string + i + num - 1) == '\0') //目标字符串到结尾了还没跳出(说明目标字符串不含子串) { return NULL; } else { if (*(strcharset + j) == '\0') //b已全部判断完毕,目标字符串中包含子串 { return string + i; } else { if (*(string + i + j) == *(strcharset + j)) //匹配到字符 { j++; } else { j = 0; i++; } } } } } }
测试代码:
int main() { char a[] = "abcdefa"; char b[] = "def"; char c[] = ""; char d[] = "acef"; printf("测试1\n"); if (my_strstr1(a, b) == &a) { printf("b为空字符串\n"); } else if (my_strstr1(a, b) == NULL) { printf("目标字符串中不包含子字符串\n"); } else { printf("目标字符串包含子字符串,起始位置为:%p,\n在目标字符串中的:%c处起始\n", my_strstr1(a, b), *my_strstr1(a, b)); } printf("测试2\n"); if (my_strstr1(a, c) == &a) { printf("b为空字符串\n"); } else if (my_strstr1(a, c) == NULL) { printf("目标字符串中不包含子字符串\n"); } else { printf("目标字符串包含子字符串,起始位置为:%p,\n在目标字符串中的:%c处起始\n", my_strstr1(a, c), *my_strstr1(a, c)); } printf("测试3\n"); if (my_strstr1(a, d) == &a) { printf("b为空字符串\n"); } else if (my_strstr1(a, d) == NULL) { printf("目标字符串中不包含子字符串\n"); } else { printf("目标字符串包含子字符串,起始位置为:%p,\n在目标字符串中的:%c处起始\n", my_strstr1(a, d), *my_strstr1(a, d)); } return 0; }
结果分析:分析比较简单,这里直接给出程序运行结果图
3. KMP法模拟代码和结果测试分析
KMP代码:代码比较长是因为我加了很多额外条件判断,KMP的关键代码注释:关键代码
//前后缀法求解Next数组 void KMP_Next(const char* string, int* next) { *next = -1; int k = -1; int j = 0; while (*(string + j + 1) != '\0') //判断结束 { if (k == -1 || *(string + j) == *(string + k)) { *(next + j + 1) = k + 1; k++; j++; } else { k = *(next + k); } } } //KMP算法模拟strstr char* my_strstr_KMP(const char* string, const char* strcharset) { //先判断子字符串是否为空? 如果是返回string if (*strcharset == '\0') { return string; } char* s1 = string; char* s2 = strcharset; int sit = 0;//报错信息 //先计算子字符串的长度(因为要定义next数组) int num = 0; while (*s2++) { num++; } int* next = NULL; while (next == NULL) { next = malloc(num * 4); //动态开辟内存(最后要释放) sit++; if (sit == 10) //连续10次都无法成功开辟,则报错,直接返回 { perror("无法为NEXT数组开辟内存!"); } } //Next数组求解 KMP_Next(strcharset,next); //KMP算法 int i = 0; int j = 0; while (*(string + i) != '\0' && *(strcharset + j) != '\0') //结束标志 { if (j == 0 || *(string + i) == *(strcharset + j)) //KMP关键判断语句(看这里,看这里,看这里,看这里,看这里,看这里,看这里) { i++; j++; } else { j = next[j]; //KMP关键判断语句(看这里,看这里,看这里,看这里,看这里,看这里,看这里) } } if (*(strcharset + j) == '\0') //匹配成功,返回地址信息 { free(next); next = NULL; return string + i - num; } else if (*(string + i) == '\0') { free(next); next = NULL; return NULL; //表示目标字符串不含子串 } }
测试代码:
这里单独测试了Next求解函数的正确性,然后分别举例测试KMP
int main() { //测试1:测试前后缀法求解Next数组 //对于字符串:"DABCDABD " 其Next数组={-1,0,0,0,0,1,2,3}; char aa[] = "DABCDABD"; int a_next[8] = {0}; KMP_Next(aa,a_next); printf("测试1\n\n"); printf("子字符串:DABCDABD 的Next数组元素为: "); for (int i = 0; i < 8; i++) { printf("%d ", a_next[i]); } printf("\n"); //测试2:KMP测试 char a[] = "abcdefa"; char b[] = "def"; char c[] = ""; char d[] = "acef"; printf("\n测试2\n\n"); if (my_strstr_KMP(a, b) == &a) { printf("b为空字符串\n"); } else if (my_strstr_KMP(a, b) == NULL) { printf("目标字符串中不包含子字符串\n"); } else { printf("目标字符串包含子字符串,起始位置为:%p,\n在目标字符串中的:%c处起始\n", my_strstr_KMP(a, b), *my_strstr_KMP(a, b)); } printf("\n测试3\n\n"); if (my_strstr_KMP(a, c) == &a) { printf("b为空字符串\n"); } else if (my_strstr_KMP(a, c) == NULL) { printf("目标字符串中不包含子字符串\n"); } else { printf("目标字符串包含子字符串,起始位置为:%p,\n在目标字符串中的:%c处起始\n", my_strstr_KMP(a, c), *my_strstr_KMP(a, c)); } printf("\n测试4\n\n"); if (my_strstr_KMP(a, d) == &a) { printf("b为空字符串\n"); } else if (my_strstr_KMP(a, d) == NULL) { printf("目标字符串中不包含子字符串\n"); } else { printf("目标字符串包含子字符串,起始位置为:%p,\n在目标字符串中的:%c处起始\n", my_strstr_KMP(a, d), *my_strstr_KMP(a, d)); } return 0; }
程序运行结果为:
2、内存操作函数
1:memcpy
1. 原理
函数作用:内存复制函数。
函数结构为:void *memcpy( void *dest, const void *src, size_t count );
函数参数介绍 参数 介绍(源于MSDN) dest 目标内存空间的起始地址。参数类型:void * src 源空间的起始地址。参数类型:const void * count 复制内存空间大小(单位:字节)。参数类型:unsigned int 返回值 返回dest的值 函数功能:从源空间起始地址开始,将往后的count个字节的内容复制到目标
内存空间之中。(要求:dest<=src或 src+count<=dest)
需要注意的一点是:使用本函数时,必须保证 dest<=src或 src+count<=dest。
由于memcpy与memmove原理相似,memcpy的原理详解放在了memmove小节中(2.2 memmove)。这里只给出模拟代码和测试。
2. 模拟代码
void* my_memcpy(void* dest, const void* src, uint count) { char* des = (char*)dest; if (src >= dest || (char*)src + count <= (char*)dest) { while (count--) { *(char*)des = *(char*)src; des = (char*)des + 1; src = (char*)src + 1; } } else { printf("源空间和目标空间有重叠,复制会出错。请使用memmove函数,本函数不会进行复制。\n"); } return dest; }
3. 测试与结果分析
测试代码为:
int main() { //测试字符串 char a[] = "12345678901234567890"; char b[] = "abcdefghijklmnopqrst"; char c[] = "abcdefghij1234567890"; printf("测试情况1\n更改前:%s\n", a); my_memcpy(a + 9, a + 4, 5); //dest>=src+n printf("更改后:%s\n", a); printf("测试情况2\n更改前:%s\n", b); my_memcpy(b, b + 5, 5); //dest<=src printf("更改后:%s\n", b); printf("测试情况3\n更改前:%s\n", c); my_memcpy(c + 4, c, 10); //src+n>dest>src printf("更改后:%s\n", c); return 0; }
输出分析:
(1)对于情况1的测试
my_memcpy(a + 9,a + 4,5); //dest>=src+n
从a[4]起始的5个字节的内容("56789")复制到a[9]起始的5个字节的空间(“01234”)。此时dest>=src+n,从低地址往高地址正常复制即可,复制完应为:“123456789(a[0]到a[8]不动)56789(a[9]到a[13]为a[4]到a[8]粘贴的内容)567890(a[14]到a[19]不动)”,故结果为:"12345678956789567890"
(2)对于情况2的测试
my_memcpy(b, b+5, 5); //dest<=src
从b[5]起始的5个字节的内容("fghij")复制到b[0]起始的5个字节空间("abcde")。此时dest<=src,从低地址往高地址正常复制即可,复制完应为:"fghij(被粘贴的内容)fghijklmnopqrst(后面的不变)",故结果为:"fghijfghijklmnopqrst"
(3)对于情况3的测试
my_memcpy(c+4, c, 10); //src+n>dest>src
不满足my_memcpy的使用条件,所以直接报错,并且不对字符串c进行任何操作。
程序运行结果:
2:memmove
1. 原理
函数作用:内存复制函数。
函数结构为:void *memmove( void *dest, const void *src, size_t n);
函数参数介绍 参数 介绍(源于MSDN) dest 目标内存空间的起始地址。参数类型: void * src 源空间的起始地址。参数类型:const void * n 复制内存空间大小(单位:字节)。参数类型:unsigned int 返回值 返回dest的值 函数功能:从源空间起始地址开始,将往后的count个字节的内容复制到目标内存空间之中。 内存复制之中存在以下三种情况:
问题:为什么情况1和情况2对memmove来说是一样的?
因为memmove是将源空间的内容复制到目标空间,那么对于源空间来说,源空间没有完成复制的空间是不允许被更改的。就比如对于情况3来说:一开始,源空间src地址处的内容会被复制,并粘贴到目标空间的dest地址处。但dest地址也属于源空间,并且此时dest地址处的内容并未被复制,所以这种行为是不被允许的。对于情况2来说,虽然源空间和目标空间也有重叠,但目标空间中src地址处空间被粘贴内容时,源空间src地址处的内容已经被复制完成了,所以没有影响。(简而言之:如果一块空间既属于源空间,有属于目标空间,那么这块空间的内容在被复制之前,不可对其进行粘贴操作)
怎么解决?
我们要注意到的是:传统意义上的复制和粘贴都是从低地址开始的,这是因为函数参数就是源空间和目标空间的起始地址。但对于情况3而言,这种从低地址到大地址的复制方法不可行,那么就反其道而行,从大地址往小地址进行复制(即对于情况3而言:首先复制src+n地址处的内容,粘贴到dest+n中,然后往低地址进行复制和粘贴操作)
2. 模拟代码
void* my_memmove(void* dst, const void* src, uint count) { void* ret = dst; if (dst <= src || (char*)dst >= ((char*)src + count)) { //情况1和情况2 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); }
3. 测试与结果分析
int main() { //测试字符串 char a[] = "12345678901234567890"; char b[] = "abcdefghijklmnopqrst"; char c[] = "abcdefghij1234567890"; printf("测试情况1\n更改前:%s\n", a); my_memmove(a + 9,a + 4,5); //dest>=src+n printf("更改后:%s\n",a); printf("测试情况2\n更改前:%s\n", b); my_memmove(b, b+5, 5); //dest<=src printf("更改后:%s\n", b); printf("测试情况3\n更改前:%s\n", c); my_memmove(c + 4, c, 10); //src+n>dest>src printf("更改后:%s\n", c); return 0; }
输出分析:
(1)对于情况1的测试
my_memmove(a + 9,a + 4,5); //dest>=src+n
从a[4]起始的5个字节的内容("56789")复制到a[9]起始的5个字节的空间(“01234”)。此时dest>=src+n,从低地址往高地址正常复制即可,复制完应为:“123456789(a[0]到a[8]不动)56789(a[9]到a[13]为a[4]到a[8]粘贴的内容)567890(a[14]到a[19]不动)”,故结果为:"12345678956789567890"
(2)对于情况2的测试
my_memmove(b, b+5, 5); //dest<=src
从b[5]起始的5个字节的内容("fghij")复制到b[0]起始的5个字节空间("abcde")。此时dest<=src,从低地址往高地址正常复制即可,复制完应为:"fghij(被粘贴的内容)fghijklmnopqrst(后面的不变)",故结果为:"fghijfghijklmnopqrst"
(3)对于情况3的测试
my_memmove(c+4, c, 10); //src+n>dest>src
从c[0]起始10个字节的空间("abcdefghij")被复制到c[4]起始10个字节的空间("efghij1234"),此时src+n>dest>src,需要从高地址往低地址进行复制,复制完应为:"abcd(c[0]到c[3]不变) abcdefghij(被粘贴的内容)567890(c[14]到c[19]不变])"
程序运行结果:
3:memset
1. 原理
函数作用:将内存块设置为指定字符(初始化)
函数结构为:void *memset( void *dest, int c, size_t count );
函数参数介绍 参数 介绍(源于MSDN) dest 目标内存空间的起始地址。参数类型: void * c 初始化字符的ASCII值(具体可查ASCII表,举例:'0'=48),参数类型:int count 初始化内存空间大小(单位:字节)。参数类型:unsigned int 返回值 返回dest的值 函数功能:从目标空间起始地址开始,将往后的count个字节的内容设置为c对应的字符。 这里给出ASCII表:
2. 模拟代码
void* my_memset(void* dest, int c, uint count) { char* des = dest; while (count --) { *des = c; des++; } return dest; }
3. 测试与结果分析
测试代码:
int main() { char a[11]="1234567890"; printf("未初始化之前的字符串:%s\n",a); printf("前5个字符初始化为字符*,后5个字符初始化为字符0\n"); my_memset(a, '*', 5); printf("初始化之后的字符串:%s\n", a); my_memset(a + 5, 48, 5); printf("初始化之后的字符串:%s\n", a); return 0; }
这里要说的就一点:函数传参时,无论函数参数是int型还是char型,我们都可以传递字符或数字,函数会通过ASCII表自己进行匹配。
比如:测试代码中my_memset的第二个参数为int型,如果我们调用时传递字符(my_memset(a, '*', 5)),函数会自动将 '*' 转化为42。反之如果函数参数为char型,我们传递int型数字时,函数会自动将数字转化为对于的ASCII字符。
测试结果:
4:memcmp
1. 原理
函数作用:内存比较函数
函数结构为:int memcmp( const void *buf1, const void *buf2, size_t count );
函数参数 参数 介绍(源于MSDN) buf1 一块内存空间的起始地址。参数类型:const void * buf2 另一块内存空间的起始地址。参数类型:const void * count 内存比较长度(单位:字节)。参数类型:unsigned int 返回值 返回比较结果(*buf1-*buf2:相等返回0 不相等返回正数或负数) 函数功能:比较二块内存空间的大小 内存比较原理:
首先从buf1和buf2空间的起始地址开始比较,判断起始地址处的字符是否相等。如果相等,地址增加,继续判断二块空间的字符是否相等。如果不相等,返回此地址字符相减的结果(二个字符相减,相当于二个字符ASCII对应的数字相减,得到的数字作为int型数据,并将此数值返回)。
所以函数功能可概述为:判断二块内存空间的字符串是否相等,如果相等返回0。如果不相等,则返回第一个不相等地址处字符的差值。
2. 模拟代码
int my_memcmp(const void* buf1, const void* buf2, uint count) { while (count--) { if (*(char*)buf1 == *(char*)buf2) { buf1=(char*)buf1 + 1; buf2 = (char*)buf2 + 1; } else { return (int)(*(char*)buf1 - *(char*)buf2); } } return 0; }
3. 测试与结果分析
测试代码:
int main() { //测试1 相等字符串 char a[] = "abcde"; char b[] = "abcde"; if (my_memcmp(a, b, 5) == 0) { printf("二个字符串相等\n"); } else { printf("二个字符串不相等,返回结果为:%d\n", my_memcmp(a, b, 5)); } //测试2 不相等字符串 char c[] = "abcdefg"; char d[] = "abcde!"; if (my_memcmp(c, d, 6) == 0) { printf("二个字符串相等\n"); } else { printf("二个字符串不相等,返回结果为:%d\n", my_memcmp(c, d, 6)); } return 0; }
结果分析:
测试1:测试二个相等字符串,这没什么好说的。输出肯定为:二个字符串相等、
测试2:二个字符串的第6个字符不相同,c[5]='f'对应ASCII数字为102,d[5]='!'对应ASCII数字为33,。那么返回值应为:102-33=69。
程序运行结果:
3、字符串操作函数
字符串操作主要有:复制、连接、和比较。
1:strcpy和strncpy
1. 原理
函数作用:将一个字符串复制到另一个字符串之中。
函数结构:
char *strcpy( char *strDestination, const char *strSource );
char *strncpy( char *strDest, const char *strSource, size_t count );
二个函数都是将strSource起始位置的字符串复制到strDestination起始的空间中。
二者区别:
(1)strcpy复制时,如果*strSource =='\0',则停止复制(把最后的'\0'也复制过去),否则strSource ++。最后返回strDestination。
(2)strncpy复制是,可以指定复制的长度。但如果strncpy至strncpy+count空间内存在'\0',则'\0'之后的字符都被替换为'\0'。最后返回strDest。
(3)与内存复制函数不同,在使用strcpy和strncpy时,必须保证二块空间没有重叠,否则会出现意外的错误。
2. strcpy模拟代码和测试
模拟代码:
char* my_strcpy(char *arr2,const char *arr1) { char* s = arr2; while (*( s ++) = *( arr1 ++)); return arr2; }
测试程序:
int main() { char arr1[200] = { '0' }; char arr2[200] = { '0' }; int i = 0; int j = 0; printf("请输入一个字符串\n"); while (scanf("%c", &arr1[i]) && arr1[i] != '\n')//将键入的字符串存放到arr中 { i++; } printf("共键入%d个字符\n",i); printf("粘贴前,arr2的前%d个字符为:\n", i); for (j = 0; j <= i; j++) { printf("%c", arr2[j]); } printf("\n粘贴后,arr2的前%d个字符为:\n", i); my_strcpy(arr2,arr1); for (j = 0; j <= i; j++) { printf("%c",arr2[j]); } }
程序运行结果:
3.strncpy模拟代码和测试
模拟代码:
char* my_strncpy(char* str1, const char* str2, uint num) { char* s = str1; while (num--) { if (*str2 != '\0') { *s = *str2; s++; str2++; } else //num>str2的长度,直接终止 { break; } } return str1; }
参数程序:
int main() { char a[20]= "abcdefghiss"; printf("原字符串为:%s\n要粘贴的字符串为:123\n 粘贴长度为4,粘贴起始位置为f\n粘贴完成后的字符串为:%s",a, my_strncpy(a+5,"123",4)-5); return 0; }
程序运行结果:
2: strcat和strncat
1. 原理
函数作用:将一个字符串粘贴到另外一个字符串后面。
函数结构:
char *strcat( char *strDestination, const char *strSource );
char *strncat( char *strDest, const char *strSource, size_t count );
二个函数都是将strSource起始位置的字符串粘贴到strDestination起始空间的后面。
二者区别:
(1)无论是strcat还是strncat,都要求strDestination起始的字符串结尾有'\0'。而strcat还要求strSource 的结尾也有'\0',否则就停不下来了。
(2)使用strcat时,必须要保证strDestination的空间够大,必须能放得下strSource 起始的字符串。
(3)无论是strcat都不能用来粘贴自身,因为这样会把字符串结尾的'\0'给覆盖度,导致意外的现象。而strncat可以。
2. strcat模拟代码和测试
模拟代码:
char* my_strcat(char* strdest, const char* strsource) { char* s = strdest; while (*s != '\0') { s++; } while (*strsource != '\0') { *s = *strsource; s++; strsource++; } return strdest; }
参数程序:
int main() { char a[20] = "abcdefg..!"; char b[] = "zah scabv"; printf("原字符串为:%s\n%s\n粘贴长度为:8\n粘贴后的字符串为:%s", a, b, my_strcat(a, b)); return 0; }
程序运行结果:
3.strncat模拟代码和测试
模拟代码:
char* my_strncat(char * strdest, const char *strsource, uint count) { char * s= strdest; while (*s != '\0') { s++; } while (count--) { if (*strsource != '\0') { *s = *strsource; s++; strsource++; } else //count>strsource的长度 直接终止 { break; } } return strdest; }
参数程序:
int main() { char a[20] = "abcdefg..!"; char b[] = "zah scabv"; printf("原字符串为:%s\n%s\n粘贴长度为:8\n粘贴后的字符串为:%s",a,b, my_strncat(a,b,8)); return 0; }
程序运行结果:
3:strcmp和strncmp
1. 原理
函数作用:比较字符串
函数结构:
int strcmp( const char *string1, const char *string2 );
int strncmp( const char *string1, const char *string2, size_t count );
二个函数都是将从左往右比,比的是字符对应ASCII的大小。碰见第一个不相同的字符则停止比较,返回二个字符ASCII对应数字的差,返回值数据类型为:int
返回值= (int)(*string1 - *string2 )
二者区别:strncmp可以规定比较范围,而strcmp会全部比完或碰见不同字符时停止。
2. strcmp模拟代码和测试
模拟代码:
int my_strcmp(const char* buf1, const char* buf2) { while (*buf1++ == *buf2++) { if (*buf1 == '\0') //字符串相等 return 0; } return (int)(*buf1 - *buf2); }
参数程序:
int main() { //测试1 相等字符串 char a[] = "abcde"; char b[] = "abcde"; if (my_strcmp(a, b, 5) == 0) { printf("二个字符串相等\n"); } else { printf("二个字符串不相等,返回结果为:%d\n", my_strcmp(a, b)); } //测试2 不相等字符串 char c[] = "abcdefg"; char d[] = "abcde!"; if (my_strcmp(c, d) == 0) { printf("二个字符串相等\n"); } else { printf("二个字符串不相等,返回结果为:%d\n", my_strcmp(c, d)); } return 0; }
程序运行结果:
3.strncmp模拟代码和测试
模拟代码:
int my_strncmp(const char* buf1, const char* buf2, uint count) { while (count--) { if (*buf1 == *buf2) { buf1 ++; buf2 ++; } else { return (int)(*buf1 - *buf2); } } return 0; }
参数程序:
int main() { //测试1 相等字符串 char a[] = "abcde"; char b[] = "abcde"; if (my_strncmp(a, b, 5) == 0) { printf("二个字符串相等\n"); } else { printf("二个字符串不相等,返回结果为:%d\n", my_strncmp(a, b, 5)); } //测试2 不相等字符串 char c[] = "abcdefg"; char d[] = "abcde!"; if (my_strncmp(c, d, 6) == 0) { printf("二个字符串相等\n"); } else { printf("二个字符串不相等,返回结果为:%d\n", my_strncmp(c, d, 6)); } return 0; }
程序运行结果:
5、结束语和一些相似函数的对比
1: 拷贝函数memcpy、memmove、strcpy和strncpy
memcpy和memmove都是将一块内存块的内容复制到另一块内存中。唯一的区别是:如果二个内存块的空间重叠,必须使用memmove。当二个内存块空间不重叠时,二个函数的功能没有区别。(就相当于,如果你想把一个桌子从一个房间搬到另外一个房间,memcpy和memmove都可以帮你实现这个效果。但如果你只是想把桌子往旁边挪一挪,那么就必须使用memmove)。而strcpy和strncpy是字符串操作函数,理论为strcpy和strncpy能干的事情,memcpy和memmove都能干,反之不一定。
区别1:memcpy、memmove和strncpy都需要输入拷贝字符的数量,而strcpy则不需要,其碰见字符串中的'\0'自动停止,所以在使用字符数组时,一定要确定字符串最后有个'\0'。
区别2:strcpy和strncpy使用时一定要注意二个字符串空间不能重叠,否则会出现意外。
区别3:strncpy在复制时碰见'\0',后面都会复制为'\0'。
2: 比较函数memcmp、strcmp和strncmp
strcmp和strncmp是比较字符串是否相同,唯一的区别的strncmp可以控制比较范围。
memcmp是内存比较函数,与字符比较函数的区别是:strcmp和strncmp在比较时,需要注意字符串是否结束,即使是strncmp也不会比较'\0'后面的字符。而memcmp不管那么多,哪怕是'\0',也会进行比较。
3:结束语
文章较长,这是因为知识点太多。还有很多文中没有涉及的字符串函数,后期本人会继续对本文进行完善。