当前位置: 首页 > news >正文

C语言字符函数和字符串函数全解析:从使用到模拟实现

在C语言编程中,字符和字符串的处理是非常核心的操作。为了简化这些操作,C语言标准库提供了一系列实用的字符函数和字符串函数。本文将全面讲解这些函数的使用方法、注意事项,并通过模拟实现帮助大家深入理解其底层逻辑。

一、字符分类函数

字符分类函数用于判断字符的类型(如是否为数字、字母、空白字符等),这些函数都声明在 <ctype.h> 头文件中。

常用字符分类函数列表

函数名功能描述
iscntrl判断是否为控制字符(如换行符、制表符)
isspace判断是否为空白字符(空格、换页\f、换行\n、回车\r、制表符\t、垂直制表符\v
isdigit判断是否为十进制数字(0~9)
isxdigit判断是否为十六进制数字(09、af、A~F)
islower判断是否为小写字母(a~z)
isupper判断是否为大写字母(A~Z)
isalpha判断是否为字母(az或AZ)
isalnum判断是否为字母或数字
ispunct判断是否为标点符号(非字母/数字的可打印图形字符)
isgraph判断是否为图形字符(可打印且非空白)
isprint判断是否为可打印字符(含图形字符和空白)

函数特点

所有字符分类函数的参数为 int c(字符的ASCII码),返回值为:

  • 非0整数:表示满足条件(真)
  • 0:表示不满足条件(假)

示例:使用islower判断小写字母

下面代码将字符串中的小写字母转为大写,其他字符不变:

#include <stdio.h>
#include <ctype.h>  // 包含字符分类函数声明int main() 
{int i = 0;char str[] = "Test String.\n";char c;while (str[i]) {  // 遍历字符串直到'\0'c = str[i];if (islower(c))  // 判断是否为小写字母c -= 32;  // 小写转大写(ASCII码差值)putchar(c);i++;}return 0;
}

输出结果:TEST STRING.

二、字符转换函数

C语言提供了两个常用的字符转换函数,同样声明在 <ctype.h> 中:

函数名功能描述
tolower(int c)将大写字母转为小写(非大写字母返回原字符)
toupper(int c)将小写字母转为大写(非小写字母返回原字符)

示例:使用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;
}

输出结果:TEST STRING.

三、字符串长度函数strlen

strlen用于计算字符串的长度(不含结束符\0),声明在 <string.h> 中。

函数原型

size_t strlen(const char *str);  // size_t是无符号整数类型

关键特性

  1. 字符串必须以\0结尾,否则结果不确定;
  2. 返回值为size_t(无符号),注意比较时的坑(见示例);
  3. 计算的是\0之前的字符个数。

使用示例

#include <stdio.h>
#include <string.h>int main() 
{const char* str1 = "abcdef";  // 长度6const char* str2 = "bbb";     // 长度3// 易错点:无符号数相减if (strlen(str2) - strlen(str1) > 0)  // 3-6 = -3,无符号下为大正数printf("str2>str1\n");  // 实际会执行这里elseprintf("str1>str2\n");return 0;
}

输出结果:str2>str1(因无符号数特性导致的逻辑错误)

模拟实现strlen

方式1:计数器法
#include <stdio.h>
#include <assert.h>  // 用于断言int my_strlen(const char* str) 
{assert(str != NULL);  // 确保指针非空int count = 0;while (*str) {  // 当*str为'\0'时终止count++;str++;  // 指针后移}return count;
}
方式2:递归法(无临时变量)
int my_strlen(const char* str) 
{assert(str);if (*str == '\0')return 0;elsereturn 1 + my_strlen(str + 1);  // 递归累加
}
方式3:指针-指针法
int my_strlen(const char* str) 
{assert(str);const char* start = str;  // 记录起始地址while (*start != '\0')start++;  // 移动到'\0'位置return start - str;  // 地址差值即长度
}

四、字符串拷贝函数strcpy

strcpy用于将源字符串拷贝到目标空间,声明在 <string.h> 中。

函数原型

char* strcpy(char* destination, const char* source);

关键特性

  1. 源字符串必须以\0结尾,且会将\0一同拷贝;
  2. 目标空间必须足够大(能容纳源字符串);
  3. 目标空间必须可修改(不能是常量字符串);
  4. 返回目标空间的起始地址。

模拟实现strcpy

#include <stdio.h>
#include <assert.h>char* my_strcpy(char* dest, const char* src) 
{assert(dest != NULL && src != NULL);  // 检查指针有效性char* ret = dest;  // 保存目标起始地址// 循环拷贝,直到拷贝'\0'后终止while ((*dest++ = *src++)) {;  // 空语句,逻辑在表达式中完成}return ret;  // 返回目标地址
}

五、字符串追加函数strcat

strcat用于将源字符串追加到目标字符串末尾,声明在 <string.h> 中。

函数原型

char* strcat(char* destination, const char* source);

关键特性

  1. 源字符串必须以\0结尾;
  2. 目标字符串必须以\0结尾(否则无法确定追加起始位置);
  3. 目标空间必须足够大(容纳原目标+源字符串);
  4. 目标空间必须可修改;
  5. 字符串不能自己追加自己(会覆盖\0导致死循环)。

模拟实现strcat

#include <stdio.h>
#include <assert.h>char* my_strcat(char* dest, const char* src) 
{assert(dest && src);char* ret = dest;// 1. 移动目标指针到'\0'位置while (*dest) {dest++;}// 2. 拷贝源字符串到目标末尾(含'\0')while ((*dest++ = *src++)) {;}return ret;
}

六、字符串比较函数strcmp

strcmp用于比较两个字符串的大小,声明在 <string.h> 中。

函数原型

int strcmp(const char* str1, const char* str2);

比较规则

  1. 从第一个字符开始逐个比较ASCII码值;
  2. 若字符不同,返回差值(str1字符 - str2字符);
  3. 若所有字符相同且都到\0,返回0;
  4. 标准规定:
    • 返回>0:str1 > str2
    • 返回=0:str1 == str2
    • 返回<0:str1 < str2

模拟实现strcmp

#include <stdio.h>
#include <assert.h>int my_strcmp(const char* str1, const char* str2) 
{assert(str1 && str2);// 比较字符,直到不同或遇到'\0'while (*str1 == *str2) {if (*str1 == '\0')  // 都到'\0',相等return 0;str1++;str2++;}// 返回差值return *str1 - *str2;
}

七、受限字符串拷贝函数strncpy

strncpystrcpy的安全版本,可指定拷贝的字符数,声明在 <string.h> 中。

函数原型

char* strncpy(char* destination, const char* source, size_t num);

关键特性

  1. 拷贝num个字符从源字符串到目标空间;
  2. 若源字符串长度 < num:拷贝完源字符串后,目标空间剩余部分补\0num个;
  3. 若源字符串长度 ≥ num:只拷贝前num个字符(不自动补\0)。

模拟实现strncpy

#include <stdio.h>
#include <assert.h>char* my_strncpy(char* dest, const char* src, size_t num) 
{assert(dest && src);char* ret = dest;// 拷贝前num个字符或直到源字符串结束while (num > 0 && *src != '\0') {*dest++ = *src++;num--;}// 若还有剩余num,补'\0'while (num > 0) {*dest++ = '\0';num--;}return ret;
}

八、受限字符串追加函数strncat

strncatstrcat的安全版本,可指定追加的字符数,声明在 <string.h> 中。

函数原型

char* strncat(char* destination, const char* source, size_t num);

关键特性

  1. 追加源字符串的前num个字符到目标字符串末尾;
  2. 无论是否追加满num个字符,最终都会在目标字符串末尾补一个\0
  3. 若源字符串长度 < num:只追加源字符串到\0的部分,再补\0

使用示例

#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 notreturn 0;
}

