注意:大部分字符串的库函数在使用前都需要加入头文件#include<string.h>
目录
5.strchr函数与strrchr函数以及strstr函数
拓展:memset函数,memmove,memcmp,memcpy函数
1.strlen函数
‘strlen’
函数用来计算字符串的长度,即字符串中字符的数量,不包括结尾的空字符 \0
。它定义在 <string.h>
头文件中。
#include <string.h>
int main() {
char str[] = "hello";
int len = strlen(str);
printf("Length of the string is: %d\n", len);
return 0;
}
上面的代码将输出字符串 "hello" 的长度,即 5。
注意事项
- 确保字符串以空字符
\0
结尾:strlen
函数依赖于\0
来判断字符串何时结束。如果字符串没有正确结束,strlen
可能会继续计算到后续的内存中,直到遇到\0
,这可能导致未定义行为(如越界访问)。 - 不检查 NULL 指针:如果传递给
strlen
的是 NULL 指针,程序将崩溃。确保在调用strlen
之前字符串指针有效。 - 与sizeof函数的区别:对于字符数组,
sizeof
返回的是整个数组的大小,包括结尾的\0
。sizeof
返回的大小与数据类型有关,与存储的具体内容无关。strlen
计算字符串的实际长度,不包括\0
。sizeof
计算数据类型或变量所占的内存大小,对于字符串,它包括\0
。
sizeof操作符
#include <stdio.h>
int main() {
char str[] = "hello";
int size = sizeof(str);
printf("Size of the array is: %d\n", size);
return 0;
}
这段代码将输出 6,因为 "hello" 包括结尾的空字符,共有 6 个字符,每个字符占一个字节。
2.strcpy函数与strncpy函数
strcpy 函数
strcpy
函数用来将一个字符串复制到另一个字符串。它的声明如下,位于 <string.h>
头文件中:
char *strcpy(char *dest, const char *src);
示例
#include <stdio.h>
#include <string.h>
int main() {
char src[20] = "Hello, world!";
char dest[20];
strcpy(dest, src);
printf("Copied string: %s\n", dest);
return 0;
}
这段代码将src复制到dest中,输出将是 "Hello, world!"。这种复制类似于覆盖,将src字符串中的所有内容覆盖到dest字符串中
注意事项
- 缓冲区溢出:如果目标数组
dest
的大小不足以容纳源字符串src
(包括结尾的空字符\0
),则会发生缓冲区溢出,这可能导致运行时错误和安全漏洞。 - 确保足够的空间:在使用
strcpy
前,确保目标字符串有足够的空间存放源字符串。 - 源字符串必须以 null 结尾:
src
必须是以 null 结尾的有效 C 字符串。
strncpy 函数
strncpy
函数也用于复制字符串,但它允许指定一个最大复制长度,这有助于防止目标缓冲区溢出。它可以把src前n字符的内容复制到dest中。
我们用例子来进行理解
char *strncpy(char *dest, const char *src, size_t n);
使用方法和示例
#include <stdio.h>
#include <string.h>
int main() {
char src[20] = "Hello, world!";
char dest[20];
strncpy(dest, src, sizeof(dest));
dest[sizeof(dest) - 1] = '\0'; // 确保有空字符结尾
printf("Copied string: %s\n", dest);
return 0;
}
这段代码安全地从 src
复制字符串到 dest
,并确保不会溢出并且字符串以空字符结尾。
注意事项
- 可能不自动添加 null 字符:如果
src
的长度大于或等于n
,strncpy
不会在dest
的末尾自动添加 null 字符,因此需要手动处理。 - 性能注意:如果
src
的长度小于n
,strncpy
会继续在dest
中填充 null 字符,直到达到n
字符,这可能会略微降低效率。 - 适用于固定大小的字符数组:
strncpy
是在处理具有固定大小的字符数组时的理想选择,特别是在网络编程和硬件接口编程中,其中字符串大小固定是常见的。
3.strcat与strncat函数
strcat(用于字符串连接)
strcat
函数将一个字符串追加到另一个字符串的末尾
1.确保 `str1` 有足够的空间:`str1` 必须有足够的空间来存放合并后的字符串,包括 `str2` 的内容和字符串结束符 `\0`。
2. 使用 `strcat` 函数:`strcat` 会自动查找 `str1` 的字符串结束符 `\0`,然后从那里开始复制 `str2` 的内容。
char *strcat(char *dest, const char *src);
例子:
#include <stdio.h>
#include <string.h>
int main() {
char dest[20] = "Hello";
char src[] = ", world!";
strcat(dest, src);
printf("Resulting String: %s\n", dest); // 输出 "Hello, world!"
return 0;
}
这段代码将 src
字符串追加到 dest
字符串后面。
注意事项
- 缓冲区溢出:如果目标数组
dest
的空间不足以容纳两个字符串的合并结果,就会发生缓冲区溢出。这可能导致运行时错误和安全漏洞。 - 确保目标缓冲区足够大:在使用
strcat
前,要确保目标字符串有足够的空间来追加源字符串。
strncat函数
strncat
函数也用于将一个字符串追加到另一个字符串的末尾,但它允许指定最大追加长度。
char *strncat(char *dest, const char *src, size_t n);
‘n’也就是将src的前n个字符拼接到dest的末尾
示例
#include <stdio.h>
#include <string.h>
int main() {
char dest[20] = "Hello";
char src[] = ", world!";
strncat(dest, src, 7); // 只追加 ", world" 中的前 7 个字符
printf("Resulting String: %s\n", dest); // 输出 "Hello, world"
return 0;
}
这段代码安全地将 src
字符串的前 7 个字符追加到 dest
字符串后面。
注意事项
- 处理结尾 null 字符:
strncat
会在最后自动添加一个 null 字符,但必须确保n
加上dest
原始长度不超过dest
的总大小。 - 性能注意:与
strncpy
类似,如果src
长度小于n
,则追加操作会在遇到src
的 null 字符时停止。
4.strcmp函数与strncmp函数
strcmp 函数
strcmp
函数比较两个字符串,直到找到一个不同的字符或遇到字符串的结尾。
int strcmp(const char *s1, const char *s2);
示例
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "hello";
char str2[] = "hello";
char str3[] = "world";
int result1 = strcmp(str1, str2); // 比较相同的字符串
int result2 = strcmp(str1, str3); // 比较不同的字符串
printf("Comparing 'hello' and 'hello': %d\n", result1); // 输出 0
printf("Comparing 'hello' and 'world': %d\n", result2); // 输出一个负数
return 0;
}
返回值
- 0:如果
s1
和s2
相等,则返回0。 - 负数:如果
s1
在字典顺序上小于s2
。 - 正数:如果
s1
在字典顺序上大于s2
。 - (字典顺序也就是你所认为的abcdefg....上述str1与str3比较过程中,其首字符h在字符顺序上便小于str3的w)
strncmp 函数
strncmp
函数比较两个字符串的前 n
个字符,或直到第一个 null 字符,以最先到达的为准。
声明
int strncmp(const char *s1, const char *s2, size_t n);
使用方法和示例
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "hello";
char str2[] = "hello world";
int result = strncmp(str1, str2, 5); // 只比较前5个字符
printf("Comparing first 5 characters of 'hello' and 'hello world': %d\n", result); // 输出 0
return 0;
}
这段代码比较了两个字符串的前五个字符。
需要注意 n
的值不要超过任一字符串的实际长度,尤其是当字符串可能不是以 null 字符终止时。
5.strchr函数与strrchr函数以及strstr函数
strchr 函数
strchr
用于在字符串中从前往后搜索第一次出现的指定字符。
声明
char *strchr(const char *s, int c);
示例
#include <stdio.h>
#include <string.h>
int main() {
const char str[] = "Hello, world!";
char ch = 'w';
char *p = strchr(str, ch);
if (p != NULL) {
printf("Character '%c' found at position: %ld\n", ch, p - str);
} else {
printf("Character '%c' not found.\n", ch);
}
return 0;
}
这段代码查找字符 'w' 在字符串中的位置。
返回值
- 非NULL指针:使用strchr时将返回一个非null'指针,其指向字符串中第一次出现字符
c
的位置。 - NULL:如果在字符串中未找到字符,返回NULL,即0;
注意事项
- 如果
c
是\0
,strchr
将返回指向字符串终结符的指针。 strchr
提供从字符串开始到结尾的搜索,不适用于从字符串结尾反向搜索。
strrchr 函数
strrchr
用于在字符串中从后往前(即从右向左)搜索指定字符的最后一次出现。
其返回类型与strchr一样,大致特点都差不多,只不过strrchr从字符串最后开始读取。
strrchr与strchr唯一区别就是从后往前!
strstr函数
这个函数是用来定位一个字符串(needle
)在另一个字符串(haystack
)中第一次出现的位置,并返回从这个位置到字符串结束的所有字符。
函数声明
char *strstr(const char *haystack, const char *needle);
-
参数:
haystack
:指向被搜索的原始字符串的指针。needle
:指向需要查找的子字符串的指针。
-
返回值:
- 如果
needle
是一个空字符串,strstr
会返回haystack
的起始地址。 - 如果
needle
在haystack
中找到了,返回一个指向haystack
中第一次出现needle
的位置的指针。 - 如果未找到,返回
NULL
- 如果
使用方法和示例
#include <stdio.h>
#include <string.h>
int main() {
const char *text = "Here is a simple example.";
const char *search = "simple";
char *result = strstr(text, search);
if (result) {
printf("Found '%s' in \"%s\" at position %ld\n", search, text, result - text);
} else {
printf("The string '%s' was not found in \"%s\"\n", search, text);
}
return 0;
}
注意事项
-
处理返回值:
- 当处理
strstr
的返回值时,总是检查它是否为NULL
,以避免在不存在子字符串的情况下使用无效的指针。
- 当处理
-
空字符串作为
needle
:- 如果
needle
是空字符串,strstr
将返回haystack
的起始地址。这是因为空字符串被视为存在于任何字符串的开头.
- 如果
6.strcspn函数
strcspn
函数是 C 语言中的一个字符串处理函数,用于计算两个字符串中第一个匹配字符之前的字符数量。它定义在 <string.h>
头文件中,并提供一种方式来测量一个字符串开头的一部分在另一个字符串中没有出现字符的长度。‘strcspn’
函数提供了一种方便的方式来确定一个字符串中第一个不希望出现的字符集中的字符出现的位置。
函数声明
size_t strcspn(const char *s1, const char *s2);
-
参数:
s1
:指向要检测的主字符串的指针。s2
:包含不应出现在s1
中的字符集的字符串。
-
返回值:
- 返回从
s1
的开头开始,直到遇到任何在s2
中出现的字符为止的字符数。如果s1
开头的字符在s2
中完全没有出现,则返回s1
的总长度。
- 返回从
示例
#include <stdio.h>
#include <string.h>
int main() {
const char *s1 = "The quick brown fox";
const char *s2 = "aeiou"; // 查找第一个元音字母出现的位置
size_t pos = strcspn(s1, s2);
printf("The first vowel in '%s' appears at position: %zu\n", s1, pos);
return 0;
}
在以上示例中:
-
字符串定义:
s1
是我们要检查的主字符串:"The quick brown fox"。s2
是包含字符集的字符串:"aeiou",这里我们想要找出s1
中第一个出现的任何元音字母。
-
函数调用:
strcspn(s1, s2)
这个函数调用的作用是测量s1
的开头起多少个字符不包含s2
中的任何字符。换句话说,它查找s1
中第一个匹配s2
中任意字符的位置。
-
函数行为:
strcspn
函数会逐个检查s1
中的每个字符,看它是否出现在s2
中。一旦找到匹配(即s1
中的字符出现在s2
中),函数停止搜索,并返回到目前为止检查的字符数量。- 在这个例子中,
s1
的第一个字符是 'T',不是元音。它会继续检查 'h', 'e', ' ', 'q',直到遇到 'u'。'u' 是第一个在 "aeiou" 中找到的匹配字符。
-
输出:
printf
语句用于输出结果。pos
的值是 'u' 出现的位置,即 10(从 0 开始计数)。因此,输出将是 "The first vowel in 'The quick brown fox' appears at position: 10"。
注意点
- 位置计数:在 C 语言中,字符串的位置是从 0 开始计数的。所以,第一个字符的位置是 0,第二个字符的位置是 1,以此类推。
- 返回值:如果
s1
中没有任何字符在s2
中出现,strcspn
会返回s1
的整体长度。
拓展:memset函数,memmove,memcmp,memcpy函数
1.memset
memset
是 C 语言中用于设置内存块内容的函数。它可以将指定的内存区域设置为给定的值,通常用于初始化数据结构或清除内存中的数据。memset
函数定义在 <string.h>
头文件中。
memset
函数可以用于初始化结构体、字符串和数组等多种类型的数据结构,但使用时需要特别注意其适用条件和潜在的局限性。(只含有 POD 类型的结构体,可以使用 memset
将其初始化为零或某种特定的字节模式。)
函数声明
void *memset(void *s, int c, size_t n);
-
参数:
s
:指向要填充的内存块的指针。c
:要设置的值,虽然这个参数的类型是int
,但只有低八位(一个字节)会被用来填充内存。n
:要设置的字节数。
-
返回值:
- 返回指向内存块
s
的指针。
- 返回指向内存块
示例
#include <stdio.h>
#include <string.h>
int main() {
char str[50] = "Hello, world!";
// 将前 5 个字符设置为 'a'
memset(str, 'a', 5);
printf("Modified string: %s\n", str);
return 0;
}
这个例子中,我们将字符串 str
的前 5 个字符全部设置为字符 'a'。输出将是 "aaaaa, world!"。
注意事项
-
数据类型转换:
- 虽然
c
参数类型为int
,但只有该整数的低 8 位(即一个字节)被用于填充内存。因此,如果传递的值大于 255,它会被截断到 255。
- 虽然
-
使用场景:
memset
通常用于初始化内存区块或数组。例如,可以在动态分配内存后使用memset
将其初始化为零或其他值。
-
性能考虑:
memset
是高度优化的,通常能快速地填充大块内存。不过,当需要初始化复杂的数据结构时(比如含有非平凡构造函数的类对象),应避免使用memset
,因为它可能会破坏对象状态。
-
安全性问题:
- 使用
memset
时需要确保不会超出目标内存块的界限,以避免缓冲区溢出等安全问题。确保n
的值不会超过分配给s
的内存大小。
- 使用
memset
是一个强大的工具,适用于多种初始化和清除内存的场景。正确使用时,它可以帮助确保数据结构的一致性和程序的稳定运行。
2.memcmp函数与memcpy函数
这两个函数与strcmp和strcpy函数的功能比较类似,可以看到它们后三位数都是一样的。
memcmp函数
memcmp
函数用于比较两块内存区域的内容,通常用于比较任意类型的数据(如结构体、数组等),而不仅限于字符串。它定义在 <string.h>
头文件中。
int memcmp(const void *s1, const void *s2, size_t n);
-
参数:
s1
:指向第一块内存区域的指针。s2
:指向第二块内存区域的指针。n
:要比较的字节数。
-
返回值:
- 如果相等,返回
0
。 - 如果
s1
小于s2
,返回小于0
的值。(字典顺序) - 如果
s1
大于s2
,返回大于0
的值。(字典顺序)
- 如果相等,返回
示例
#include <stdio.h>
#include <string.h>
int main() {
char buf1[10] = "abc";
char buf2[10] = "abd";
int result = memcmp(buf1, buf2, 3);
if (result == 0) {
printf("The buffers are equal.\n");
} else {
printf("The buffers are not equal.\n");
}
return 0;
}
在这个示例中,memcmp
比较 buf1
和 buf2
的前三个字符。因为第三个字符不同(d的字典顺序大于c),函数将返回一个非零值。
memcpy
函数
memcpy
用于从源内存地址复制 n 个字节到目标内存地址,常用于拷贝各种类型的数据,如数组或结构体。
void *memcpy(void *dest, const void *src, size_t n);
-
参数:
dest
:目标内存地址的指针。src
:源内存地址的指针。n
:要复制的字节数。
-
返回值:
- 返回指向
dest
的指针。
- 返回指向
示例
#include <stdio.h>
#include <string.h>
int main() {
char src[50] = "Hello World!";
char dest[50];
memcpy(dest, src, sizeof(src));
printf("Copied string: %s\n", dest);
return 0;
}
这个示例中,memcpy
将 src
中的数据(包括字符串和后面的空字节)复制到 dest
中。
Tips:区别与 strcpy
和 strcmp
-
memcpy
vsstrcpy
:memcpy
是按字节拷贝数据,不会检查数据的类型和内容,只按照指定的字节数从源复制到目标。它也不关心 null 字符。strcpy
专门用于复制字符串,会一直复制直到源字符串的终止 null 字符,但不需要指定字节数。
-
memcmp
vsstrcmp
:memcmp
用于比较两块内存区域,可以比较任何类型的数据,并根据提供的字节数进行比较。它只比较指定的字节数。strcmp
专门用于比较字符串,比较过程会一直进行,直到遇到字符串的终止 null 字符。
3.memmove函数
memmove
是 C 语言标准库中的一个函数,用于从源内存地址复制字节到目标内存地址。它与 memcpy
类似,但是 memmove
的主要特点是它能够处理源地址和目标地址重叠的情况。这使得 memmove
在特定情况下更安全可靠,特别是在移动内存块时。
函数声明
void *memmove(void *dest, const void *src, size_t n);
-
参数:
dest
:指向目标内存位置的指针。src
:指向源内存位置的指针。n
:要复制的字节数。
-
返回值:
- 返回指向目标内存 (
dest
) 的指针。
- 返回指向目标内存 (
使用方法和示例
memmove
可以用于数组、结构体和任何类型的数据复制,与memcpy
函数不同的是,memmove
保证了即使源区域和目标区域有重叠,复制操作也能正确地执行。如果源和目标内存区域没有重叠,memmove
和memcpy
的行为是相同的。包括处理可能重叠的内存区域。以下是一个使用 memmove
的示例。
使用注意事项:
memmove
函数不检查目标数组是否能够存下要复制的数据,如果目标区域不够大,可能会导致缓冲区溢出和数据损坏。- 因为
memmove
能够处理内存重叠的情况,所以在有重叠的内存区域复制时应当总是使用memmove
而不是memcpy
。 memmove
不会对传入的指针进行NULL检查,所以调用之前确保指针有效是很重要的。
一个简单的memmove
示例代码:
示例:
假设我们有一个字符数组,并且我们想要将数组的一部分向前或向后移动几个位置。
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "hello world";
// 造成内存重叠的情况, 将字符串的开始部分复制到结束部分
// 在这里,我们将 "hello" 复制到 "world" 之后,即 str[6] 开始的位置
memmove(str + 6, str, 5);
// 此时预期输出为 "hello hello"
printf("%s\n", str);
return 0;
}
目标
在这个例子中,我们有一个字符串"hello world"
,使用memmove
函数把"hello"
这部分复制到"world"
后面。由于memmove
能够处理重叠,所以即使"hello"
和"world"
在内存中是重叠的,也可以正确地执行复制。最终str
数组中的内容就会变为"hello hello"
memmove
函数调用
// 造成内存重叠的情况, 将字符串的开始部分复制到结束部分
// 在这里,我们将 "hello" 复制到 "world" 之后,即 str[6] 开始的位置
memmove(str + 6, str, 5);
memmove
函数在这里被调用以移动内存块。函数的第一个参数 str + 6
是目标地址,意味着复制操作将开始于数组 str
第七个元素的位置(因为数组索引是从0开始的)。第二个参数 str
是源地址,表示复制将从数组 str
的第一个元素即字符串的开头开始。第三个参数 5
指定复制的字节数,正好对应 "hello"
字符串的长度。
由于我们是从数组自身复制到数组内较高的索引位置,这里存在内存重叠。memmove
是为了处理这种内存重叠情况而设计的,所以它能够正确地处理这个复制。
结果
// 此时预期输出为 "hello hello"
printf("%s\n", str);
调用 printf
函数打印 str
数组。此时数组 str
的内容应该是 "hello hello"
,因为原来位置 str[6]
到 str[10]
的 "world"
部分被 "hello"
覆盖了。
因为memmove的特性,我们还可以利用memove函数来进行对字符串等数据的覆盖删除(字符串较长时效率往往不高)
示例:
#include <stdio.h>
#include <string.h>
int main()
{
char Str[100] = "ibbiiibbibib";
char *Found;
int i = 0;
while ((Found = strchr(Str, 'b')) != NULL)
{
memmove(Found, Found + 1, strlen(Found + 1) + 1);//memmove函数实现删除
i++;
}
printf("%s\n", Str);
printf("%d", i);
return 0;
}
这段代码利用 strchr
和 memmove
函数来删除字符串 Str
中所有的 'b' 字符,并统计删除的总数。让我们一步步分析这个代码的工作流程和执行结果。
Str
初始化为字符串 "ibbiiibbibib"。Found
是一个字符指针,用来存放strchr
找到的字符位置。i
用来计数,记录删除了多少个 'b' 字符。
循环处理
while
循环使用strchr
函数寻找Str
中第一个出现的 'b' 字符。如果找到(即Found
不为NULL
),则进入循环体。memmove
函数用来删除找到的 'b' 字符。它通过将Found + 1
(即 'b' 后面的字符串)向前移动到Found
的位置实现,同时移动的长度是从Found + 1
到字符串末尾的长度加上字符串结束符\0
。i++
操作每次循环递增,记录删除字符的次数此代码可以正确统计并删除所有指定字符,但对于较大数据或更复杂的字符处理任务,可能需要更高效或安全的方法。
4.strtok函数
strtok
函数简介
strtok
是 C 语言标准库中的一个函数,用于将字符串分割成一系列的标记(token)。这个函数可以通过指定一个或多个分割字符(称为分隔符)来切割原始字符串。
函数声明
char *strtok(char *str, const char *delim);
-
参数:
str
:指向要分割的原始字符串的指针。第一次调用strtok
时,str
应指向要分割的字符串。在随后的调用中,str
应为NULL
,表示继续分割前一个字符串。delim
:包含所有分隔符的字符串。这些字符中的任意一个都可用作分割原始字符串的分隔符。
-
返回值:
- 返回指向当前标记的指针,如果没有更多标记,则返回
NULL
。
- 返回指向当前标记的指针,如果没有更多标记,则返回
使用注意事项
strtok
会修改原始字符串,它通过在每个分隔符位置插入'\0'
字符来“切断”字符串。- 在多线程环境中,使用
strtok
是不安全的,因为它使用静态缓冲区存储数据。在这种情况下,建议使用strtok_r
,这是一个可重入版本。 - 为了避免对原始数据的修改,建议在使用
strtok
之前复制原始字符串。
示例:使用 strtok
分割字符串
以下是一个程序示例,展示如何使用 strtok
将字符串分割成单词。
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello, world! This is a test string.";
char delim[] = " ,.!"; // 分隔符包括空格、逗号和感叹号
char *token;
// 获取第一个标记
token = strtok(str, delim);
// 继续获取其他标记
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, delim); // 注意此处传入 NULL
}
return 0;
}
- 这个程序首先定义了一个字符串
str
和一个包含分隔符的字符串delim
。 - 使用
strtok(str, delim)
获取第一个标记。 - 通过一个循环,使用
strtok(NULL, delim)
获取并打印出所有剩余的标记。每次调用strtok
时,如果找到了标记,它就会返回标记的指针,否则返回NULL
。 - 在输出中,你会看到原始字符串被分割成了单词,每个单词都是按照定义的分隔符分开的。
通过这种方式,strtok
可以有效地将字符串按指定的分隔符分割成多个单词或标记,这对于处理格式化的文本数据非常有用。
未完待续...