1.字符串函数的介绍及模拟实现
1.1 strlen
size_t strlen (const char * str)
字符串是以‘\0作为结束标志,strlen函数返回的是在字符串中‘\0’之前出现的字符的个数(不包括‘\0’)。
参数指向的字符串必须要以‘\0’结束。这就意味着,如果要用strlen函数求一个字符串的长度,那你提供的这个字符串末尾必须要有‘\0’。像下面这种写法就是错误的:
注意函数的返回值为size_t,是无符号的。
下面的代码可以证明:
由此可见strlen函数的返回值是无符号的,无符号数永远大于0。
strlen函数的模拟实现:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> size_t my_strlen(const char* str) { int count = 0; while (*str != '\0') { count++; str++; } return count; } int main() { char arr[] = "abcdef"; size_t ret = my_strlen(arr); printf("%u\n", ret); return 0; }
1.2 strcpy
char* strcpy(char * destination, const char * source);
strcpy函数的功能:把source指向的字符串拷贝到destination指向的空间,拷贝时包含结束字符‘\0’。
打开监视窗口可以发现,strcpy连同arr2中的结束字符‘\0’也拷贝到arr1中去了:
注意:使用strcpy函数时,源字符串必须以‘\0’结束;目标空间必须足够大,并且目标空间必须可变。否则拷贝会出现错误
strcpy函数的模拟实现:(该函数的返回值是目的空间的地址)
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strcpy(char* dest, const char* src) { assert(dest != NULL);//断言,判断是否为空指针 assert(src != NULL); while (*dest++ = *src++) { ; } } int main() { char arr1[] = "hello world"; char arr2[] = "xxxxx"; my_strcpy(arr1, arr2); printf("%s\n", arr1); return 0; }
strcpy函数返回值是char*型的原因是:为了实现链式访问,它返回目标空间的地址,那打印时就可以直接在printf函数中调用:
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strcpy(char* dest, const char* src) { char* ret = dest; assert(dest != NULL);//断言,判断是否为空指针 assert(src != NULL); while (*dest++ = *src++) { ; } return ret;//返回目标空间的地址 } int main() { char arr1[] = "hello world"; char arr2[] = "xxxxx"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; }
上述代码最终打印的结果是xxxxx,那我们如果想将hello后面的world覆盖掉就可以做出如下改变:
那如果说此时字符数组arr1中存放的是hello\0,arr2存放的是world\0,我们想将world放在hello后面该怎么办呢?
这就要用到strcat函数了。
1.3 strcat
char* strcat ( char * destination, const char * source )
strcat函数的功能就是将source指向的字符串追加到destination指向的目标空间中的字符串后面,注意strcat函数的返回值类型也是char*的,它返回的是目标空间的地址,也可以实现链式访问。
注意:使用strcat函数时,源字符串必须以‘\0’结束;目标空间必须足够大,并且目标空间必须可修改。
了解了strcat函数的具体功能,下面我们来模拟实现一下:
先来分析,要想在arr1中的字符串后面追加字符串,就要先找到arr1中字符串在哪里结束,然后在其后面追加,追加部分的代码其实与strcpy的功能一致。
strcat函数的模拟实现:
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strcat(char* dest, const char* src) { char* ret = dest; assert(dest && src); //找到目标字符串的结束字符 while (*dest) { dest++; } //在目标字符串后面追加字符串 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = "world"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }
现在来思考一个问题,如果我要用strcat函数自己给自己追加可不可以?
试一下:
很明显,此时程序崩了。
为什么会崩呢?
因为要是自己给自己追加,此时dest和src指向的是同一位置,经过第一次while循环后,dest指向\0,然后进入第二次循环,此时\0被改成h,而\0对src很重要,\0没有了,意味着src找不到结束的位置了,所以dest和src你走一步,我走一步,陷入死循环,代码自然就崩了。
1.4 strcmp
int strcmp ( const char * str1, const char * str2 )
strcmp函数的功能是:比较两个字符串的大小。
如果第一个字符串大于第二个字符串,则返回一个大于0的数字。
如果第一个字符串等于第二个字符串,则返回0。
如果第一个字符串小于第二个字符串,则返回一个小于0的数字。
它的比较方式,实际上是一个字节一个字节的比较的,字符串1和字符串2中只要有一对比出大小,后面的就不在作比。
注意strcmp函数的返回值类型是int型, 在vs编译器中,返回值分别是:1,-1,0
strcmp函数的模拟实现:
#include<stdio.h> #include<string.h> #include<assert.h> int my_strcmp(const char* str1, const char* str2) { assert(str1 && str2); while (*str1 == *str2) { if (*str1 == '\0') { return 0; } str1++; str2++; } if (*str1 > *str2) { return 1; } else return -1; } int main() { int ret = my_strcmp("abcdef", "abq"); printf("%d\n", ret); return 0; }
因为strcmp函数只规定了返回大于0,等于0,小于0的数,不一定非的是1,0,-1,所以我们可以对代码简化一下:
#include<stdio.h> #include<string.h> #include<assert.h> int my_strcmp(const char* str1, const char* str2) { while (*str1 == *str2) { if (*str1 == '\0') { return 0; } str1++; str2++; } return (*str1 - *str2); } int main() { int ret = my_strcmp("abcdef", "ab"); printf("%d\n", ret); return 0; }
前面学的strcpy,strcat,strcmp这三个函数都只关注\0,它们属于长度不受限制的字符串函数,下面我们来学几个长度受限制的字符串函数。
1.5 strncpy
char* strncpy( char * destination, const char * source, size_t num )
strncpy相对于strcpy函数,参数多了一个num,它的功能是:把source指向的num个字符拷贝到destination指向的空间,拷贝时包含结束字符‘\0’。
上述代码中,把arr2中的3个字符拷贝到arr1中去。
那如果arr2中只有3个字符,而使用strncpy时却要求拷贝5个会发生什么呢?
我们可以到,它把不够的两个用'\0'补上了。
其实strncpy函数就相当于在strcpy函数上增加了一个长度的限制。
1.6 strncat
char * strncat ( char * destination, const char * source, size_t num );
strncat函数相对于strcat函数,参数多了一个参数num,它的功能就是将source指向的num个字符追加到destination指向的目标空间中的字符串后面。
类似于strncpy,但是它和strncpy又有区别:
它在追加了3个字符结束后还加上了'\0',因为arr1最终要是一个字符串,就必须有\0。
而如果arr2中只有3个字符,我们又要求追加5个字符呢?
我们可以看到,它只追加了3个,后面没有再补\0,这就是strncat和strncpy的区别。