1. 字符分类函数
C语言中有一系列的函数是专门做字符分类的,也就是一个字符是属于什么类型的字符的。这些函数的使用都需要包含一个头文件是<ctype.h>
<ctype.h>头文件中的字符分类函数提供了一组用于检查单个字符特性的函数。这些函数接收一个字符(通常为int类型的char值),返回布尔值(非零为真,零为假)。常用于处理用户输入或文本解析。
1.1 函数原理
字符分类函数通过ASCII码表定义字符范围。例如,isalpha判断是否是字母的逻辑是基于字符ASCII码是否位于大写字母(65-90)或小写字母(97-122)的范围内。
1.2 标准库实现的特点
- 实现高效,直接使用位掩码或跳转表。
- 通常硬件层面优化,比如ARM架构中会用单指令完成掩码操作。
1.3 用法
| 函数 | 如果参数符号符合下列条件就返回真 | 
|---|---|
| iscntrl | 任何控制字符 | 
| isspace | 空白字符:空格 ' ',换页 '\f',换行 '\n',回车 '\r',制表符 '\t' 或者垂直制表符 '\v' | 
| isdigit | 十进制数字 '0' ~ '9' 字符 | 
| isxdigit | 十六进制数字,包括所有十进制数字字符,小写字母 a~f,大写字母 A~F | 
| islower | 小写字母 a~z | 
| isupper | 大写字母 A~Z | 
| isalpha | 字母 a~z 或 A~Z | 
| isalnum | 字母或者数字 a~z, A~Z, 0~9 | 
| ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) | 
| isgraph | 任何图形字符 | 
| isprint | 任何可打印字符,包括图形字符和空白字符 | 
这些函数的使用方法非常类似,我们就讲解一个函数的事情,其他的非常类似:
int islower ( int c );
islower 是能够判断参数部分的c 是否是小写字母的。
通过返回值来说明是否是小写字母,如果是小写字母就返回非0的整数,如果不是小写字母,则返回0。
1.4 使用示例
写一个代码,将字符串中的小写字母转大写,其他字符不变。
#include <stdio.h>
#include <ctype.h>
// 函数实现小写字母转大写
void toUppercase(char *str) {
    int i = 0;
    while (str[i] != '\0') {  // 遍历字符串
        if (islower(str[i])) {  // 判断是否是小写字母
            str[i] -= 32;  // 转换为大写字母
        }
        i++;
    }
}
int main() {
    char str[100];
    
    // 输入字符串
    printf("请输入一个字符串:");
    fgets(str, sizeof(str), stdin);  // 使用fgets以支持空格输入
    
    // 转换小写字母为大写
    toUppercase(str);
    
    // 输出转换后的字符串
    printf("转换后的字符串:%s", str);
    
    return 0;
}
输入:
Hello, World!
输出:
制换后的字符串:HELLO, WORLD!
2. 字符转换函数
2.1 toupper 函数
 
功能:将小写字母转换为对应的大写字母。如果字符不是小写字母,则返回原字符。
函数原型:
int toupper(int c);
参数:
- c是一个字符(通常以整数形式传递,如- char类型值)。
返回值:
- 如果 c是小写字母(‘a’ 到 ‘z’),返回其对应的大写字母;
- 如果 c不是小写字母,直接返回c。
示例:
#include <stdio.h>
#include <ctype.h>
int main() {
    char ch = 'a';
    printf("toupper('%c') = '%c'\n", ch, toupper(ch)); 
    ch = 'A';
    printf("toupper('%c') = '%c'\n", ch, toupper(ch));  
    ch = '1';
    printf("toupper('%c') = '%c'\n", ch, toupper(ch));  
    return 0;
}

2.2 tolower 函数
 
