前言
- 常见的字符串函数在一定程度上可以让我们在写代码,或者是在刷某些有关字符串的题目时事半功倍,并且常见字符串函数的功能非常常用,因此我们应该熟练使用这些字符串函数,以及部分函数要能自我实现。
- 字符串函数都要引入一个库函数:
string
(#include <string.h>
)
1.长度不受限制的常见字符串函数
strlen
strlen函数是求字符串长度的,遇到
\0
停止(计算\0
之前有多少个字符),如果有多个\0
,则只计算第一个\0
前面的字符个数。
strlen
的使用:
#include <stdio.h> #include <string.h> int main() { char arr[] = "abcdef"; printf("%d\n", strlen(arr)); return 0; }
运行结果:6
arr有6个字符,所以所得为6,值得注意的是,strlen函数的返回值是 size_t(unsigned int),size_t表示无符号整型,但是这里我们用%d形式打印也是没有问题的。
为什么strlen函数的返回值要弄size_t 呢?因为长度是没有负数之说的,所以size_t符合实际,但是size_t又难免会出现一些问题,例如:
#include <stdio.h> #include <string.h> int main() { char a1[] = "abc"; // 3 char a2[] = "abcdef"; // 6 if (strlen(a1) - strlen(a2) < 0) { printf("a1 < a2\n"); } else { printf("a1 > a2\n"); } return 0; }
猜这里输出的结果是什么呢?
正常来说应该输出a1 < a2才对,可是这里的输出是a1 > a2,那么就说明strlen(a1) - strlen(a2) > 0,这是为什么呢?
strlen(a1)返回一个size_t的数3,strlen(a2)返回一个size_t的数6,3 - 6 = -3,此时-3也是一个size_t类型,所以当-3作为一个无符号数来看待的话,那将是一个很大的整数,自然也就大于零输出第一个printf了。
所以库函数中strlen返回值为size_t可以说有利也有弊,需细心使用,接下来我对字符串函数的实现,如果是返回整型的话,我都会采用返回int的。
strlen
的自我实现
这里我的strlen实现有三种方式:计数,指针减指针,递归,他们分别对应
my_strlen1
,my_strlen2
,my_strlen3
。
#include <stdio.h> #include <assert.h> int my_strlen1(const char* s) { assert(s); int count = 0; while (*s) { ++count; // *s 不是 \0 就加一 ++s; } return count; } int my_strlen2(const char* s) { assert(s); const char* cur = s; while (*cur) { cur++; } return (int)(cur - s); // 用 cur 指针找到 \0 ,再用 cur 减去 s 得到之间字符的个数 6 } int my_strlen3(const char* s) { assert(s); if (*s != '\0') return 1 + my_strlen3(s + 1); else return 0; } int main() { char a[] = "abcdef"; printf("%d\n", my_strlen1(a)); // 6 printf("%d\n", my_strlen2(a)); // 6 printf("%d\n", my_strlen3(a)); // 6 return 0; }
strcpy
strcpy
的功能是字符串拷贝,将源头(src
)字符串拷贝到目的地(dest
)字符串当中,并且是从头开始拷贝,src
中的\0
也要拷贝过去。
注意:dest 的字符串长度要大于等于 src ,不然 src 拷贝过去会出现非法访问的错误。
strcpy函数返回目的地字符串(被拷贝后)首元素地址。
strcpy
的使用
#include <stdio.h> #include <string.h> int main() { char arr1[] = "xxxxxxxxxx"; char arr2[] = "abcdef"; printf("%s\n", strcpy(arr1, arr2)); return 0; }
运行结果为:abcdef
strcpy
的自我实现
上图实际上就是整个拷贝的过程,
*dest++ = *src++
是整个代码实现核心。
实现代码如下:
#include <stdio.h> #include <assert.h> char* my_strcpy(char* dest, const char* src) { assert(dest && src); char* ret = dest; // 先要记住dest的起始位置 while (*dest++ = *src++) // 先运算*dest = *src,再判断*dest,再分别++ { ; } return ret; // 返回dest起始位置 } int main() { char arr1[] = "xxxxxxxxxx"; char arr2[] = "abcdef"; printf("%s\n", my_strcpy(arr1, arr2)); return 0; }
strcat
该函数的功能是在目的地字符串末尾追加源字符串(连接),目的地字符串的末尾不包括
\0
,也就是说\0
将会被追加的字符串覆盖。
- 注意:
源字符串必须以\0
结束。
目标空间必须足够的大,能容纳下追加后目的地字符串的所有内容。
目标空间必须可修改。
strcat
的使用
1.正常的追加
#include <stdio.h> #include <string.h> int main() { char arr1[20] = "xxxxx"; char arr2[] = "abc"; printf("%s\n", strcat(arr1, arr2)); return 0; }
运行结果为:xxxxxabc
2.目的地字符串中存在\0
#include <stdio.h> #include <string.h> int main() { char arr1[20] = "xxx\0xxxxxxxx"; char arr2[] = "abcdef"; printf("%s\n", strcat(arr1, arr2)); return 0; }
运行结果为:xxxabcdef
要注意:
strcat不能追加自己,因为再追加自己的同时,末尾的\0在追加的时候被修改了,这时就会死循环,因为要追加的字符串也找不到\0了,此时程序会崩溃。如果要追加自己,可以用下面要讲解的strnpy函数。
strcat
的自我实现
通过上面的解析可以知道,我们首先要让一个指针找到
dest
(目的地字符串
)的\0
,再进行追加(连接),而追加的功能类似于拷贝(*dest++ = *src++
)。
代码实现:
#include <stdio.h> #include <assert.h> char* my_strcat(char* dest, const char* src) { assert(dest && src); char* ret = dest; // 先找到 dest 的第一个 \0 while (*dest) { dest++; } while (*dest++ = *src++) // 追加 { ; } return ret; } int main() { char arr1[20] = "xxxxx"; // 大小为 20 ,为了能够承受住 arr2 的追加 char arr2[] = "abcdef"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }
运行结果为:xxxxxabcdef
strcmp
该函数的功能是比较两个字符串,看相等,小于,还是大于,是小于还是大于是根据字符的ASCLL码值来比较的。而字符的比较是两个字符串一对一对字符的比。
- 如果
str1 < str2
返回一个小于0
的数,如果str1 == str2
返回0
,如果str1 > str2
返回一个大于0
的数。 - 例如
”abc“
与“ac”
比较,a == a
,b != c
,又b
的ASCLL码值``小于``c的ASCLL码值
,所以返回一个小于零
的数。
strcmp
的使用
#include <stdio.h> #include <string.h> int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; char arr3[] = "abcd"; printf("%d\n", strcmp(arr1, arr2)); printf("%d\n", strcmp(arr1, arr3)); return 0; }
运行结果为:-1 1
这是因为vs的strcmp如果小返回-1,大返回1,相等返回0,而标准就是上面所说。
strcmp
的自我实现
这里按标准的返回值来实现
#include <stdio.h> #include <assert.h> int my_strcmp(const char* str1, const char* str2) { assert(str1 && str2); while (*str1 != '\0' || *str2 != '\0') { if (*str1 - *str2) { return *str1 - *str2; } str1++; str2++; } return 0; } int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; printf("%d\n", my_strcmp(arr1, arr2)); return 0; }
运行结果为:-12
2.长度受限制的常见字符串函数
strncpy
该函数的功能是指定拷贝几个字符,与strcpy不同的是,strncpy多了一个确定拷贝字符个数的参数,这也就限制了长度,让使用者更能精确的拷贝自己想要的字符。
同样要注意的是:
1.源字符串拷贝到目的地字符串时不能超出目的地字符串的空间大小;
2.如果拷贝个数小于源字符串的长度,这时不会拷贝\0,也就是“abcdef”,如果拷贝4个,则只拷贝“abcd”过去;
3.如果拷贝个数大于源字符串的长度 + 1(因为后面还有一个\0),则多出来的拷贝放\0。
该函数的函数参数:
strncpy
的使用
1.正常拷贝:
#include <stdio.h> #include <string.h> int main() { char arr1[] = "xxxxxxxxxxxxxxxx"; char arr2[] = "abcdef"; printf("%s\n", strncpy(arr1, arr2, 5)); // 拷贝5个 return 0; }
运行结果为:abcdexxxxxxxxxxx
2.拷贝个数等于
源字符串的长度 + 1
:
#include <stdio.h> #include <string.h> int main() { char arr1[] = "xxxxxxxxxxxxxxxx"; char arr2[] = "abcdef"; printf("%s\n", strncpy(arr1, arr2, 7)); // 拷贝7个,该字符串的长度为6 return 0; }
运行结果为:abcdef
3.拷贝个数大于
源字符串的长度 + 1
:
#include <stdio.h> #include <string.h> int main() { char arr1[] = "xxxxxxxxxxxxxxxx"; char arr2[] = "abcdef"; printf("%s\n", strncpy(arr1, arr2, 10)); // 拷贝10个,arr2不够,后面拷贝\0 return 0; }
运行结果为:abcdef