

前言
在C语言编程中,字符和字符串是最基础也最常用的数据类型,从文本解析、数据处理、输入输出等场景几乎都离不开对字符/字符串的操作。为了简化开发,C语言标准库封装了一系列专用处理函数,这些函数分布在ctype.h(字符操作)、string.h(字符串操作)、errno.h(错误处理)等头文件中。本文将系统梳理这些核心函数,从基本使用 、注意事项 到手动模拟实现,帮你彻底掌握这些高频函数。
一、字符操作函数
字符操作函数主要集中在ctype.h头文件中,分为"分类"和"转换"两类,是处理单个字符的基础。
1.1 字符分类函数
字符分类函数用于判断字符的类型(如是否为小写字母、数字、字母数字等),所有分类函数的返回规则一致:符合条件返回非0整数,不符合返回0。核心函数如下(附专属示例):
| 函数 | 判断条件 |
|---|---|
islower |
小写字母(a~z) |
isalnum |
字母(az/AZ)或数字(0~9) |
isgraph |
任何图形字符(非控制字符、非空格) |
示例1:islower(判断小写字母)
功能:筛选字符串中的所有小写字母并打印
c
#include <stdio.h>
#include <ctype.h>
int main()
{
char str[] = "Hello123! 世界";
int i = 0;
printf("字符串中的小写字母:");
while (str[i] != '\0')
{
if (islower(str[i])) // 仅匹配小写字母a~z
{
putchar(str[i]);
}
i++;
}
printf("\n");
return 0;
}
// 输出:字符串中的小写字母:ello
输出结果
字符串中的小写字母:ello
示例2:isalnum(判断字母或数字)
功能:统计字符串中字母和数字的总个数
c
#include <stdio.h>
#include <ctype.h>
int main()
{
char str[] = "C语言2025!@# 编程";
int count = 0, i = 0;
while (str[i] != '\0')
{
if (isalnum(str[i])) // 匹配字母(大小写)或数字
{
count++;
}
i++;
}
printf("字母和数字的总个数:%d\n", count);
return 0;
}
// 输出:字母和数字的总个数:5(仅识别ASCII码中的C、2、0、2、5,中文非ASCII不识别)
输出结果
字母和数字的总个数:5
示例3:isgraph(判断图形字符)
功能:筛选字符串中的图形字符(排除空格、控制字符)
c
#include <stdio.h>
#include <ctype.h>
int main()
{
char str[] = "Test \t String123! \n"; // 包含空格、制表符\t、换行符\n
int i = 0;
printf("字符串中的图形字符:");
while (str[i] != '\0')
{
if (isgraph(str[i])) // 匹配可显示的图形字符(非空格、非控制字符)
{
putchar(str[i]);
}
i++;
}
printf("\n");
return 0;
}
// 输出:字符串中的图形字符:TestString123!
输出结果
字符串中的图形字符:TestString123!
1.2 字符转换函数
C语言提供两个专用转换函数,替代手动修改ASCII码的方式,更易读、不易错:
tolower(int c):大写字母转小写toupper(int c):小写字母转大写
C以下是两种转换场景的完整示例:
示例1:toupper(小写字母转大写)
功能:将字符串中所有小写字母转为大写,其他字符保持不变
c
#include <stdio.h>
#include <ctype.h>
int main()
{
char str[] = "Hello World! 2025 编程";
int i = 0;
printf("转换后的字符串:");
while (str[i] != '\0')
{
// 若为小写字母则转大写,否则直接输出原字符
putchar(islower(str[i]) ? toupper(str[i]) : str[i]);
i++;
}
printf("\n");
return 0;
}
// 输出:转换后的字符串:HELLO WORLD! 2025 编程
输出结果
转换后的字符串:HELLO WORLD! 2025 编程
示例2:tolower(大写字母转小写)
功能:将字符串中所有大写字母转为小写,其他字符保持不变
c
#include <stdio.h>
#include <ctype.h>
int main()
{
char str[] = "C LANGUAGE IS FUN! 987";
int i = 0;
printf("转换后的字符串:");
while (str[i] != '\0')
{
// 若为大写字母则转小写,否则直接输出原字符
putchar(isupper(str[i]) ? tolower(str[i]) : str[i]);
i++;
}
printf("\n");
return 0;
}
// 输出:转换后的字符串:c language is fun! 987
输出结果
转换后的字符串:c language is fun! 987
二、字符串长度:strlen的使用与模拟实现
strlen是最基础的字符串函数,用于获取字符串长度,核心规则和实现逻辑是理解字符串的关键。
2.1 函数基础
- 原型:
-
size_t strlen(const char *str); - 核心规则:
- 字符串以
'\0'为结束标志,返回'\0'前的字符个数(不包含'\0')。 - 参数必须指向以
'\0'结束的字符串 。 - 返回值
size_t是无符号整数(易错点:减法可能出现"负数变正数")。
- 字符串以
使用示例:统计字符串长度
c
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "Hello C!"; // 包含'\0'的正常字符串
char str2[] = {'H', 'i'}; // 无'\0'的字符串(危险)
char str3[] = ""; // 空字符串(仅含'\0')
printf("str1长度:%zu\n", strlen(str1)); // 输出:7(H e l l o 空格 C)
printf("str2长度:%zu\n", strlen(str2)); // 输出:随机值(直到内存中遇到'\0')
printf("str3长度:%zu\n", strlen(str3)); // 输出:0
return 0;
}
易错示例:无符号返回值的坑
c
#include <stdio.h>
#include <string.h>
int main()
{
const char* str1 = "abcdef"; // 长度6
const char* str2 = "bbb"; // 长度3
if(strlen(str2) - strlen(str1) > 0) // 3-6=-3,但无符号下是极大正数
printf("str2>str1\n"); // 实际会执行这行
else
printf("str1>str2\n");
return 0;
}
// 输出:str2>str1
2.2 模拟实现strlen
掌握模拟实现能理解底层逻辑,推荐以下3种方式:
方式1:计数器法(最直观)
c
#include <assert.h>
int my_strlen(const char *str)
{
assert(str); // 防止空指针
int count = 0;
while(*str) // 直到遇到'\0'停止
{
count++;
str++;
}
return count;
}
方式2:递归法(无临时变量)
c
#include <assert.h>
int my_strlen(const char *str)
{
assert(str);
if(*str == '\0')
return 0;
else
return 1 + my_strlen(str+1); // 递归遍历每个字符
}
方式3:指针-指针法(高效)
c
#include <assert.h>
int my_strlen(char *s)
{
assert(s);
char *p = s;
while(*p != '\0')
p++;
return p - s; // 同类型指针相减=元素个数
}
三、字符串拷贝:strcpy与strncpy
字符串拷贝是高频操作,strcpy是基础版,strncpy是更安全的限定长度版。
3.1 strcpy(无长度限制拷贝)
- 原型:
-
char* strcpy(char *destination, const char *source); - 核心规则:
- 源字符串必须以
'\0'结束; - 会将源字符串的
'\0'一并拷贝到目标空间; - 目标空间需足够大且可修改(不能是常量字符串)。
- 源字符串必须以
使用示例:拷贝字符串
c
#include <stdio.h>
#include <string.h>
int main()
{
char dest[20]; // 目标空间足够大
const char src[] = "I love C!";
strcpy(dest, src); // 将src拷贝到dest
printf("拷贝结果:%s\n", dest); // 输出:I love C!
return 0;
}
模拟实现strcpy:
c
#include <assert.h>
char *my_strcpy(char *dest, const char* src)
{
assert(dest != NULL && src != NULL); // 校验空指针
char *ret = dest; // 保存目标起始地址(用于返回)
while((*dest++ = *src++)) // 拷贝直到'\0'(赋值表达式为0时停止)
{
;
}
return ret; // 返回目标地址,支持链式调用
}
3.2 strncpy(限定长度拷贝)
- 原型:
-
char *strncpy(char *destination, const char *source, size_t num); - 核心规则:
- 仅拷贝源字符串的前
num个字符 。 - 若源字符串长度 <
num,拷贝完源字符串后,目标空间剩余位置补'\0'至num个。
- 仅拷贝源字符串的前
使用示例:限定长度拷贝
c
#include <stdio.h>
#include <string.h>
int main()
{
char dest[10];
const char src1[] = "HelloWorld"; // 长度10(含'\0')
const char src2[] = "Hi"; // 长度2(含'\0')
// 拷贝src1的前5个字符到dest
strncpy(dest, src1, 5);
dest[5] = '\0'; // 手动补'\0'(避免源字符串无'\0'时乱码)
printf("dest1:%s\n", dest); // 输出:Hello
// 拷贝src2的前5个字符(src2长度不足,剩余位置补'\0')
strncpy(dest, src2, 5);
printf("dest2:%s\n", dest); // 输出:Hi(后续隐含'\0')
return 0;
}
四、字符串追加:strcat与strncat
strcat用于将源字符串追加到目标字符串末尾,strncat是限定长度的安全版。
4.1 strcat(无长度限制追加)
- 原型:
-
char *strcat(char *destination, const char *source); - 核心规则:
- 源、目标字符串都必须以
'\0'结束。 - 目标空间需足够大,能容纳追加后的全部内容。
- 不支持"字符串自追加"(会覆盖自身
'\0',导致死循环)。
- 源、目标字符串都必须以
使用示例:追加字符串
c
#include <stdio.h>
#include <string.h>
int main()
{
char dest[30] = "Hello, "; // 目标字符串需提前初始化(含'\0')
const char src[] = "C programmer!";
strcat(dest, src); // 将src追加到dest末尾
printf("追加结果:%s\n", dest); // 输出:Hello, C programmer!
return 0;
}
模拟实现strcat:
c
#include <assert.h>
char *my_strcat(char *dest, const char* src)
{
assert(dest != NULL && src != NULL);
char *ret = dest;
// 1. 找到目标字符串的'\0'位置
while(*dest)
{
dest++;
}
// 2. 拷贝源字符串到目标末尾(同strcpy逻辑)
while((*dest++ = *src++))
{
;
}
return ret;
}
4.2 strncat(限定长度追加)
- 原型:
char *strncat(char *destination, const char *source, size_t num); - 核心规则:仅追加源字符串的前
num个字符,且会在追加完成后自动补'\0'(即使源字符串长度不足num)。
使用示例:限定长度追加
c
#include <stdio.h>
#include <string.h>
int main()
{
char str1[20];
char str2[20];
strcpy(str1,"To be ");
strcpy(str2,"or not to be");
strncat(str1, str2, 6); // 追加str2前6个字符:"or not"
printf("%s\n", str1); // 输出:To be or not
return 0;
}
五、字符串比较:strcmp与strncmp
字符串比较不能直接用==(比较的是地址),需用strcmp/strncmp比较字符ASCII码。
5.1 strcmp(全长度比较)
- 原型:
-
int strcmp(const char *str1, const char *str2); - 标准返回规则:
- str1 > str2:返回大于0的数。
- str1 == str2:返回0。
- str1 < str2:返回小于0的数。
- 比较逻辑:逐字符对比ASCII码,直到出现不同字符或遇到
'\0'。
使用示例:比较两个字符串
c
#include <stdio.h>
#include <string.h>
int main()
{
const char str1[] = "apple";
const char str2[] = "app";
const char str3[] = "banana";
const char str4[] = "apple";
printf("strcmp(str1, str2):%d\n", strcmp(str1, str2)); // 输出:>0(str1长于str2且前3字符相等)
printf("strcmp(str1, str3):%d\n", strcmp(str1, str3)); // 输出:<0('a'ASCII < 'b')
printf("strcmp(str1, str4):%d\n", strcmp(str1, str4)); // 输出:0(完全相等)
return 0;
}
模拟实现strcmp:
c
#include <assert.h>
int my_strcmp(const char *str1, const char *str2)
{
assert(str1 != NULL && str2 != NULL);
while(*str1 == *str2)
{
if(*str1 == '\0') // 全部字符相等且到末尾
return 0;
str1++;
str2++;
}
return *str1 - *str2; // 返回差值(正负对应大小)
}
5.2 strncmp(限定长度比较)
- 原型:
-
int strncmp(const char *str1, const char *str2, size_t num); - 核心规则:仅比较前
num个字符,若提前出现不同字符则停止,否则返回0。
使用示例:限定长度比较字符串
c
#include <stdio.h>
#include <string.h>
int main()
{
const char str1[] = "HelloWorld";
const char str2[] = "HelloCoder";
const char str3[] = "HiThere";
// 比较前5个字符("Hello" vs "Hello")
printf("strncmp(str1, str2, 5):%d\n", strncmp(str1, str2, 5)); // 输出:0
// 比较前2个字符("He" vs "Hi")
printf("strncmp(str1, str3, 2):%d\n", strncmp(str1, str3, 2)); // 输出:<0('e' < 'i')
return 0;
}
六、字符串查找:strstr的使用与模拟实现
strstr用于查找子串在主串中首次出现的位置,是文本解析的核心函数。
6.1 函数基础
- 原型:
-
char *strstr(const char *str1, const char *str2); - 规则:返回str2在str1中首次出现的指针;若str2不是str1的子串,返回NULL(匹配不包含
'\0')。
使用示例:查找子串位置
c
#include <stdio.h>
#include <string.h>
int main()
{
char str[] ="This is a simple string";
char *pch;
pch = strstr(str,"simple"); // 找到"simple"的起始位置
if (pch != NULL)
{
strncpy(pch,"sample",6); // 替换为"sample"
printf("%s\n", str); // 输出:This is a sample string
}
else
{
printf("未找到子串\n");
}
return 0;
}
6.2 模拟实现strstr
c
#include <assert.h>
char *my_strstr(const char *str1, const char *str2)
{
assert(str1 && 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的使用
strtok用于按指定分隔符分割字符串,是日志解析、IP处理等场景的常用工具。
7.1 函数核心规则
- 原型:
-
char *strtok(char *str, const char *sep); - 关键特性:
sep是分隔符集合(如".,-"表示分隔符为.、,、-)。- 首次调用时
str传需分割的字符串,后续调用传NULL(函数会保存上次分割的位置)。 - 会修改原字符串(将分隔符替换为
'\0'),建议使用临时拷贝的字符串。
使用示例(分割IP地址)
c
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "192.168.6.111";
char* sep = ".";
char* str = NULL;
// 循环分割:首次传arr,后续传NULL
for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\n", str); // 依次输出:192、168、6、111
}
return 0;
}
扩展示例(分割多分隔符字符串)
c
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "name:zhangsan,age:20;city:beijing";
char* sep = ":,;"; // 分隔符集合:冒号、逗号、分号
char* str = NULL;
for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\n", str); // 输出:name、zhangsan、age、20、city、beijing
}
return 0;
}
八、错误信息处理:strerror与perror
编程中经常需要处理错误(如文件打开失败),strerror和perror用于将错误码转为可读的错误信息。
8.1 strerror(错误码转字符串)
- 原型:
-
char *strerror(int errnum); - 核心:C语言用
errno(全局变量,头文件errno.h)保存错误码,strerror将错误码转为对应的描述字符串。
使用示例:打印文件打开失败的错误信息
c
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
FILE *pFile;
pFile = fopen("unexist.ent","r"); // 打开不存在的文件
if (pFile == NULL)
printf("Error: %s\n", strerror(errno)); // 输出:Error: No such file or directory
return 0;
}
8.2 perror(简化版错误打印)
perror是strerror的简化版,直接打印自定义提示+错误信息:
使用示例:简化错误打印
c
#include <stdio.h>
#include <errno.h>
int main()
{
FILE *pFile;
pFile = fopen("unexist.ent","r");
if (pFile == NULL)
perror("Error opening file"); // 输出:Error opening file: No such file or directory
return 0;
}
扩展示例:打印指定错误码信息
c
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
// 打印0~5号错误码对应的信息
for (int i = 0; i <= 5; i++)
{
printf("错误码%d:%s\n", i, strerror(i));
}
return 0;
}
// 输出(Windows环境):
// 错误码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
C语言字符/字符串函数是编程基础中的基础,这些函数看似简单,但细节(如无符号返回值、原字符串修改、空指针校验)容易出错,建议多写示例代码,结合实际场景(如文本解析、文件处理)练习,才能真正做到熟练运用。
至此,我们已梳理完"字符函数和字符串函数"的全部内容了。最后我们在文末来进行一个投个票,告诉我你对哪部分内容最感兴趣、收获最大,也欢迎在评论区聊聊你的学习感受。
以上就是本期博客的全部内容了,感谢各位的阅读以及关注。如有内容存在疏漏或不足之处,恳请各位技术大佬不吝赐教、多多指正。