功能:将大写字母转换为对应的小写字母。如果字符不是大写字母,则返回原字符。
函数原型:
int tolower(int c);
参数和返回值:同 toupper,但作用于大写字母。
示例:
#include <stdio.h>
#include <ctype.h>
int main() {
    char ch = 'A';
    printf("tolower('%c') = '%c'\n", ch, tolower(ch));  // 输出:tolower('A') = 'a'
    ch = 'a';
    printf("tolower('%c') = '%c'\n", ch, tolower(ch));  // 输出:tolower('a') = 'a'
    ch = '!';
    printf("tolower('%c') = '%c'\n", ch, tolower(ch));  // 输出:tolower('!') = '!'
    return 0;
}

3. strlen 的使用和模拟实现
size_t strlen ( const char * str );
3.1 函数原理
strlen通过指针遍历,寻找终止符\0,并计算字符个数。标准实现可能对齐内存块,通过字节操作实现性能优化。
3.2 标准库实现的特点
- 在大多数平台上,strlen通过内存对齐和SIMD指令完成高效计算。
- 一次性读取多个字节,通过掩码操作检测是否包含\0。
3.3 注意事项
- 字符串以'\0'作为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(不包含'\0')。若字符串未正确终止,可能导致越界读取。
- 参数指向的字符串必须要以'\0'结束。
- 注意函数的返回值为 size_t,是无符号的(易错)
- 在循环中多次调用strlen会带来性能问题,应将长度预存。
- strlen的使用需要包含头文件- <string.h>
3.4 模拟实现
3.4.1 方式1
//通过指针运算
#include <stdio.h>
size_t my_strlen(const char *str) {
    const char *ptr = str;
    while (*ptr != '\0') {
        ptr++;
    }
    return ptr - str;
}
int main() {
    char str[] = "I like KUST";
    printf("字符串长度: %zu\n", my_strlen(str));
    return 0;
}

3.4.2 方式2
#include <stdio.h>
//计数器方式
int my_strlen(const char * str)
{
	int count = 0;
	while(*str)
	{
		count++;
		str++;
	}
	return count;
}
int main() {
    char str[] = "I like KUST";
    printf("字符串长度: %zu\n", my_strlen(str));
    return 0;
}

3.4.3 方式3
#include <stdio.h>
//不能创建临时变量计数器,递归方式
int my_strlen(const char * str)
{
	if(*str == '\0')
		return 0;
	else
		return 1 + my_strlen(str+1);
}
int main() {
    char str[] = "I like KUST";
    printf("字符串长度: %zu\n", my_strlen(str));
    return 0;
}

4. strcpy的使用和模拟实现
char* strcpy(char * destination, constchar * source );
4.1 函数原理
strcpy逐字节将源字符串的内容复制到目标地址,最后加上终止符\0。标准实现可能使用内存块操作(如memcpy)加速。
4.2 标准库实现的特点
- 不检查目标缓冲区大小,容易导致缓冲区溢出。
- 通常通过CPU指令(如MOVS指令)实现批量拷贝。
4.3 注意事项
- Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).
- 源字符串必须以'\0'结束。
- 会将源字符串中的'\0'拷贝到目标空间。
- strcpy不会检查- dest的大小,应确保目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可修改。
4.4 模拟实现
#include <stdio.h>
char *my_strcpy(char *dest, const char *src) {
    char *ptr = dest;
    while ((*ptr++ = *src++) != '\0');
    return dest;
}
int main() {
    char src[] = "I like KUST";
    char dest[50];
    my_strcpy(dest, src);
    printf("复制后的字符串: %s\n", dest);
    return 0;
}

