目录
1 strncat
1.1 函数原型
strncat 函数是 C 语言标准库中的一个函数,用于将一个字符串(源字符串)的前 n 个字符追加到另一个字符串(目标字符串)的末尾。这个函数定义在 <string.h> 头文件中。其原型如下:
#include <string.h>
char *strncat(char *dest, const char *src, size_t n);
- 参数:
- dest:指向目标字符串的指针,这个字符串必须足够大,以容纳原有的字符以及要追加的字符和一个额外的空字符(\0)作为结束符。
- src:指向源字符串的指针,从这个字符串中拷贝字符到目标字符串。
- n:指定从源字符串中拷贝的最大字符数(不包括空字符)。
- 返回值:返回目标字符串的指针。
1.2 功能说明
strncat 会将源字符串 src 的前 n 个字符(不包括空字符)追加到目标字符串 dest 的末尾。strncat 会先在目标字符串 dest 的末尾找到终止符 '\0',然后从该位置开始追加源字符串 src 的前 n 个字符(不包括空字符)。追加完成后,strncat 会在新的末尾位置添加一个终止符 '\0',确保目标字符串仍然是一个有效的 C 字符串。如果源字符串的长度小于 n,则只追加实际存在的字符。
1.3 案例演示
#include <stdio.h>
#include <string.h>
int main()
{
char dest[20] = "Hello, ";
const char src[] = "World! I won't be appended";
// 使用 strncat 追加 src 的前 5 个字符到 dest
strncat(dest, src, 5); // 注意:这里只追加前 5 个字符,即 "World"
printf("拼接后的字符串是: %s\n", dest);
// 输出: 拼接后的字符串是: Hello, World
char dest2[20] = "Hello, ";
const char src2[] = "World! I";
// 使用 strncat 追加 src 的 100 个字符到 dest
// 如果源字符串 src 的长度小于 n,则只追加实际存在的字符
strncat(dest2, src2, 100); // 注意:这里只追加实际存在的字符,即 "World! I"
printf("拼接后的字符串是: %s\n", dest2);
// 输出: 拼接后的字符串是: Hello, World
return 0;
}
输出结果如下所示:
字符串 dest 初始化完成后的存储情况,如下所示:
使用 strncat 追加字符串后,字符串 dest 的内容,如下所示:
字符串 dest2 初始化完成后的存储情况,如下所示:
使用 strncat 追加字符串后,字符串 dest 的内容,如下所示:
1.4 注意事项
缓冲区溢出:确保目标字符串 dest 有足够的空间来存放要追加的字符以及原有的字符和一个空字符。否则,strncat 可能会导致缓冲区溢出,这是常见的安全问题。
目标字符串的结束符:strncat 假定目标字符串 dest 在调用前已经是一个以空字符结尾的字符串。如果 dest 没有以空字符结尾,结果将不可预测。
追加字符数:strncat 会追加最多 n 个字符,但如果源字符串 src 的长度小于 n,则只会追加实际存在的字符数。
性能:strncat 会在内部遍历目标字符串以找到其末尾的空字符,这可能会影响性能,尤其是在处理非常长的字符串时。如果需要频繁追加字符串,考虑使用其他数据结构(如动态字符串或链表)可能更高效。
兼容性:strncat 是 C 标准库的一部分,在大多数现代 C 编译器和环境中都可用。然而,对于非常旧的或特殊的系统,最好检查其可用性和行为。
2 strncpy
2.1 函数原型
strncpy 函数是 C 语言标准库中的一个函数,用于将源字符串的前 n 个字符复制到目标字符串中。这个函数定义在 <string.h> 头文件中。其原型如下:
#include <string.h>
char *strncpy(char *dest, const char *src, size_t n);
- 参数:
- dest:指向目标字符串的指针,它应该有足够的空间来存储 n 个字符。
- src:指向源字符串的指针。
- n:指定最多从源字符串中复制的字符数。
- 返回值:返回目标字符串的指针。
2.2 功能说明
strncpy 函数会将源字符串 src 的前 n 个字符(或直到遇到空字符,以先到者为准)复制到目标字符串 dest 中。
- 如果源字符串 src 的长度小于 n,strncpy 会复制整个源字符串,包括其终止符 '\0',然后用额外的 '\0' 填充目标字符串 dest 的剩余部分,直到总共复制了 n 个字符。
- 如果源字符串 src 的长度(包括 '\0')等于 n,strncpy 会复制整个源字符串,包括其终止符 '\0'。
- 如果源字符串 src 的长度大于 n,strncpy 会只复制前 n 个字符,不会包括源字符串的终止符 '\0',这意味着如果程序员不手动添加终止符,dest 可能不会是一个以空字符结尾的有效字符串。
2.3 案例演示
#include <stdio.h> // 引入标准输入输出库
#include <string.h> // 引入字符串处理库,其中包含 strncpy 函数
int main()
{
// 情况 1: 源字符串长度小于 n
// strncpy 会复制整个源字符串,包括其终止符 '\0'
// 然后用额外的 '\0' 填充目标字符串 dest 的剩余部分
// 直到总共复制了 n 个字符
char src1[] = "Hello";
char dest1[10];
// 方便调试,使用 memset 将数组的所有元素初始化为 't'
memset(dest1, 't', sizeof(dest1));
// 使用 strncpy 将 src1 的前 10 个字符复制到 dest1
// 源字符串没这么长,就用 '\0' 填充
strncpy(dest1, src1, 10);
// 也可以手动确保 dest1 是一个有效的 C 字符串,但没必要
// dest2[9] = '\0';
// 打印结果
printf("情况 1: 源字符串长度小于 10\n");
printf("源字符串: %s\n", src1);
printf("复制后的目标字符串: %s\n", dest1);
// 情况 2: 源字符串长度等于 n(包括'\0')
// strncpy 会复制整个源字符串,包括其终止符 '\0'
char src2[] = "Hello";
char dest2[10];
// 方便调试,使用 memset 将数组的所有元素初始化为 't'
memset(dest2, 't', sizeof(dest2));
// 使用 strncpy 将 src2 的前 6 个字符复制到 dest2
// 长度刚刚好,
strncpy(dest2, src2, 6);
// 也可以手动确保 dest2 是一个有效的 C 字符串,但没必要
// dest2[5] = '\0';
// 打印结果
printf("\n情况 2: 源字符串长度等于 6\n");
printf("源字符串: %s\n", src2);
printf("复制后的目标字符串: %s\n", dest2);
// 情况 3: 源字符串长度大于 n
// strncpy 会只复制前 n 个字符,不会包括源字符串的终止符 '\0'
// 需要手动添加终止符 '\0'
char src3[] = "Hello, World!";
char dest3[10];
// 方便调试,使用 memset 将数组的所有元素初始化为 't'
memset(dest3, 't', sizeof(dest3));
// 使用 strncpy 将 src3 的前 10 个字符复制到 dest3
strncpy(dest3, src3, 10);
// 必须手动确保 dest3 是一个有效的 C 字符串
dest3[9] = '\0';
// 打印结果
printf("\n情况 3: 源字符串长度大于 10\n");
printf("源字符串: %s\n", src3);
// printf("复制后的目标字符串(没有设置结束符,%%s会一直去找结束符才结束): %s\n", dest3);
printf("复制后的目标字符串(最后一个字符已手动设置成了结束符): %s\n", dest3);
return 0;
}
输出结果如下所示:
在 C 语言中,%s 格式说明符用于 printf 函数来打印字符串。字符串是以空字符 '\0' 结束的字符数组。如果目标字符串中没有设置结束符 '\0',printf 函数会继续读取内存,直到找到一个 '\0' 才停止。这可能导致未定义行为,包括读取超出预期范围的内存,甚至程序崩溃。
字符串 src1、dest1 初始化完成后的存储情况,如下所示:
执行 strncpy(dest1, src1, 10); 后 desc1 中的值,如下所示:
字符串 src2、dest2 初始化完成后的存储情况,如下所示:
执行 strncpy(dest2, src2, 6); 后 desc2 中的值,如下所示:
字符串 src3、dest3 初始化完成后的存储情况,如下所示:
执行 strncpy(dest3, src3, 10); 后 desc3 中的值,如下所示:
需要手动设置结束符,执行 dest3[9] = '\0'; 后,desc3 中的值,如下所示:
2.4 注意事项
缓冲区溢出:虽然 strncpy 限制了复制的字符数,但如果没有正确地处理目标字符串的末尾(如上例所示),它仍然可能导致缓冲区溢出。因此,确保在调用 strncpy 后手动添加空字符是一个好习惯。
空字符处理:
- 如果源字符串 src 的长度小于 n,strncpy 会复制整个源字符串,包括其终止符 '\0',然后用额外的 '\0' 填充目标字符串 dest 的剩余部分,直到总共复制了 n 个字符。
- 如果源字符串 src 的长度(包括 '\0')等于 n,strncpy 会复制整个源字符串,包括其终止符 '\0'。
- 如果源字符串 src 的长度大于 n,strncpy 会只复制前 n 个字符,不会包括源字符串的终止符 '\0',这意味着如果程序员不手动添加终止符,dest 可能不会是一个以空字符结尾的有效字符串。
性能:与 strcpy 相比,strncpy 提供了更多的灵活性,但也可能导致性能下降,因为它可能需要额外的步骤来确保字符串以空字符结尾。
使用场景:strncpy 在处理来自不受信任源的数据时特别有用,因为它允许程序员限制复制的字符数,从而减少潜在的缓冲区溢出风险。然而,它要求程序员更加注意目标字符串的结尾处理。
3 strncmp
3.1 函数原型
strncmp 函数是 C 语言标准库中的一个字符串比较函数,它定义在 <string.h> 头文件中。该函数的原型如下:
#include <string.h>
int strncmp(const char *s1, const char *s2, size_t n);
- 参数:
- const char *s1:指向第一个要比较的字符串的指针。
- const char *s2:指向第二个要比较的字符串的指针。
- size_t n:指定要比较的最大字符数。
- 返回值:
- 如果 s1 和 s2 字符串的前 n 个字符完全相同,则返回 0。
- 如果根据 ASCII 码值,s1 字符串中的字符在字典序上小于 s2 字符串中对应位置的字符,则返回小于 0 的值。
- 如果根据 ASCII 码值,s1 字符串中的字符在字典序上大于 s2 字符串中对应位置的字符,则返回大于 0 的值。
3.2 功能说明
strncmp 函数用于比较两个字符串的前 n 个字符。它特别适用于在不需要完全比较整个字符串时,或者当知道字符串长度但不想因为空字符而停止比较时。如果两个字符串的前 n 个字符完全相同,则函数返回 0;否则,根据字符的 ASCII 码值返回小于或大于 0 的值,以表示第一个不同字符的比较结果。
- 如果 n 大于任一字符串的实际长度,strncmp 将持续比较到遇到第一个字符串的终止符 '\0' 为止,即使 n 的值更大。
- 如果 n 小于任一字符串的长度,strncmp 只比较前 n 个字符,即使字符串的实际长度更长。
3.3 案例演示
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "Hello, World!";
char str2[] = "Hello, C!";
char str3[] = "Hello";
// 比较 str1 和 str2 的前 8 个字符
int result1 = strncmp(str1, str2, 8);
if (result1 == 0)
{
printf("str1 和 str2 的前 8 个字符相同。\n");
}
else if (result1 < 0)
{
printf("str1 的前 8 个字符在字典序上小于 str2 的。\n");
}
else
{
printf("str1 的前 8 个字符在字典序上大于 str2 的。\n"); // 这里会输出
}
// 比较 str1 和 str3 的前 5 个字符
int result2 = strncmp(str1, str3, 5);
if (result2 == 0)
{
printf("str1 和 str3 的前 5 个字符相同。\n"); // 这里会输出
}
else if (result2 < 0)
{
printf("str1 的前 5 个字符在字典序上小于 str3 的。(理论上不会发生,因为实际是相等的)\n");
}
else
{
printf("str1 的前 5 个字符在字典序上大于 str3 的。(理论上也不会发生,因为实际是相等的)\n");
}
return 0;
}
输出结果如下所示:
3.4 注意事项
字符串的有效性:确保传递给 strncmp 的字符串是以空字符 '\0' 结尾的。如果字符串未正确终止,strncmp 可能会继续读取内存直到遇到随机的空字符,这可能导致未定义行为,如读取到敏感数据或程序崩溃。
比较的长度:仔细选择 n 的值,它指定了要比较的最大字符数。
- 如果 n 大于任一字符串的实际长度,strncmp 将持续比较到遇到第一个字符串的终止符 '\0' 为止,即使 n 的值更大。
- 如果 n 小于任一字符串的长度,strncmp 只比较前 n 个字符,即使字符串的实际长度更长。
大小写敏感性:strncmp 是区分大小写的。如果需要进行不区分大小写的比较,应使用其他方法(如先将字符串转换为全小写或全大写,然后再进行比较,或者使用专门的函数如 strncasecmp,但请注意后者不是标准 C 库的一部分)。
返回值的理解:正确理解 strncmp 的返回值。
- 返回值小于 0 表示 str1 在字典序上小于 str2 的前 n 个字符;
- 返回值等于 0 表示两者相等(至少在前 n 个字符内);
- 返回值大于 0 表示 str1 在字典序上大于 str2 的前 n 个字符。
性能考虑:在性能敏感的应用中,避免在循环中频繁调用 strncmp,特别是当 n 值较大或循环次数较多时。考虑是否可以通过其他方式(如使用哈希表、预先排序等)来优化性能。
安全边界:确保不会由于 n 的值过大而导致越界访问。虽然 strncmp 本身在达到 n 个字符或遇到字符串终止符时会停止,但确保 n 不超过目标字符串的实际分配空间是很重要的,以防止潜在的缓冲区溢出。
4 strspn
4.1 函数原型
strspn 函数是 C 语言标准库中的一个函数,用于计算字符串中首次出现不属于指定集合中任一字符之前的字符数(包括该集合中的字符)。其函数原型定义在 <string.h> 头文件中。
#include <string.h>
size_t strspn(const char *s1, const char *s2);
- 参数:
- const char *s1:指向要检查的字符串的指针。
- const char *s2:指向包含接受字符集合的字符串的指针。这些字符中的任何一个在 s1 中连续出现时,strspn 函数会继续计数,直到遇到不在 s2 中的字符。
- 返回值:返回 s1 开头到第一个不属于 s2 中任一字符出现之前的字符数(包括 s2 中的字符)。如果 s1 的所有字符都在 s2 中,则返回 s1 的长度。
4.2 功能说明
strspn 函数遍历 s1 字符串,计算从开始到第一个不在 s2 字符串中定义的字符集内的字符之前(包括该集合内的字符)的字符数。这个函数常用于跳过字符串开头的特定字符序列。
- 如果 s1 的开头就是 s2 中不存在的字符,则函数返回 0。
- 如果 s1 的所有字符都包含在 s2 中,则返回 s1 的长度(不包括终止的空字符)。
4.3 案例演示
#include <stdio.h>
#include <string.h>
int main()
{
const char *str1 = "abc123def";
const char *accept = "abc"; // 字符集
// 计算 str1 中首次出现不属于 accept 中字符之前的字符数
size_t len = strspn(str1, accept);
printf("在字符串 '%s' 中首次出现不属于 '%s' 中字符之前的字符数是: %zu\n", str1, accept, len); // 3
// 另一个例子,包含所有字符都在 accept 中的情况
// 如果 s1 的所有字符都包含在 s2 中,则返回 s1 的长度(不包括终止的空字符)
const char *str2 = "abc";
printf("在字符串 '%s' 中首次出现不属于 '%s' 中字符之前的字符数是: %zu\n", str2, accept, strspn(str2, accept)); // 3
// 还有一个例子,s1 开头就是 s2 中不存在的字符
// 如果 s1 的开头就是 s2 中不存在的字符,则函数返回 0
const char *str3 = "123abc";
const char *reject = "xyz";
printf("在字符串 '%s' 中首次出现不属于 '%s' 中字符之前的字符数是: %zu\n", str3, reject, strspn(str3, reject)); // 0
return 0;
}
输出结果如下所示:
4.4 注意事项
空字符串:如果 s1 是空字符串(""),则 strspn 返回 0。
NULL 指针:如果 s1 或 s2 是 NULL 指针,则行为是未定义的。在实际使用中,应确保这两个参数都不是 NULL。
字符集:s2 中的字符集是区分大小写的。例如,'a' 和 'A' 被视为不同的字符。
返回值类型:strspn 返回的是 size_t 类型,这是一个无符号整数类型,能够表示对象的大小。
性能:strspn 函数在内部逐个字符地检查 s1,直到遇到不在 s2 中的字符或到达 s1 的末尾。因此,其性能与 s1 的长度和 s2 的内容有关。
用途:该函数常用于需要跳过字符串开头特定字符序列的场景,如解析或过滤字符串数据。
5 strcspn
5.1 函数原型
strcspn 函数是 C 语言标准库中的一个函数,用于计算字符串中首次出现指定集合中任一字符之前的字符数(不包括该字符)。其函数原型定义在 <string.h> 头文件中。
#include <string.h>
size_t strcspn(const char *s1, const char *s2);
- 参数:
- const char *s1: 指向要检查的源字符串的指针。
- const char *s2: 指向包含拒绝字符集的字符串的指针。这些字符中的任何一个在 s1 中首次出现时,strcspn 函数就会停止计数。
- 返回值::返回 s1 开头到第一个 s2 中任一字符出现之前的字符数(不包括该字符)。如果 s1 的所有字符都不在 s2 中,则返回 s1 的长度。
5.2 功能说明
strcspn 函数遍历 s1 字符串,计算从开始到第一个出现在 s2 字符串中定义的字符集内的字符之前的字符数(不包括该字符)。这个函数常用于确定需要跳过的字符数,以便处理字符串的剩余部分。
- 如果 s1 的开头就是 s2 中的字符,则函数返回 0。
- 如果 s1 的所有字符都不在 s2 中,则返回 s1 的长度(不包括终止的空字符)。
5.3 案例演示
#include <stdio.h>
#include <string.h>
int main()
{
const char *str1 = "Hello, World!";
const char *reject = "oW"; // 字符集
// 计算 str1 中首次出现 reject 中任一字符之前的字符数
size_t pos = strcspn(str1, reject);
printf("在字符串 '%s' 中首次出现 '%s' 中任一字符之前的字符数是: %zu\n", str1, reject, pos); // 4(Hell)
// 另一个例子,s1 开头就是 s2 中的字符
// 如果 s1 的开头就是 s2 中的字符,则函数返回 0
const char *str2 = "World!";
const char *startChar = "W";
printf("在字符串 '%s' 中首次出现 '%s' 中字符之前的字符数是: %zu\n", str2, startChar, strcspn(str2, startChar)); // 0
// 还有一个例子,s1 中不包含 s2 中的任何字符
// 如果 s1 的所有字符都不在 s2 中,则返回 s1 的长度(不包括终止的空字符)
const char *str3 = "abcdef";
const char *nonExistent = "xyz";
printf("在字符串 '%s' 中首次出现 '%s' 中任一字符之前的字符数是: %zu\n", str3, nonExistent, strcspn(str3, nonExistent)); // 6
return 0;
}
输出结果如下所示:
5.4 注意事项
空字符串:如果 s1 是空字符串(""),则 strcspn 返回 0。
NULL 指针:如果 s1 或 s2 是 NULL 指针,则行为是未定义的。在实际使用中,应确保这两个参数都不是 NULL。
字符集:s2 中的字符集是区分大小写的。例如,'a' 和 'A' 被视为不同的字符。
返回值类型:strcspn 返回的是 size_t 类型,这是一个无符号整数类型,能够表示对象的大小。
性能:strcspn 函数在内部逐个字符地检查 s1,直到找到 s2 中的任一字符或到达 s1 的末尾。因此,其性能与 s1 的长度和 s2 的内容有关。
用途:该函数常用于需要跳过字符串开头特定字符序列的场景,或者确定字符串中某一部分的起始位置。
6 strpbrk
6.1 函数原型
strpbrk 函数是 C 语言标准库中的一个函数,用于查找字符串中首次出现指定集合中任一字符的位置。其函数原型定义在 <string.h> 头文件中。
#include <string.h>
char *strpbrk(const char *s1, const char *s2);
- 参数:
- const char *s1:指向要搜索的字符串的指针。
- const char *s2:指向包含要搜索的字符集合的字符串的指针。
- 返回值:如果找到 s2 中的任一字符在 s1 中的首次出现,则返回指向该字符的指针。如果未找到,则返回 NULL。
6.2 功能说明
strpbrk 函数遍历 s1 字符串,查找首次出现的 s2 字符串中定义的任一字符。它从 s1 的开头开始搜索,并逐个字符地检查,直到找到 s2 中的任一字符为止一旦找到,函数就返回指向该字符的指针。这个函数常用于确定字符串中特定字符集首次出现的位置。
- 如果 s1 的开头就是 s2 中的字符,则返回指向 s1 开头的指针。
- 如果 s1 中不包含 s2 中的任何字符,则返回 NULL。
6.3 案例演示
#include <stdio.h>
#include <string.h>
int main()
{
const char *str1 = "Hello, World!";
const char *chars = "Wdl"; // 字符集
// 查找 str1 中首次出现 chars 中任一字符的位置
char *found = strpbrk(str1, chars);
if (found)
{
printf("在字符串 '%s' 中首次出现 '%s' 中任一字符是 '%c',位置在索引 %d。\n",
str1, chars, *found, found - str1); // 2
}
else
{
printf("在字符串 '%s' 中没有找到 '%s' 中的任何字符。\n", str1, chars);
}
// 另一个例子,查找不存在的字符
const char *nonExistent = "xyz";
found = strpbrk(str1, nonExistent);
if (!found)
{
printf("在字符串 '%s' 中没有找到 '%s' 中的任何字符。\n", str1, nonExistent);
}
return 0;
}
输出结果如下所示:
6.4 注意事项
修改字符串:尽管 strpbrk 返回的指针不是 const 的,但如果修改了通过该指针访问的 s1 字符串的内容,这通常是不推荐的做法,因为 s1 被声明为 const char*,意味着它不应该被修改。然而,由于 strpbrk 的设计,返回的指针实际上是 char* 类型的,这允许修改。但在实际编程中,应尊重原字符串的 const 属性,除非有充分的理由需要修改。
空字符串:如果 s1 或 s2 是空字符串(""),则 strpbrk 的行为可能不是直观的。特别是,如果 s2 是空字符串,strpbrk 将返回 NULL,因为没有字符可以搜索。
NULL 指针:如果 s1 或 s2 是 NULL 指针,则行为是未定义的。在实际编程中,应确保这两个参数都不是 NULL。
性能:strpbrk 函数在内部逐个字符地检查 s1,直到找到 s2 中的任一字符或到达 s1 的末尾。因此,其性能与 s1 的长度和 s2 的内容有关。对于较长的字符串,如果不需要搜索整个字符串,考虑使用其他更高效的方法(如使用循环和字符比较)可能是有益的。
用途:strpbrk 函数常用于字符串处理任务中,特别是当需要找到某个特定字符集合中任一字符首次出现的位置时。
7 strerror
7.1 函数原型
strerror 函数是 C 语言标准库中的一个函数,用于将错误码(通常是一个整型值)转换为人类可读的错误消息字符串。其函数原型定义在 <string.h> 或 <stdio.h>(取决于编译器和系统)头文件中,但更常见的是在 <string.h> 中。
#include <stdio.h>
#include <string.h>
#include <errno.h>
char *strerror(int errnum);
- 参数:errnum 是一个整型,表示错误码。这个值通常由系统调用或库函数在发生错误时设置,比如通过 errno 变量。
- 返回值:返回一个指向错误消息字符串的指针。这个字符串描述了与 errnum 对应的错误。注意,返回的字符串指向静态分配的内存,因此不应被修改,并且在后续的 strerror 调用中可能会被覆盖。
7.2 功能说明
strerror 函数的主要功能是将一个错误码(如由 errno 提供的)转换为一个可读的错误消息字符串。这对于程序调试和向用户报告错误信息非常有用。由于不同的系统和环境可能产生不同的错误码,strerror 提供了一种标准化的方式来获取这些错误码的文本描述。
7.3 案例演示
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE *fp = fopen("不存在的文件.txt", "r");
if (fp == NULL)
{
// 当 fopen 失败时,errno 被设置为相应的错误码
printf("打开文件失败,错误码: %d, 错误信息: %s\n", errno, strerror(errno));
// 打开文件失败,错误码: 2, 错误信息: No such file or directory
}
else
{
// 文件打开成功,进行其他操作...
fclose(fp);
}
return 0;
}
在这个例子中,如果尝试打开的文件不存在,fopen 会失败并返回 NULL,同时设置 errno 为一个表示错误的整数值(如 ENOENT 表示文件或目录不存在)。然后,我们使用 strerror(errno) 获取该错误码的文本描述,并通过 printf 输出。
7.4 注意事项
静态缓冲区:strerror 返回的字符串存储在静态分配的内存中,这意味着它在后续的 strerror 调用中可能会被覆盖。如果需要保留错误信息,应该将其复制到自己的缓冲区中。
线程安全:由于 strerror 使用静态缓冲区,它在多线程程序中可能不是线程安全的。一些系统提供了 strerror_r 函数作为线程安全的替代方案,但请注意 strerror_r 的行为在不同的系统和标准中可能有所不同(例如,GNU C 库中的 strerror_r 和 POSIX.1-2001 中的 strerror_r 有不同的行为)。
错误码:errno 需要在调用可能设置它的函数之前被清零(通过 errno = 0;),或者确保在调用 strerror 之前,errno 确实被设置了预期的错误码。
8 strcoll
8.1 函数原型
strcoll 函数是 C 语言标准库中的一个函数,用于比较两个字符串,并根据当前的区域设置(locale)来排序它们。这个函数定义在 <string.h> 头文件中,但在使用它之前,通常需要包含 <locale.h> 来设置或查询程序的区域设置。
#include <string.h>
#include <locale.h>
int strcoll(const char *s1, const char *s2);
- 参数:s1 和 s2 是指向要比较的两个字符串的指针。
- 返回值:
- 如果 s1 小于 s2,则返回一个负数。
- 如果 s1 等于 s2,则返回 0。
- 如果 s1 大于 s2,则返回一个正数。
8.2 功能说明
strcoll 函数用于比较两个字符串 s1 和 s2,但不同于 strcmp 函数,strcoll 会考虑当前的区域设置(locale)。这意味着它可能会考虑字符的排序规则,这些规则可能与简单的字节值比较不同,特别是在使用非 ASCII 字符集(如中文、俄文等)时。
例如,在某些区域设置中,大写字母可能会被认为小于小写字母,而在其他设置中则可能相反。此外,某些字符(如重音字符)的排序可能也取决于区域设置。
8.3 案例演示
#include <stdio.h>
#include <string.h>
#include <locale.h>
int main()
{
// 设置区域设置为当前环境的区域设置
setlocale(LC_COLLATE, "");
const char *str1 = "apple";
const char *str2 = "Apple";
int result = strcoll(str1, str2);
if (result < 0)
{
printf("'%s' 在排序顺序中小于 '%s'\n", str1, str2);
}
else if (result > 0)
{
printf("'%s' 在排序顺序中大于 '%s'\n", str1, str2);
}
else
{
printf("'%s' 和 '%s' 在排序顺序中相等\n", str1, str2);
}
// 注意:具体输出可能依赖于程序的区域设置
return 0;
}
这个例子的输出将取决于程序运行的区域设置。在某些设置中,strcoll 可能会认为 "apple" 小于 "Apple",而在其他设置中(特别是区分大小写的环境),它可能会认为两者不相等,但不一定能准确预测哪个更大或更小,除非对特定的区域设置排序规则有深入了解。
输出结果如下所示:
8.4 注意事项
区域设置:strcoll 的行为完全依赖于当前的区域设置。如果程序需要在不同的语言环境中运行,并需要可靠的字符串比较和排序,那么必须正确设置区域设置。
性能:与 strcmp 相比,strcoll 可能会更慢,因为它需要考虑更多的排序规则。如果对性能有严格要求,并且不需要考虑区域设置,那么应该使用 strcmp。
线程安全:strcoll 本身是线程安全的,但它使用的区域设置状态(通过 setlocale 设置)可能在多线程环境中被意外修改。在需要处理多线程的程序中,应该仔细管理区域设置状态。
平台依赖性:不同的系统和编译器可能有不同的默认区域设置,因此相同的 strcoll 调用在不同的平台上可能会产生不同的结果。这需要在跨平台软件开发中特别注意。
9 strxfrm
9.1 函数原型
strxfrm 函数是 C 语言标准库中的一个函数,用于转换字符串以便进行区域设置感知的比较。它定义在 <string.h> 头文件中,但通常还需要 <locale.h> 头文件来设置或查询程序的区域设置。
#include <string.h>
#include <locale.h>
size_t strxfrm(char *dest, const char *src, size_t n);
- 参数:
- dest:指向用于存储转换后字符串的缓冲区的指针。如果为 NULL,则函数不存储任何内容,但会返回转换后字符串所需的字节数(不包括终止的空字符)。
- src:指向要转换的源字符串的指针。
- n:dest 缓冲区的大小。如果 dest 不是 NULL,则函数会尝试将转换后的字符串放入此缓冲区中,但不超过 n-1 个字符(以留出空间给终止的空字符)。
- 返回值:
- 如果 dest 不是 NULL,则返回存储在 dest 中的字符数(不包括终止的空字符),但不超过 n。
- 如果 dest 是 NULL,则返回转换整个 src 字符串所需的字符数(不包括终止的空字符)。
9.2 功能说明
strxfrm 函数根据当前的区域设置将 src 字符串转换为一个新的字符串形式,这种转换是为了使得转换后的字符串能够使用 strcmp 或类似的字节比较函数来进行正确的区域设置感知的字符串比较。然而,需要注意的是,strxfrm 本身并不直接比较字符串,而是进行转换;转换后的字符串(如果 dest 非空)应使用 strcmp 或 strncmp 进行比较。
由于 strxfrm 的转换是区域设置感知的,因此相同的 src 字符串在不同的区域设置下可能会转换为不同的结果。
9.3 案例演示
#include <stdio.h>
#include <string.h>
#include <locale.h>
int main()
{
// 设置区域设置为德语(德国),以演示区域设置感知的转换
setlocale(LC_COLLATE, "de_DE.UTF-8");
char dest[100];
const char *src = "Straße"; // 德语单词,包含特殊字符 ß
// 转换字符串
size_t len = strxfrm(dest, src, sizeof(dest));
// 注意:由于 strxfrm 的转换结果不是直接可见的字符串(它依赖于内部排序规则),
// 我们通常不会直接打印 dest 的内容进行比较。而是使用 strxfrm 和 strcmp的组合来演示其功能。
// 假设我们有两个转换后的字符串(这里为了简化,只使用一个作为示例)
// 实际上,你应该对两个字符串都进行 strxfrm 转换,然后用 strcmp 比较它们
// 但为了演示,我们可以输出转换后的长度
printf("转换后的字符串长度(不包括终止的空字符): %zu\n", len);
// 转换后的字符串长度(不包括终止的空字符): 7
// 如果需要比较,可以这样做(假设有另一个转换后的字符串 dest2)
// int cmp_result = strcmp(dest, dest2);
return 0;
}
上面的代码示例主要演示了如何使用 strxfrm 进行转换,并输出了转换后字符串的长度。由于 strxfrm 的转换结果通常不是直接可见的(它依赖于排序规则,而不是简单的字符替换),因此没有直接打印 dest 的内容。在实际应用中,会对两个字符串都进行 strxfrm 转换,并使用 strcmp 或 strncmp 来比较它们。
9.4 注意事项
区域设置:strxfrm 的行为完全依赖于当前的区域设置。如果需要在不同的语言环境中运行程序,并期望字符串比较行为一致,那么必须确保区域设置被正确设置。
性能:与简单的字符串复制或比较函数相比,strxfrm 可能会更慢,因为它需要执行区域设置感知的转换。
缓冲区大小:调用者需要确保 dest 缓冲区足够大,以容纳转换后的字符串(包括终止的空字符)。如果缓冲区太小,则函数可能会截断转换后的字符串。
线程安全:strxfrm 函数本身是线程安全的,但它使用的区域设置状态(通过 setlocale 设置)可能在多线程环境中被意外修改。在需要处理多线程的程序中,应该仔细管理区域设置状态。
10 总结对比表
函数原型 | 功能描述 | 关键点 |
---|---|---|
#include <string.h> char *strncat(char *dest, const char *src, size_t n); | 将 src 的前 n 个(最大字符数)字符(不包括终止的空字符)追加到 dest 的末尾,并在结果字符串的末尾添加一个空字符。 | 1. dest 必须有足够的空间来存放追加后的字符串。 2. 如果源字符串 src 的长度小于 n,则只追加实际存在的字符。 3. 不检查 dest 是否足够大,可能导致缓冲区溢出。 |
#include <string.h> char *strncpy(char *dest, const char *src, size_t n); | 将 src 的前 n 个字符(或直到遇到空字符,以先到者为准)复制到 dest 中。 | 1. 如果 src 的长度小于 n,则 dest 的剩余部分将被空字符填充。 2. 不检查 dest 是否足够大,可能导致缓冲区溢出。 3. 不会自动添加终止的空字符,除非 src 的长度至少为 n。 |
#include <string.h> int strncmp(const char *s1, const char *s2, size_t n); | 比较 s1 和 s2 的前 n 个字符。 | 1. 如果前 n 个字符相等,则返回 0;如果 s1 小于 s2,则返回负值;如果 s1 大于s2,则返回正值。 2. 只比较前 n 个字符,而不是整个字符串。 3. 区分大小写。 |
#include <string.h> size_t strspn(const char *s1, const char *s2); | 遍历 s1 字符串,计算从开始到第一个不在 s2 字符串中定义的字符集内的字符之前的字符数(不包括该字符)。 | 1. 如果 s1 的开头就是 s2 中不存在的字符,则函数返回 0。 2. 如果 s1 的所有字符都包含在 s2 中,则返回 s1 的长度(不包括终止的空字符)。 |
#include <string.h> size_t strcspn(const char *s1, const char *s2); | 遍历 s1 字符串,计算从开始到第一个出现在 s2 字符串中定义的字符集内的字符之前的字符数(不包括该字符)。 | 1. 如果 s1 的开头就是 s2 中的字符,则函数返回 0。 2. 如果 s1 的所有字符都不在 s2 中,则返回 s1 的长度(不包括终止的空字符)。 |
#include <string.h> char *strpbrk(const char *s1, const char *s2); | 扫描 s1 中的字符,查找第一个在 s2 中出现的字符。 | 1. 如果 s1 的开头就是 s2 中的字符,则返回指向 s1 开头的指针。 2. 如果 s1 中不包含 s2 中的任何字符,则返回 NULL。 |
#include <string.h> #include <errno.h> char *strerror(int errnum); | 根据错误码 errnum,返回指向错误描述字符串的指针。 | 1. 返回的字符串是静态分配的,可能在后续的 strerror 调用中被覆盖。 2. 线程安全版本(如 strerror_r 或 strerror_l)可能需要用户提供的缓冲区。 |
#include <string.h> #include <locale.h> int strcoll(const char *s1, const char *s2); | 根据当前的区域设置对 s1 和 s2 进行字符串比较。 | 1. 比较是区域设置感知的,可能涉及字符的排序规则、大小写转换等。 2. 如果 s1 小于 s2,则返回负值;如果相等,则返回 0;如果 s1 大于 s2,则返回正值。 3. 需要在使用前设置正确的区域设置。 |
#include <string.h> #include <locale.h> size_t strxfrm(char *dest, const char *src, size_t n); | 根据当前的区域设置,将 src 转换为一种形式,以便可以使用 strcmp 等函数进行比较。 | 1. 如果 dest 非空,则将转换后的字符串(最多 n-1 个字符)存储在 dest 中,并在末尾添加空字符。 2. 如果 dest 为空,则返回转换整个 src 所需的字符数(不包括终止的空字符)。 3. 转换是区域设置感知的,转换后的字符串可以用 strcmp 等函数进行正确的比较。 |