strtok
- 在 C 语言中,
strtok
函数的原型为char *strtok(char *str, const char *delim)
。 - 它的主要功能是将字符串
str
按照delim
中指定的分隔符进行分割,返回被分割出来的子字符串。
- 首次调用:
- 当第一次调用
strtok
时,需要将待分割的字符串str
作为第一个参数传入。strtok
函数会在str
中查找第一个不是分隔符(由delim
指定)的字符,然后从这个字符开始,找到下一个分隔符(或者字符串结束符\0
),将分隔符替换为\0
,并返回这个子字符串的指针。
- 后续调用:
- 对于后续的调用,第一个参数需要传入
NULL
。strtok
函数会从上一次分割的位置之后继续查找下一个子字符串,同样将找到的分隔符替换为\0
,并返回新的子字符串指针。
- 分割过程结束:
- 当没有更多的子字符串可分割时,
strtok
返回NULL
。
- 例如,将一个逗号分隔的字符串进行分割:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "apple,banana,cherry";
char *token;
token = strtok(str, ",");
while (token!= NULL) {
printf("%s\n", token);
token = strtok(NULL, ",");
}
return 0;
}
- 在这个示例中,首先将
str
作为第一个参数调用strtok
得到第一个子字符串("apple"),然后在循环中不断传入NULL
作为第一个参数调用strtok
,依次得到 "banana" 和 "cherry",直到strtok
返回NULL
,循环结束。
strtok
函数会修改原始字符串,将分隔符替换为\0
。- 它不是线程安全的,如果在多线程环境下需要对字符串进行分割,应该考虑使用可重入的替代函数(如
strtok_r
,它是strtok
的可重入版本,在一些多任务操作系统中使用)。
strerror
- 在 C 语言中,
strerror
函数的原型为char *strerror(int errnum)
。 - 其功能是根据给定的错误码
errnum
,返回一个描述该错误的字符串。这个函数主要用于将系统调用或库函数返回的错误码转换为人类可读的错误信息字符串。
- 在 Unix 和类 Unix 系统(如 Linux)中,系统调用和很多库函数在执行失败时会返回一个特定的错误码(通常是一个小的整数)。例如,
open
函数在无法打开一个文件时可能返回 - 1,并设置全局变量errno
为一个对应的错误码(如ENOENT
表示文件不存在,EACCES
表示权限不足等)。
- 以下是一个简单的示例,展示如何使用
strerror
函数:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
FILE *fp = fopen("nonexistent_file.txt", "r");
if (fp == NULL) {
int err = errno;
printf("Error opening file: %s\n", strerror(err));
}
return 0;
}
- 在这个示例中,尝试打开一个不存在的文件。
fopen
函数返回NULL
表示失败,然后通过errno
获取错误码,并使用strerror
函数将错误码转换为相应的错误描述字符串并打印出来。
- 虽然
strerror
函数在大多数 Unix 和类 Unix 系统以及一些其他操作系统上都存在,但不同操作系统对于相同错误码的错误描述可能会有细微差异。 - 在一些多线程环境下,对
errno
变量(用于存储错误码)的访问可能需要特殊处理,因为errno
可能是一个全局变量,多个线程同时访问可能会导致问题。不过,现代操作系统通常提供了线程安全的机制来处理errno
,如使用pthread
库的线程环境下,每个线程有自己独立的errno
副本(在某些实现中)。
字符分类函数
- 函数原型:
int isalnum(int c);
- 功能:判断字符
c
是否为字母或数字。如果c
是一个字母(a - z
或A - Z
)或者数字(0 - 9
),则函数返回非零值(真),否则返回 0(假)。 - 示例:
#include <stdio.h>
#include <ctype.h>
int main() {
char c1 = 'a';
char c2 = '9';
char c3 = '!';
printf("%d\n", isalnum(c1));// 输出1(非零,表示真)
printf("%d\n", isalnum(c2));// 输出1
printf("%d\n", isalnum(c3));// 输出0
return 0;
}
- 函数原型:
int isalpha(int c);
- 功能:判断字符
c
是否为字母。如果c
是一个字母(a - z
或A - Z
),则函数返回非零值(真),否则返回 0(假)。 - 示例:
#include <stdio.h>
#include <ctype.h>
int main() {
char c1 = 'b';
char c2 = '8';
char c3 = '?';
printf("%d\n", isalpha(c1));// 输出1
printf("%d\n", isalpha(c2));// 输出0
printf("%d\n", isalpha(c3));// 输出0
return 0;
}
- 函数原型:
int isdigit(int c);
- 功能:判断字符
c
是否为数字。如果c
是一个数字(0 - 9
),则函数返回非零值(真),否则返回 0(假)。 - 示例:
#include <stdio.h>
#include <ctype.h>
int main() {
char c1 = '5';
char c2 = 'a';
char c3 = ' ';
printf("%d\n", isdigit(c1));// 输出1
printf("%d\n", isdigit(c2));// 输出0
printf("%d\n", isdigit(c3));// 输出0
return 0;
}
- 函数原型:
int islower(int c);
- 功能:判断字符
c
是否为小写字母。如果c
是一个小写字母(a - z
),则函数返回非零值(真),否则返回 0(假)。 - 示例:
#include <stdio.h>
#include <ctype.h>
int main() {
char c1 = 'z';
char c2 = 'A';
char c3 = '9';
printf("%d\n", islower(c1));// 输出1
printf("%d\n", islower(c2));// 输出0
printf("%d\n", islower(c3));// 输出0
return 0;
}
- 函数原型:
int isupper(int c);
- 功能:判断字符
c
是否为大写字母。如果c
是一个大写字母(A - Z
),则函数返回非零值(真),否则返回 0(假)。 - 示例:
#include <stdio.h>
#include <ctype.h>
int main() {
char c1 = 'B';
char c2 = 'b';
char c3 = '3';
printf("%d\n", isupper(c1));// 输出1
printf("%d\n", isupper(c2));// 输出0
printf("%d\n", isupper(c3));// 输出0
return 0;
}
- 函数原型:
int isspace(int c);
- 功能:判断字符
c
是否为空白字符。空白字符包括空格' '
、制表符'\t'
、换行符'\n'
、垂直制表符'\v'
、换页符'\f'
和回车符'\r'
。如果c
是其中一种空白字符,则函数返回非零值(真),否则返回 0(假)。 - 示例:
#include <stdio.h>
#include <ctype.h>
int main() {
char c1 ='';
char c2 = 'a';
char c3 = '\n';
printf("%d\n", isspace(c1));// 输出1
printf("%d\n", isspace(c2));// 输出0
printf("%d\n", isspace(c3));// 输出1
return 0;
}
- 函数原型:
int ispunct(int c);
- 功能:判断字符
c
是否为标点符号。标点符号是除字母、数字和空白字符之外的可打印字符。如果c
是标点符号,则函数返回非零值(真),否则返回 0(假)。 - 示例:
#include <stdio.h>
#include <ctype.h>
int main() {
char c1 = '!';
char c2 = 'a';
char c3 = '9';
printf("%d\n", ispunct(c1));// 输出1
printf("%d\n", ispunct(c2));// 输出0
printf("%d\n", ispunct(c3));// 输出0
return 0;
}
字符转换函数
- 函数原型:
int tolower(int c);
- 功能:将大写字母转换为小写字母。如果输入的字符
c
是大写字母(A - Z
),则将其转换为对应的小写字母(a - z
),并返回转换后的字符;如果输入的字符不是大写字母,则直接返回该字符本身。 - 示例:
#include <stdio.h>
#include <ctype.h>
int main() {
char c1 = 'A';
char c2 = 'a';
char c3 = '9';
printf("%c\n", tolower(c1));// 输出 'a'
printf("%c\n", tolower(c2));// 输出 'a'
printf("%c\n", tolower(c3));// 输出 '9'
return 0;
}
- 函数原型:
int toupper(int c);
- 功能:将小写字母转换为大写字母。如果输入的字符
c
是小写字母(a - z
),则将其转换为对应的大写字母(A - Z
),并返回转换后的字符;如果输入的字符不是小写字母,则直接返回该字符本身。 - 示例:
#include <stdio.h>
#include <ctype.h>
int main() {
char c1 = 'a';
char c2 = 'A';
char c3 = '9';
printf("%c\n", toupper(c1));// 输出 'A'
printf("%c\n", toupper(c2));// 输出 'A'
printf("%c\n", toupper(c3));// 输出 '9'
return 0;
}
memcpy
- 在 C 和 C++ 中,
memcpy
函数的原型为void *memcpy(void *dest, const void *src, size_t n);
。 - 它的主要功能是将一段内存(由
src
指向)中的数据复制到另一段内存(由dest
指向)中,复制的字节数由n
指定。
dest
:目标内存地址,是复制数据的接收端。这个指针指向的内存区域必须有足够的空间来容纳要复制的数据,否则可能会导致缓冲区溢出等错误。src
:源内存地址,是要被复制数据的起始位置。n
:要复制的字节数。
memcpy
函数返回一个指向目标内存区域(dest
)的指针。这个返回值在某些情况下可以方便地进行链式操作或者在函数调用后对目标区域进行进一步操作。
- 例如,将一个数组的部分数据复制到另一个数组中:
#include <stdio.h>
#include <string.h>
int main() {
int arr1[5] = {1, 2, 3, 4, 5};
int arr2[5];
memcpy(arr2, arr1, sizeof(int) * 3);
for (int i = 0; i < 5; i++) {
if (i < 3) {
printf("%d ", arr2[i]);
} else {
printf("0 ");
}
}
return 0;
}
- 在这个示例中,
memcpy
将arr1
数组的前 3 个元素(共sizeof(int) * 3
字节)复制到arr2
数组中。然后遍历arr2
数组并输出结果,可以看到前 3 个元素被正确复制,后 2 个元素未被初始化(这里简单输出 0 表示)。 - 再例如,复制结构体数据:
#include <stdio.h>
#include <string.h>
struct Point {
int x;
int y;
};
int main() {
struct Point p1 = {1, 2};
struct Point p2;
memcpy(&p2, &p1, sizeof(struct Point));
printf("p2.x = %d, p2.y = %d\n", p2.x, p2.y);
return 0;
}
- 这里通过
memcpy
将p1
结构体的内容复制到p2
结构体中,然后输出p2
的成员变量,可以看到数据被正确复制。
- 如果源和目标内存区域有重叠,
memcpy
的行为是未定义的。在这种情况下,可以使用memmove
函数,它可以正确处理重叠的内存区域。 - 当复制非基本数据类型(如结构体中包含指针等情况)时,需要确保在目标区域中相关的指针等资源能够被正确处理,仅仅复制字节可能会导致后续使用时出现问题(例如浅复制可能需要进一步处理以实现深复制的效果)。
memmove
- 在 C 和 C++ 中,
memmove
函数的原型为void *memmove(void *dest, const void *src, size_t n);
。 - 其功能是将
src
所指向的内存区域中的n
个字节的数据移动到dest
所指向的内存区域。与memcpy
类似,但memmove
能够正确处理源内存区域和目标内存区域重叠的情况。
dest
:目标内存地址,是数据要移动到的位置。src
:源内存地址,是要被移动数据的起始位置。n
:要移动的字节数。
memmove
函数返回一个指向目标内存区域(dest
)的指针。
- 当源内存区域和目标内存区域重叠时:
- 如果
src
在dest
之前,memmove
会从源内存区域的起始位置开始复制数据到目标内存区域,这种情况下,数据的复制顺序是正常的顺序,不会影响到尚未复制的数据。 - 如果
src
在dest
之后,memmove
会从源内存区域的末尾开始复制数据到目标内存区域的末尾,然后逐步向前复制,这样可以避免在复制过程中覆盖还未被复制的数据。
- 以下是一个处理重叠内存区域的示例:
#include <stdio.h>
#include <string.h>
int main() {
char arr[] = "abcdef";
// 目标区域在源区域之后,有重叠
memmove(arr + 1, arr, 3);
printf("%s\n", arr);
return 0;
}
- 在这个示例中,
memmove
将arr
数组中从起始位置开始的 3 个字节(即 "abc")移动到从arr + 1
开始的位置。由于memmove
能够正确处理重叠情况,最终arr
数组的内容变为 "aabcde"。 - 再看一个源区域在目标区域之后的示例:
#include <stdio.h>
#include <string.h>
int main() {
char arr[] = "abcdef";
// 源区域在目标区域之后,有重叠
memmove(arr, arr + 1, 3);
printf("%s\n", arr);
return 0;
}
- 这里
memmove
将arr
数组中从arr + 1
开始的 3 个字节(即 "bcd")移动到arr
的起始位置。由于memmove
正确处理了重叠,最终arr
数组的内容变为 "bcddef"。
memcmp
memset
- 在 C 和 C++ 中,
memset
函数的原型为void *memset(void *s, int c, size_t n);
。
- 该函数用于将指定内存块中的前
n
个字节设置为特定的值c
。 - 这里的
s
是指向要设置内存块的起始地址的指针,可以是数组、结构体等各种数据类型的指针。 - 虽然
c
的类型为int
,但在实际操作中,函数会将c
转换为unsigned char
类型,然后用这个unsigned char
类型的值来填充内存块。
- 清零数组
- 例如,将一个整数数组清零:
#include <stdio.h>
#include <string.h>
int main() {
int arr[5];
memset(arr, 0, sizeof(arr));
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
return 0;
}
- 在这个例子中,
memset
将arr
数组中的所有字节都设置为 0。因为arr
是一个包含 5 个int
类型元素的数组,在大多数系统中int
占 4 个字节,所以sizeof(arr)
为20
字节,memset
会将这 20 个字节都设置为 0,从而达到将数组元素清零的效果。 - 填充字符数组
- 再如,填充一个字符数组:
#include <stdio.h>
#include <string.h>
int main() {
char str[10];
memset(str, 'A', 5);
str[5] = '\0';
printf("%s\n", str);
return 0;
}
- 这里将
str
字符数组的前 5 个字节设置为字符'A'
,然后手动添加字符串结束符'\0'
,以便能够正确地将其作为字符串进行输出,最终会输出"AAAAA"
。
- 当使用
memset
初始化非字符类型数组(如int
数组)时,要确保填充的值在目标类型的合法取值范围内。例如,如果用memset
将int
数组填充为 1(对于大多数系统中int
占 4 字节,实际上是将每个字节设置为 1),可能会得到意外的结果,因为这不是通常意义上对int
类型的赋值操作。 - 在 C++ 中,虽然
memset
仍然可以使用,但对于类对象的初始化,更推荐使用构造函数等面向对象的初始化方法。