5.strcat 的使用和模拟实现
5.1 函数功能
strcat 函数用于将一个字符串连接到另一个字符串的末尾。它会覆盖目标字符串的末尾的 \0,然后追加源字符串内容,最后加上新的终止符 \0。
5.2 函数声明
#include <string.h>
char *strcat(char *dest, const char *src);
- dest:目标字符串,存放拼接结果。
- src:源字符串,追加到目标字符串后。
- 返回值:目标字符串 dest的指针。
5.3 注意事项
- 源字符串必须以'\0'结束。
- 目标字符串中也得有\0,否则没办法知道追加从哪里开始。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
- 如果 src是空字符串,则目标字符串不会发生任何变化。
5.3 使用示例
#include <stdio.h>
#include <string.h>
int main() {
    char str1[50] = "I like ";  // 确保str1有足够的空间存储拼接结果
    char str2[] = "KUST!";
    strcat(str1, str2);  // 将str2拼接到str1末尾
    printf("拼接结果: %s\n", str1);  // 输出拼接后的字符串
    return 0;
}
输出结果:
拼接结果: I like KUST!
5.4 模拟实现
可以通过指针操作手动实现 strcat,逐步找到目标字符串的末尾,并从末尾开始追加源字符串。
#include <stdio.h>
char *my_strcat(char *dest, const char*src)
{
	char *ret = dest;
	while(*dest)
	{
		dest++;
	}
	while((*dest++ = *src++));
	return ret;
}
int main() {
    char str1[100] = "I like ";
    char str2[] = "Kunming University of Science and Technology";
    
    my_strcat(str1, str2);
    printf("拼接结果: %s\n", str1);
    return 0;
}
输出结果:
拼接结果: I like Kunming University of Science and Technology
6. strcmp的使用和模拟实现
 
strcmp 是 C 语言 <string.h> 库中最常用的字符串比较函数之一,用于比较两个字符串的字典序大小。以下从函数功能、实现原理、标准用法、模拟实现、常见问题、注意事项和扩展应用等方面进行详细说明。
6.1 函数功能
strcmp 用于逐字符比较两个字符串的大小,其比较规则如下:
- 如果字符串 str1等于str2,返回值为0。
- 如果字符串 str1小于str2(基于ASCII码值),返回负值。
- 如果字符串 str1大于str2(基于ASCII码值),返回正值。
6.2 函数声明
#include <string.h>
int strcmp(const char *str1, const char *str2);
- 参数: 
  - str1:第一个字符串的指针。
- str2:第二个字符串的指针。
 
- 返回值: 
  - 0:两个字符串相等。
- 正值:str1的第一个不匹配字符大于str2对应字符。
- 负值:str1的第一个不匹配字符小于str2对应字符。
 
