字符串是一种重要的数据类型,但是 C 语言并未提供显式的字符串数据类型,而是用字符串常量或者字符数组来表示字符串。
字符串表示
字符串是一个或多个字符,并以空字符 ''
作为终止符。
char str[14] = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!'};
上述字符数组声明了 14 个字符,但初始化中只有 13 个字符,那是因为最后一个字符会自动加上 ''
终止符结束,当然,也可以手动加上。
char str[14] = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', ''};
C 语言提供了一种更简洁的方式来对字符数组进行初始化。
char str[] = "Hello, World!";
字符串常量一般是用双引号 ""
括起来的一串字符来表示,并且不可被修改。
“Hello, World!”
实际上,字符串常量是通过第一个字符的地址存储的,而不是存储字符本身,可以通过 C 语言提供的指针来指向字符串常量。
char *str = "Hello, World!";
字符串处理
字符串声明与初始化后,还需要处理字符串,如计算字符串长度、字符串复制等操作,C 语言已经提供了处理字符串的库函数,它们的原型位于头文件 string.h
中。
strlen
字符串长度是指它所包含的字符个数。string.h
库函数中提供了计算字符串长度的原型。
size_t strlen(char const *string);
如果想计算长度,如下所示。
char s1[14] = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', ''};
strlen(str); // 13 ‘’ 不计入长度
char s2[] = "Hello";
char s3[] = "World!";
strlen(s2); // 5
strlen(s3); // 6
strlen
返回一个类型为 size_t
值,该类型在头文件 stddef.h
中定义的,是无符号整数类型。在表达式中使用无符号数可能导致不可预料的结果。
if (strlen(s2) >= strlen(s3))
...
else
...
上述结果可以会跳到 else
分支中运行。
if (strlen(s2) - strlen(s3) >= 0)
...
else
...
上述语句永远不会跳到 else
中去,因为 strlen
的结果是无符号数,操作符 >=
左边的表达式也将是无符号数,而无符号数绝不可能是负,因此将永远为真。
strcpy
想要复制字符串,可以使用库函数 string.h
中提供的 strcpy
函数,原型如下所示。
char *strcpy(char *dst, const char *src);
该函数会把字符串 src
复制到 dst
参数,并返回一个指向目标字符数组的指针拷贝。如果参数 src
和 dst
在内存中出现重叠,其结果是未定义的。由于 dst
参数将进行修改,所以它必须是个字符数组或者是一个指向动态分配内存的数组的指针,不能使用字符串常量。
目标参数之前存储的值会被覆盖并丢失。
char s1[] = "Hello, World!";
char s2[] = "The Computer";
strcpy(s1, s2); // The Computer
新字符串以 ''
结尾,老字符串 s1
最后剩余的几个字符也被有效地删除。
当 src
参数长度比目标参数 dst
要长,多余的字符仍被复制,但是会覆盖 dst
后面的内存空间的值。strcpy
无法解决这个问题,因为它无法判断 dst
的长度。
char s1[] = "The Computer";
char s2[] = "Hello, World!";
strcpy(s1, s2); // Hello, World!
strncpy
除了 strcpy
,库函数还提供了有长度限制的字符串复制函数,函数原型如下所示。
char *strncpy(char *dst, char const *src, size_t len);
strncpy
函数是通过传递的 len
长度将 src
参数中对应的长度复制到 dst
参数中。如果 src
的长度大于或等于 len
,那么会将 len
个字符复制到 dst
中。
char s1[] = "Hello, World!";
char s2[] = "Hello, Computer";
strncpy(s1, s2, 10); // Hello, Comld!
如果 src
的长度小于 len
,dst
就用额外的 ''
填充到 len
长度中。
char s1[] = "Hello, World!";
char s2[] = "Hello, Computer";
strncpy(s1, s2, 20); // Hello, Computer
strncpy
不会向结果中追加 ''
结束标记。
char s1[] = "Hello, World!";
char s2[] = "Hello, Computer";
strncpy(s1, s2, 20); // Hello, ComputeHello, World!
因此,使用 strncpy
的时候,最好把 src
中的 ''
追加到 dst
中。
strcat
当一个字符串连接到另一个字符串后面时,库函数提供了 strcat
函数来完成,函数原型如下所示。
char *strcat(char *dst, char const *src);
strcat
函数要求 dst
参数有值。如果 src
和 dst
的位置发生重叠,其结果是未定义的。
char s1[] = "Hello, World!";
char s2[] = "The Computer";
strcat(s1, s2); // Hello, World!The Computer
必须保证 dst
在刨除原先存储字符数组所占的空间后,有足够的剩余空间保存 src
整个字符串。
strncat
如果想设置追加到字符数组后的个数,库函数还提供了 strncat
函数来设置长度,函数原型如下所示。
char *strncat(char *dst, char const *src, size_t len);
strncpy
会将 src
的 len
长度的字符串追加到 dst
后面。
char s1[] = "Hello, World!";
char s2[] = "The Computer";
strncat(s1, s2, 9); // Hello, World!Hello, Co
strncat
总是会在结果字符串后面添加 ''
结束符。strncat
最多向目标数组复制 len
个字符,再加上 ''
结束符,不会管目标数组中的空间够不够。
strcmp
如果想比较两个字符串,可以使用库函数提供的 strcmp
函数,函数原型如下所示。
int strcmp(char const *s1, char const *s2);
该比较方式是对两个字符串逐个字符进行比较,直到发现不匹配为止。当 s1 < s2
,返回负值;当 s1 == s2
,返回零值;当 s1 > s2
,返回正值;当 s1
是 s2
的一部分,认为 s1 < s2
,反之亦然。
char s1[] = "Hello, World!";
char s2[] = "Hello, The Computer";
strcmp(s1, s2); // 1
并未规定用于提示不相等的具体值。只是返回大于零的值或零或小于零的值,并不一定是
1
和-1
。
strncmp
strcmp
是比较两个字符串,但没有长度限制,库函数还提供了 strncmp
函数来限制两个字符串比较的长度,函数原型如下所示。
int strncmp(char const *s1, char const *s2, size_t len);
strncmp
最多比较 len
个字符。如果两个字符串在 len
字符之前存在不相等的字符时,函数会停止并返回结果。如果两个字符串在前 len
个字符相等,函数返回零。
char s1[] = "Hello, World!";
char s2[] = "Hello, The Computer";
strncmp(s1, s2, 5); // 0
strchr
当查找字符串中某个字符第一次出现的位置时,可以使用 strchr
函数,函数原型如下所示。
char *strchr(char const *str, int ch);
strchr
查找字符串 str
中第一次出现 ch
的位置,找到后函数返回一个指向该位置的指针。如果不存在,就返回一个 NULL
指针。
char s1[] = "Hello, World!";
char *ret = strchr(s1, 'o');
printf("o 之后的字符串是:%s", ret); // o 之后的字符串是:o, World!
strrchr
当查找字符串中某个字符最后一次出现的位置时,可以使用 strrchr
函数,函数原型如下所示。
char *strrchr(char const *str, int ch);
strrchr
是查找字符串 str
中最后一次出现 ch
的位置,找到后函数返回一个指向该位置的指针。如果不存在,就返回一个 NULL
指针。
char s1[] = "Hello, World!";
char *ret = strrchr(s1, 'o');
printf("o 之后的字符串是:%s", ret); // o 之后的字符串是:orld!
strpbrk
C 语言库函数提供了查找任意几个字符在字符串中第 1 次出现的位置,函数原型如下所示。
char *strpbrk(char const *str, char const *group);
这个函数返回一个指向 str
中第 1 个匹配 group
中任意一个字符的字符位置。如果未匹配到,函数返回一个 NULL
指针。
char s1[] = "Hello, World!";
char s2[] = "Computer";
char *ret = strpbrk(s1, s2);
printf("%sn", ret); // ello, World!
如上所示,s1
中的字符 e
是第 1 个匹配 s2
字符数组中的字符,函数返回的是 s1
中字符 e
的位置。
strstr
如果要在字符串中查找一个子串,库函数也提供了 strstr
函数,函数原型如下所示:
char *strstr(char const *s1, char const *s2);
这个函数是查找整个 s2
在 s1
中第 1 次出现的起始位置,并返回一个指向该位置的指针。如果未找到,将返回一个 NULL
指针。
char s1[] = "Hello, World!";
char s2[] = "llo, W";
char *ret = strstr(s1, s2);
printf("%sn", ret); // llo, World!
当第二个参数是空字符串,函数会返回 s1
。
char s1[] = "Hello, World!";
char s2[] = "";
char *ret = strstr(s1, s2);
printf("%sn", ret); // Hello, World!
strspn
库函数提供了 strspn
函数用于在字符串的起始位置计算与另一个字符串中任意字符相匹配的个数,函数原型如下所示:
size_t strspn(char const *str, char const *group);
该函数返回 str
起始部分匹配 group
中任意字符的字符数。
char s1[] = "Hello, World!";
char s2[] = "Computer";
int len = strspn(s1, s2);
printf("%dn", len); // 0
由上可知,printf
打印出来的结果是 0
,这是因为 s1
中第一个字符就与 s2
中任意一个字符不匹配。
char s1[] = "Hello, World!";
char s2[] = "Homputer";
int len = strspn(s1, s2);
printf("%dn", len); // 2
当把 s2
中第 1 个字符改为 H
后,printf
打印出来的结果是 2
,这是因为 s1
中第 1 个字符与 s2
中产生匹配,一直到 l
,在 s2
中找不到匹配字符,就返回已经匹配上的结果,前缀总共匹配上 2
个字符。
strcspn
而 strcspn
函数与 strspn
正好相反,它是在字符串的起始位置计算与另一个字符串中任意字符不匹配的个数,函数原型如下所示。
size_t strcspn(char const *str, char const *group);
该函数返回 str
起始部分不匹配 group
中任意字符的字符数。
char s1[] = "Hello, World!";
char s2[] = "Computer";
int len = strcspn(s1, s2);
printf("%dn", len); // 1
printf
打印出来的结果是 1
,这是因为 s1
的第 1 个字符 H
在 s2
中不存在,而第 2 个字符 e
在 s2
中能找到匹配的字符,因此返回的结果为 1
。
如果使用 "nrftv”
作为 group
参数,该函数将返回 str
中的起始部分所有非空白字符的值。
char s1[] = "Hello, World!";
char s2[] = "nrftv";
int len = strcspn(s1, s2);
printf("%dn", len); // 13
strtok
当想要对字符串进行分割的时候,可以使用库函数提供的 strtok
函数,函数原型如下所示:
char *strtok(char *str, char const *sep);
这个函数会通过传入的 sep
参数作为分隔符,对传入的 str
字符串找到下一个分隔符,并将其用 ''
结尾,然后返回一个指向这个分割后的字符串指针,如果无法分割,就返回 NULL
指针。
char s[] = "Desire causes suffering because it can never be completely gratified.";
char *token;
for (token = strtok(s, " "); token != NULL ;token = strtok(NULL, " ")) {
printf("%sn", token);
}
/*
Desire
causes
suffering
because
it
can
never
be
completely
gratified.
*/
如上所示,strtok
函数的第 1 个参数不是 NULL
时,函数找到分隔符,并且 strtok
同时将保存它在字符串中的位置;strtok
函数的第 1 个参数是 NULL
,函数就在同一个字符串中从这个被保存的位置开始查找下一个分隔符;当再也找不到分隔符后,strtok
函数返回一个 NULL
指针结束分割操作。
strtok
会修改它所处理的字符串,如果想原字符串不被修改,那就复制一份,在使用strtok
函数。strtok
还会保存局部状态信息,不能同时分割两个字符串。

原文始发于微信公众号(海人为记):一文讲解C语言字符串
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/27471.html