模拟实现strncat

#include <stdio.h>
#include <assert.h>char* my_strncat(char* dest, const char* src, size_t num) 
{assert(dest && src);char* ret = dest;// 1. 移动到目标字符串的'\0'位置while (*dest) {dest++;}// 2. 追加前num个字符(或源字符串结束)while (num > 0 && *src != '\0') {*dest++ = *src++;num--;}// 3. 必须补'\0'*dest = '\0';return ret;
}

九、受限字符串比较函数strncmp

strncmp用于比较两个字符串的前num个字符,声明在 <string.h> 中。

函数原型

int strncmp(const char* str1, const char* str2, size_t num);

关键特性

  1. 比较前num个字符,或直到遇到\0
  2. 若前num个字符都相同,返回0;
  3. 若中途字符不同,返回差值(str1字符 - str2字符)。

模拟实现strncmp

#include <stdio.h>
#include <assert.h>int my_strncmp(const char* str1, const char* str2, size_t num) 
{assert(str1 && str2);// 比较前num个字符while (num > 0) {if (*str1 != *str2) {return *str1 - *str2;  // 字符不同,返回差值}if (*str1 == '\0') {break;  // 已到字符串末尾}str1++;str2++;num--;}return 0;  // 前num个字符相同
}

十、字符串查找函数strstr

strstr用于查找子字符串在母字符串中第一次出现的位置,声明在 <string.h> 中。

函数原型

char* strstr(const char* str1, const char* str2);  // str1:母串,str2:子串

关键特性

  1. 若找到子串,返回子串在母串中第一次出现的起始地址;
  2. 若未找到,返回NULL
  3. 若子串为空(*str2 == '\0'),返回母串起始地址。

使用示例

#include <stdio.h>
#include <string.h>int main() 
{char str[] = "This is a simple string";char* pch = strstr(str, "simple");  // 查找"simple"if (pch) {strncpy(pch, "sample", 6);  // 替换为"sample"printf("%s\n", str);  // 输出:This is a sample string}return 0;
}

模拟实现strstr