6.3 注意事项
-  This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs until the characters differ or until a terminating null-character is reached. 
-  strcmp是区分大小写的。例如,"Apple"与"apple"是不同的。
-  空字符串的比较结果永远小于任何非空字符串。 
-  确保字符串以 \0结尾,否则可能导致内存越界。
6.4 函数使用示例
以下示例比较两个字符串并根据返回值输出结果。
#include <stdio.h>
#include <string.h>
int main() {
    char str1[] = "apple";
    char str2[] = "banana";
    int result = strcmp(str1, str2);
    if (result == 0) {
        printf("两个字符串相等\n");
    } else if (result < 0) {
        printf("\"%s\" 小于 \"%s\"\n", str1, str2);
    } else {
        printf("\"%s\" 大于 \"%s\"\n", str1, str2);
    }
    return 0;
}
输出结果:
"apple" 小于 "banana"
6.5 实现原理
strcmp 的核心逻辑是:
- 逐字符比较两个字符串对应位置的字符。
- 如果找到不相同的字符,则返回它们的 ASCII 值差。
- 如果全部字符相等,返回 0。
6.6 模拟实现
以下代码实现了 strcmp 的核心逻辑。
#include <stdio.h>
int my_strcmp (const char * str1, const char * str2)
{
	int ret = 0 ;
	while(*str1 == *str2)
	{
		if(*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	return *str1-*str2;
}
int main() {
    char str1[] = "hello";
    char str2[] = "world";
    int result = my_strcmp(str1, str2);
    if (result == 0) {
        printf("两个字符串相等\n");
    } else if (result < 0) {
        printf("\"%s\" 小于 \"%s\"\n", str1, str2);
    } else {
        printf("\"%s\" 大于 \"%s\"\n", str1, str2);
    }
    return 0;
}
输出结果:
"hello" 小于 "world"
7. strncpy函数的使用
 
7.1 函数功能
strncpy 用于将一个字符串的指定长度字符复制到目标字符串。如果目标长度大于源字符串长度,会填充额外的空字符 \0。如果指定的复制长度小于源字符串的长度,则目标字符串可能不是一个以 '\0' 结尾的有效 C字符串,可能引发未定义行为。
7.2 函数声明
#include <string.h>
char *strncpy(char *dest, const char *src, size_t n);
- dest:目标字符串。
- src:源字符串。
- n:要复制的最大字符数。
- 返回值:返回目标字符串 dest。
7.3 使用示例
#include <stdio.h>
#include <string.h>
int main() {
    char src[] = "I like KUST which is the best school in my heart";
    char dest[20];
    strncpy(dest, src, 11);
    dest[11] = '\0'; //手动添加终止符
    printf("目标字符串: %s\n", dest);
    return 0;
}
输出结果:
目标字符串: I like KUST
8. strncat函数的使用
 
8.1 函数功能
- Appends the first num characters of source to destination, plus a terminating null-character.(将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加一个\0字符)。
- If the length of the C string in source is less than num, only the content up to the terminating null-character is copied.(如果source 指向的字符串的长度小于num的时候,只会将字符串中到\0的内容追加到destination指向的字符串末尾)。
8.2 函数声明
#include <string.h>
char *strncat(char *dest, const char *src, size_t n);
- dest:目标字符串,需有足够空间存储结果。
- src:源字符串。
- n:追加的最大字符数。
- 返回值:目标字符串 dest。
8.3 使用示例
#include <stdio.h>
#include <string.h>
int main() {
    char str1[20] = "I like ";
    char str2[] = "KUST is the best school in my heart";
    strncat(str1, str2, 4);  // 只追加3个字符
    printf("拼接结果: %s\n", str1);
    return 0;
}
输出结果:
拼接结果: I like KUST
9. strncmp函数的使用
 
9.1 函数功能
strncmp 用于比较两个字符串的前 n 个字符,并返回比较结果。
9.2 函数声明
#include <string.h>
int strncmp(const char *str1, const char *str2, size_t n);
- str1和- str2:要比较的两个字符串。
- n:比较的字符数。
- 返回值: 
  - 0:相等。
- 正值:str1大于str2。
- 负值:str1小于str2。
 
9.3 使用示例
#include <stdio.h>
#include <string.h>
int main() {
    char str1[] = "abcdef";
    char str2[] = "abcxyz";
    int result = strncmp(str1, str2, 3);
    if (result == 0) {
        printf("前3个字符相等\n");
    } else {
        printf("前3个字符不相等\n");
    }
    return 0;
}
输出结果:
前3个字符相等
10. strstr的使用和模拟实现
 
strstr 是 C 标准库中一个非常有用的字符串操作函数,用于在一个字符串中查找子字符串的第一次出现。下面将详细解析 strstr 的功能、用法、原理、模拟实现、注意事项和扩展应用。
10.1 函数功能
-  strstr用于在字符串haystack中查找子字符串needle的第一次出现,并返回指向该子字符串起始位置的指针。如果未找到,返回NULL。
-  Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.(函数返回字符串str2在字符串str1中第一次出现的位置)。 
-  The matching process does not include the terminating null-characters, but it stops there.(字符串的比较匹配不包含 \0字符,以\0作为结束标志)。
10.2 函数声明
#include <string.h>
char *strstr(const char *haystack, const char *needle);
- 参数: 
  - haystack:要搜索的目标字符串。
- needle:要查找的子字符串。
 
- 返回值: 
  - 如果找到子字符串,返回其在 haystack中的起始地址。
- 如果未找到子字符串,返回 NULL。
- 如果 needle为空字符串,则返回haystack的起始地址。
 
- 如果找到子字符串,返回其在 
10.3 使用示例
#include <stdio.h>
#include <string.h>
int main() {
    char haystack[] = "Kunming University of Science and Technology Faculty of Information Engineering and Automation";
    char needle[] = "Technology";
    char *result = strstr(haystack, needle);
    if (result != NULL) 
    {
        printf("子字符串 \"%s\" 找到了,起始位置: %s\n", needle, result);
    } 
    else 
    {
        printf("子字符串 \"%s\" 未找到。\n", needle);
    }
    return 0;
}
输出结果:
子字符串 "Technology" 找到了,起始位置: Technology Faculty of Information Engineering and Automation
10.4 模拟实现
以下是 strstr 的核心逻辑模拟实现:
#include <stdio.h>
char * my_strstr (const char * str1, const char * str2)
{
    char *cp = (char *) str1;
    char *s1, *s2;
    if ( !*str2 )
        return((char *)str1);
    while (*cp)
    {
        s1 = cp;
        s2 = (char *) str2;
        while ( *s1 && *s2 && !(*s1-*s2) )
            s1++, s2++;
        if (!*s2)
            return(cp);
        cp++;
    }
    return(NULL);
}
int main() {
    char haystack[] = "Faculty of Information Engineering and Automation";
    char needle[] = "Information";
    char *result = my_strstr(haystack, needle);
    if (result != NULL) {
        printf("子字符串 \"%s\" 找到了,起始位置: %s\n", needle, result);
    } else {
        printf("子字符串 \"%s\" 未找到。\n", needle);
    }
    return 0;
}
输出:
子字符串 "Information" 找到了,起始位置: Information Engineering and Automation
10.5 注意事项
-  如果 needle是空字符串(""),则strstr返回haystack的起始位置。
-  strstr是大小写敏感的。如果需要忽略大小写,可以结合tolower和toupper函数对字符进行预处理。
11. strtok函数的使用
 
strtok 是 C 标准库中的字符串处理函数,用于将字符串分割为多个子字符串(token),以指定的分隔符为分割依据。
11.1 函数功能
strtok 用于将字符串分割为一个个子字符串,分隔符由用户指定。每次调用 strtok 会返回字符串中的下一个子字符串,并用 NULL 标记字符串的结束。
11.2 函数声明
#include <string.h>
char *strtok(char *str, const char *delim);
- 参数: 
  - str:待分割的字符串(首次调用时提供整个字符串,之后传入- NULL表示接着上次的位置继续分割)。
- delim:分隔符字符串(可包含多个字符)。
 
- 返回值: 
  - 如果找到子字符串,则返回指向该子字符串的指针。
- 如果字符串分割完成或找不到分隔符,则返回 NULL。
 
11.3 注意事项
- delim参数指向一个字符串,定义了用作分隔符的字符集合
- 第一个参数指定一个字符串,它包含了0个或者多个由delim字符串中一个或者多个分隔符分割的标记。
- strtok函数找到str中的下一个标记,并将其用- \0结尾,返回一个指向这个标记的指针。(注:- strtok函数会改变被操作的字符串,所以被- strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
- strtok函数的第一个参数不为- NULL,函数将找到str中第一个标记,- strtok函数将保存它在字符串中的位置。
- strtok函数的第一个参数为- NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
- 如果字符串中不存在更多的标记,则返回NULL指针。
11.4 使用示例
以下代码使用 strtok 按空格分割字符串:
#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = "Kunming University of Science and Technology Faculty of Information Engineering and Automation";
	char* sep = " ";
	char* str = NULL;
	for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
	{
		printf("子字符串:%s\n", str);
	}
	return 0;
}
输出结果:
子字符串:Kunming
子字符串:University
子字符串:of
子字符串:Science
子字符串:and
子字符串:Technology
子字符串:Faculty
子字符串:of
子字符串:Information
子字符串:Engineering
子字符串:and
子字符串:Automation
11.5 实现原理
- 首次调用: 
  - strtok接收完整字符串- str和分隔符- delim。
- 找到第一个分隔符位置,将其替换为 \0,以标记第一个子字符串的结束。
- 返回指向第一个子字符串的指针。
 
- 后续调用: 
  - 如果 str参数为NULL,strtok会继续从上次的位置向后查找下一个子字符串。
- 找到分隔符后继续分割,直到字符串末尾。
 
- 如果 
12. strerror函数的使用
 
12.1 函数功能
strerror 是 C 标准库中的一个错误处理函数,用于将错误码(通常是 errno)转换为人类可读的错误信息字符串。这在调试程序和输出错误信息时非常有用。
12.2 函数声明
#include <string.h>
char *strerror(int errnum);
- 参数: 
  - errnum:一个整数类型的错误代码(通常是全局变量- errno的值)。
 
- 返回值: 
  - 一个指向描述错误信息的字符串的指针。
- 如果 errnum不对应任何已定义的错误代码,则返回的字符串是实现相关的。
 
12.3 使用示例
我们首先来认识一下错误代码对应的字符串
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
	for (int i = 0; i < 42; i++)
	{
		printf("%d: %s\n", i, strerror(i));
	}
	
	return 0;
}
输出:
0: No error
1: Operation not permitted
2: No such file or directory
3: No such process
4: Interrupted function call
5: Input/output error
6: No such device or address
7: Arg list too long
8: Exec format error
9: Bad file descriptor
10: No child processes
11: Resource temporarily unavailable
12: Not enough space
13: Permission denied
14: Bad address
15: Unknown error
16: Resource device
17: File exists
18: Improper link
19: No such device
20: Not a directory
21: Is a directory
22: Invalid argument
23: Too many open files in system
24: Too many open files
25: Inappropriate I/O control operation
26: Unknown error
27: File too large
28: No space left on device
29: Invalid seek
30: Read-only file system
31: Too many links
32: Broken pipe
33: Domain error
34: Result too large
35: Unknown error
36: Resource deadlock avoided
37: Unknown error
38: Filename too long
39: No locks available
40: Function not implemented
41: Directory not empty
以下代码展示了 strerror 的基本用法:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
    FILE *file;
    // 尝试打开一个不存在的文件
    file = fopen("nonexistent.txt", "r");
    if (file == NULL) {
        // 打印errno的值和对应的错误信息
        printf("错误代码: %d\n", errno);
        printf("错误信息: %s\n", strerror(errno));
    }
    return 0;
}
输出结果(示例):
错误代码: 2
错误信息: No such file or directory
12.4 strerror 的常见用途
 
