在C语言开发中,字符串与字符处理是基础且高频的需求。从字符分类、大小写转换,到字符串的拷贝、比较、分割,再到错误信息的定位与输出,<ctype.h>与<string.h>中的一系列标准库函数,构成了我们处理文本与调试问题的核心工具链。本文将带你快速掌握这些函数的用法、核心要点与实战场景,让你在开发中更高效地处理字符串与字符问题。
一、字符分类函数
这类函数用于判断字符的类型,需要包含头文件 <ctype.h>。
| 函数名 | 如果他的参数符合下列条件就返回真 |
| iscntrl | 任何控制字符 |
| isspace | 空白字符(空格、换页\f、换行\n、回车\r、制表符\t、垂直制表符\v) |
| isdigit | 十进制数字 0~9 |
| isxdigit | 十六进制数字(0~9、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 | 任何可打印字符(含图形字符和空白字符) |
示例:判断小写字母并转换为大写
#include <stdio.h> #include <ctype.h> int main() { int i = 0; char str[] = "Test String.\n"; char c; while (str[i]) { c = str[i]; if (islower(c)) c -= 32; // 利用ASCII码差值转大写 putchar(c); i++; } return 0; }二、字符转换函数
用于字母的大小写转换,需要包含头文件 <ctype.h>。
| 函数名 | 功能 |
| tolower | 将大写字母转为小写 |
| toupper | 将小写字母转为大写 |
示例:用 toupper 实现小写转大写
#include <stdio.h> #include <ctype.h> int main() { int i = 0; char str[] = "Test String.\n"; char c; while (str[i]) { c = str[i]; if (islower(c)) c = toupper(c); putchar(c); i++; } return 0; }三、strlen 函数
用于计算字符串长度(统计 \0 之前的字符个数),需要包含头文件 <string.h>。
函数原型
size_t strlen(const char *str);核心要点
• 返回值类型是 size_t(无符号整数),比较时需注意避免负数错误。
• 字符串必须以 \0 结尾,否则结果未定义。
示例:strlen 的使用
#include <stdio.h> #include <string.h> int main() { const char* str = "abcdef"; printf("%zd\n", strlen(str)); // 输出:6 return 0; }模拟实现
1. 计数器方式
int my_strlen(const char *str) { int count = 0; assert(str); while (*str) { count++; str++; } return count; }2. 递归方式(无临时变量)
int my_strlen(const char *str) { assert(str); if (*str == '\0') return 0; else return 1 + my_strlen(str+1); }3. 指针-指针方式
int my_strlen(char *s) { assert(str); char *p = s; while (*p != '\0') p++; return p - s; }四、strcpy 函数
用于字符串拷贝(从源字符串拷贝到目标空间,包含 \0),需要包含头文件 <string.h>。
函数原型
char* strcpy(char *destination, const char *source);核心要点
• 源字符串必须以 \0 结尾。
• 目标空间必须足够大且可修改。
示例:strcpy 的使用
#include <stdio.h> #include <string.h> int main() { char arr1[10] = {0}; char arr2[] = "hello"; strcpy(arr1, arr2); printf("%s\n", arr1); // 输出:hello return 0; }模拟实现
#include <stdio.h> #include <assert.h> char* my_strcpy(char *dest, const char*src) { char *ret = dest; assert(dest != NULL); assert(src != NULL); while((*dest++ = *src++)) { ; } return ret; } int main() { char arr1[10] = {0}; char arr2[] = "hello"; my_strcpy(arr1, arr2); printf("%s\n", arr1); // 输出:hello return 0; }五、strcat 函数
用于字符串追加(将源字符串追加到目标字符串末尾),需要包含头文件 <string.h>。
函数原型
char *strcat(char *destination, const char *source);核心要点
• 源字符串必须以 \0 结尾。
• 目标字符串必须以 \0 结尾,且空间足够大。
示例:strcat 的使用
#include <stdio.h> #include <string.h> int main() { char arr1[20] = "hello "; char arr2[] = "world"; strcat(arr1, arr2); printf("%s\n", arr1); // 输出:hello world return 0; }模拟实现
#include <stdio.h> #include <assert.h> char* my_strcat(char *dest, const char*src) { char *ret = dest; assert(dest != NULL); assert(src != NULL); while(*dest) { dest++; } while((*dest++ = *src++)) { ; } return ret; } int main() { char arr1[20] = "hello "; char arr2[] = "world"; my_strcat(arr1, arr2); printf("%s\n", arr1); // 输出:hello world return 0; }六、strcmp 函数
用于字符串比较(按 ASCII 码逐字符比较),需要包含头文件 <string.h>。
函数原型
int strcmp(const char *str1, const char *str2);返回值规则
• > 0:str1 大于 str2
• = 0:str1 等于 str2
• < 0:str1 小于 str2
示例:strcmp 的使用
#include <stdio.h> #include <string.h> int main() { char arr1[] = "abcdef"; char arr2[] = "abq"; int ret = strcmp(arr1, arr2); printf("%d\n", ret); // 输出:负数('c' < 'q') if(ret > 0) printf("arr1 > arr2\n"); else if(ret == 0) printf("arr1 == arr2\n"); else printf("arr1 < arr2\n"); return 0; }模拟实现
int my_strcmp(const char *str1, const char *str2) { int ret = 0 ; assert(str1 != NULL); assert(str2 != NULL); while(*str1 == *str2) { if(*str1 == '\0') return 0; str1++; str2++; } return *str1-*str2; }七、strncpy 函数
strcpy 的安全版本,可指定拷贝的最大字符数,需要包含头文件 <string.h>。
函数原型
char *strncpy(char *destination, const char *source, size_t num);核心要点
• 最多拷贝 num 个字符,源字符串无需以 \0 结尾。
• 若源字符串长度不足 num,剩余位置用 \0 填充。
示例:strncpy 的使用
#include <stdio.h> #include <string.h> int main() { char arr1[20] = {0}; char arr2[] = "abcdefghi"; char* str = strncpy(arr1, arr2, 5); printf("%s\n", arr1); // 输出:abcde printf("%s\n", str); // 输出:abcde return 0; }八、strncat 函数
strcat 的安全版本,可指定追加的最大字符数,需要包含头文件 <string.h>。
函数原型
char *strncat(char *destination, const char *source, size_t num);示例:strncat 的使用
#include <stdio.h> #include <string.h> int main() { char arr1[20] = "hello "; char arr2[] = "world"; char* str = strncat(arr1, arr2, 5); printf("%s\n", arr1); // 输出:hello world printf("%s\n", str); // 输出:hello world return 0; }九、strncmp 函数
strcmp 的安全版本,可指定比较的最大字符数,需要包含头文件 <string.h>。
函数原型
int strncmp(const char *str1, const char *str2, size_t num);示例:strncmp 的使用
#include <stdio.h> #include <string.h> int main() { char arr1[] = "abcdef"; char arr2[] = "abcqw"; int ret1 = strncmp(arr1, arr2, 3); printf("%d\n", ret1); // 输出:0(前3个字符相等) int ret2 = strncmp(arr1, arr2, 4); printf("%d\n", ret2); // 输出:负数('d' < 'q') return 0; }十、strstr 函数
用于在字符串中查找子字符串首次出现的位置,需要包含头文件 <string.h>。
函数原型
char *strstr(const char *str1, const char *str2);返回值
• 找到子串:返回首次出现位置的指针
• 未找到:返回 NULL
示例:strstr 的使用
#include <stdio.h> #include <string.h> int main() { char str[] = "This is a simple string"; char *pch; pch = strstr (str,"simple"); if (pch != NULL) printf("%s\n", pch); // 输出:simple string else printf("查找的字符串不存在\n"); return 0; }模拟实现(暴力匹配
char * 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); }十一、strtok 函数
用于分割字符串(根据分隔符拆分),需要包含头文件 <string.h>。
函数原型
char *strtok(char *str, const char *delim);核心要点
• 首次调用:传入待分割字符串和分隔符。
• 后续调用:传入 NULL 和相同分隔符,继续分割。
• 会修改原字符串(将分隔符替换为 \0),建议先拷贝原串。
示例:strtok 的使用(分割IP地址)
#include <stdio.h> #include <string.h> int main() { char arr[] = "192.168.6.111"; const char* sep = "."; const char* str = NULL; char buf[30] = {0}; strcpy(buf, arr); for (str = strtok(buf, sep); str != NULL; str = strtok(NULL, sep)) { printf("%s\n", str); } return 0; }十二、strerror 与 perror 函数
用于获取和打印错误信息,需要包含头文件 <string.h> 和 <errno.h>。
1. strerror
char* strerror(int errnum);头文件依赖
• <string.h>:提供 strerror 函数声明
• <errno.h>:提供全局错误码变量 errno 的定义
核心功能
1. 错误码转字符串
接收一个整数错误码 errnum,返回该错误码对应的人类可读的错误信息字符串首地址。
2. 仅处理标准库错误码
该函数只针对 C 标准库函数执行失败后设置的错误码进行转换。
3. 全局错误码 errno 的配合
C 程序启动时,全局变量 errno 初始化为 0(表示无错误)。当标准库函数调用出错时,系统会自动将对应的错误码存入 errno,再通过 strerror(errno) 即可获取错误描述。
参数与返回值
| 部分 | 说明 |
| 参数 errnum | 传入的错误码,通常是全局变量 errno 的值 |
| 返回值 | 指向错误信息字符串的首字符地址,该字符串由系统维护,无需手动释放 |
示例:strerror 的使用
#include <errno.h> // 包含系统错误码的宏定义(如EINVAL、ENOENT等) #include <string.h> // 包含strerror()函数的声明,用于错误码转字符串 #include <stdio.h> // 包含printf()函数的声明,用于控制台输出 int main() { // 循环遍历错误码0~10 for (int i = 0; i <= 10; i++) { // 打印:错误码 + 对应的错误描述 printf("%d: %s\n", i, strerror(i)); } return 0; }关键函数说明
char *strerror(int errnum):
• 接收一个整数错误码作为参数,返回该错误码对应的人类可读的错误描述字符串。
• 错误码0通常表示无错误(Success),不同系统(Linux/Windows)对1~10的错误码定义略有差异。
Windows 11 + VS2022 环境运行结果
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常见应用场景
在文件操作、网络通信等场景中,结合 errno 和 strerror 可以快速定位问题:
#include <errno.h> #include <string.h> #include <stdio.h> int main() { // 尝试打开一个不存在的文件 FILE *fp = fopen("non_exist.txt", "r"); if (fp == NULL) { // 打印错误码和对应的错误信息 printf("文件打开失败,错误码:%d,原因:%s\n", errno, strerror(errno)); return -1; } fclose(fp); return 0; }运行结果示例:
文件打开失败,错误码:2,原因:No such file or directory核心逻辑
• 当 fopen 打开不存在的文件时,会将全局错误码变量 errno 设置为 2。
• strerror(errno) 会将错误码 2 转换为对应的错误描述字符串:No such file or directory。
• 该方式的优势是可以灵活控制错误信息的输出格式。
跨平台说明
• Linux 与 Windows 对部分错误码的定义不同,例如:
◦ 错误码 5 在 Linux 中是 Input/output error,在 Windows 中是 Access is denied
• 实际开发中,建议以当前运行环境的输出为准
2. perror
void perror(const char *str);头文件依赖
• <stdio.h>:提供 perror 函数声明。
核心功能
• perror 会直接打印错误信息,无需额外调用 printf。
• 它会先打印参数 str 中的字符串,然后自动添加冒号和空格,最后输出 errno 对应的错误描述。
示例:perror 的使用
#include <stdio.h> #include <string.h> #include <errno.h> int main() { FILE *pFile = NULL; pFile = fopen ("unexist.ent", "r"); if (pFile == NULL) { perror("错误信息是"); return 1; } return 0; }运行输出:
错误信息是: No such file or directorystrerror 与 perror 对比
| 特性 | strerror | perror |
| 返回值 | 返回错误信息字符串的首地址 | 无返回值,直接打印 |
| 输出方式 | 需要配合 printf 等函数输出 | 自动完成打印 |
| 灵活性 | 高,可自定义输出格式 | 低,输出格式固定 |
| 适用场景 | 需要对错误信息进行二次处理时 | 快速打印错误信息时 |
常见应用场景
场景1:文件操作错误排查
FILE *fp = fopen("data.txt", "r"); if (fp == NULL) { perror("文件打开失败"); return -1; }输出:
文件打开失败: No such file or directory场景2:自定义错误信息格式
FILE *fp = fopen("data.txt", "r"); if (fp == NULL) { printf("[ERROR] 打开文件失败,原因:%s\n", strerror(errno)); return -1; }输出:
[ERROR] 打开文件失败,原因:No such file or directoryC语言的字符串与字符处理函数,看似基础却贯穿了开发的各个场景。从字符的类型判断到字符串的复杂操作,再到错误信息的精准定位,这些工具函数正是C语言灵活性与高效性的体现。
熟练掌握它们不仅能让你写出更健壮的代码,更能在调试与问题定位时事半功倍。希望本文的梳理能成为你开发路上的实用手册,也期待你在实践中探索出更多巧妙的用法。