#include <stdio.h>
#include <assert.h>char* my_strstr(const char* str1, const char* str2) 
{assert(str1 && str2);// 子串为空,返回母串if (*str2 == '\0') {return (char*)str1;}const char* cp = str1;  // 母串起始指针while (*cp) {const char* s1 = cp;  // 母串当前比较位置const char* s2 = str2;  // 子串起始位置// 匹配子串while (*s1 && *s2 && *s1 == *s2) {s1++;s2++;}// 若子串已匹配完,返回当前cp位置if (*s2 == '\0') {return (char*)cp;}cp++;  // 母串指针后移继续查找}return NULL;  // 未找到
}

十一、字符串分割函数strtok

strtok用于按指定分隔符分割字符串,声明在 <string.h> 中。

函数原型

char* strtok(char* str, const char* sep);  // sep:分隔符集合

关键特性

  1. sep是分隔符集合(如".,"表示.,都是分隔符);
  2. 第一次调用时,str为待分割字符串,函数返回第一个标记的地址;
  3. 后续调用需将str设为NULL,函数从上次保存的位置继续查找下一个标记;
  4. 函数会修改原字符串(将分隔符替换为\0),建议使用临时拷贝;
  5. 无更多标记时返回NULL

使用示例

#include <stdio.h>
#include <string.h>int main() {char arr[] = "192.168.6.111";  // 原字符串(可修改)char* sep = ".";  // 分隔符为.char* str = NULL;// 循环分割字符串for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep)) {printf("%s\n", str);  // 依次输出每个IP段}return 0;
}

输出结果:

192
168
6
111

十二、错误信息函数strerror

strerror用于获取错误码对应的描述信息,声明在 <string.h> 中。

函数原型

char* strerror(int errnum);  // errnum:错误码

关键特性

  1. C语言用全局变量errno(声明在 <errno.h>)记录错误码;
  2. 程序启动时errno为0(无错误);
  3. 库函数出错时会设置errnostrerror将其转为可读字符串。

使用示例

#include <stdio.h>
#include <string.h>
#include <errno.h>  // 包含errno定义int main() {// 打印0~10错误码对应的信息for (int i = 0; i <= 10; i++) {printf("%d: %s\n", i, strerror(i));}// 文件操作错误示例FILE* pFile = fopen("unexist.txt", "r");  // 打开不存在的文件if (pFile == NULL) {printf("Error: %s\n", strerror(errno));  // 输出错误信息}return 0;
}

扩展:perror函数

perror可直接打印错误信息,等价于printf("xxx: %s\n", strerror(errno))

#include <stdio.h>
#include <errno.h>int main() {FILE* pFile = fopen("unexist.txt", "r");if (pFile == NULL) {perror("Open file failed");  // 自动拼接错误信息}return 0;
}

输出结果:Open file failed: No such file or directory

总结

本文详细讲解了C语言中常用的字符函数和字符串函数,包括功能、使用注意事项及模拟实现。掌握这些函数的核心逻辑不仅能提高编程效率,还能帮助大家规避潜在的bug(如无符号数比较、字符串越界等)。实际开发中,建议优先使用标准库函数,但理解其底层实现能让你在遇到问题时更快定位原因。

希望本文对你的C语言学习有所帮助!如果有疑问或补充,欢迎在评论区交流~

http://www.dtcms.com/a/311138.html

相关文章:

  • MyBatis与MySQL
  • 【安装教程】Docker Desktop 安装与使用教程
  • 从毫秒到真义:构建工业级RAG系统的向量检索优化指南
  • Python爬虫实战:研究mahotas库,构建图像获取及处理系统
  • (思维)洛谷 P13551 ももいろの鍵 题解
  • 位菜:仪式锚与价值符
  • 24黑马SpringCloud的Docker本地目录挂载出现相关问题解决
  • 【图像处理基石】用Python实现基础滤镜效果
  • LLM中Function Call的原理及应用
  • 工业仪表盘识别误检率↓79%!陌讯多模态融合算法在设备巡检中的落地优化​
  • 安全光幕Muting功能程序逻辑设计
  • [mssql] 分析SQL Server中执行效率较低的SQL语句
  • Git、Gitee、GitHub、GitLab完整讲解:从基础到进阶
  • GaussDB 约束的使用举例
  • Leetcode-141.环形链表
  • Rust: 工具链版本更新
  • Linux网络-------4.传输层协议UDP/TCP-----原理
  • Ajax——异步前后端交互提升OA系统性能体验
  • python chardet文本编码识别代码
  • Java单元测试和设计模式
  • uniapp 富文本rich-text 文本首行缩进和图片居中
  • Day 29: 复习
  • 音视频学习(四十五):声音的产生
  • 数据集-目标检测系列- 地球仪 数据集 globe>> DataBall
  • 前端应用权限设计面面观
  • 网络基础实操篇-05-路由基础-最佳实践
  • 【机器学习】非线性分类算法(上):KNN(基于距离相似度)与朴素(特征独立)贝叶斯(基于概率统计)
  • MybatisPlus-逻辑删除
  • 【ElementPlus】深入探索ElementPlus:前端界面的全能组件库
  • 【Conda】配置Conda镜像源