- 错误处理和调试: 
  - 在程序出错时,输出错误码对应的可读信息,便于排查问题。
- 通常配合标准库的 errno使用。
 
- 日志记录: 
  - 将错误信息写入日志文件,便于开发者在后续分析错误原因。
 
- 用户提示: 
  - 当程序出错时,向用户展示错误原因,而不仅仅是错误代码。
 
12.5 实现原理
错误代码 (errno):
errno 是一个全局变量,定义在 <errno.h> 中,用于存储最近一次函数调用的错误代码。标准库函数在出错时会设置 errno,以便开发者通过 strerror 查询错误信息。
strerror 的工作机制:
- strerror通过- errnum查找内部的错误信息表,并返回对应的描述信息。
- 如果 errnum超出范围,则返回实现相关的默认错误信息。
12.6 扩展
可以了解一下 perror 函数,perror函数相当于一次将上述代码中的第9行完成了,直接将错误信息打印出来。perror函数打印完参数部分的字符串后,再打印一个冒号和一个空格,再打印错误信息。
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{
	FILE * pFile;
	pFile = fopen ("unexist.ent","r");
	if (pFile == NULL)
		perror("Error opening file unexist.ent");
	return 0;
}
输出:
Error opening file unexist.ent: No such file or directory
—完—












![[大数据] Iceberg](https://i-blog.csdnimg.cn/direct/ab80c47b948444329e1ffb1037ed24d4.png)






![[高阶数据结构(一)]并查集详解](https://i-blog.csdnimg.cn/direct/51f95aa39f9d42f99ff7c74139bf3f2c.png)