尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

C语言字符与字符串函数

C语言字符与字符串函数 系列文章目录第九章 操作符详解第十章 C语言指针上第十一章 C语言指针中第十二章 C语言指针下1. 引言在C语言编程中字符和字符串的处理是日常开发中最基础、最频繁的操作之一。C语言标准库提供了一系列功能强大的字符函数和字符串函数它们定义在ctype.h和string.h头文件中。掌握这些函数的使用方法、内部原理以及如何模拟实现它们不仅能帮助我们更高效地编写代码还能加深对C语言底层机制的理解。本文将系统性地介绍C语言中常用的字符分类函数、字符转换函数以及strlen、strcpy、strcat、strcmp、strncpy、strncat、strncmp、strstr、strtok、strerror等核心字符串函数。对于每个函数我们都会详细讲解其功能、参数、返回值并给出典型的使用示例。更重要的是我们将深入探讨这些函数的模拟实现揭示其底层逻辑帮助你从“使用者”转变为“创造者”。2. 字符分类函数ctype.h字符分类函数用于判断一个字符是否属于特定的类别例如是否为数字、字母、空格等。这些函数接受一个int类型的参数字符的ASCII码值返回一个非零值真或0假。2.1 常用函数列表函数原型功能描述int isalnum(int c);检查字符是否为字母或数字int isalpha(int c);检查字符是否为字母int iscntrl(int c);检查字符是否为控制字符int isdigit(int c);检查字符是否为十进制数字int isgraph(int c);检查字符是否为图形字符除空格外的可打印字符int islower(int c);检查字符是否为小写字母int isprint(int c);检查字符是否为可打印字符包括空格int ispunct(int c);检查字符是否为标点符号int isspace(int c);检查字符是否为空白字符空格、换行、制表符等int isupper(int c);检查字符是否为大写字母int isxdigit(int c);检查字符是否为十六进制数字2.2 使用示例#includestdio.h#includectype.hintmain(){charchA;if(isalpha(ch)){printf(%c 是一个字母。\n,ch);}if(isupper(ch)){printf(%c 是一个大写字母。\n,ch);}if(isdigit(5)){printf(5 是一个数字。\n);}if(isspace( )){printf(空格是一个空白字符。\n);}return0;}2.3 模拟实现思路这些函数的实现通常依赖于ASCII码表的特性。例如判断一个字符是否为数字可以检查其ASCII码值是否在0到9之间。// 模拟实现 isdigitintmy_isdigit(intc){return(c0c9);}// 模拟实现 isalphaintmy_isalpha(intc){return((cAcZ)||(cacz));}// 模拟实现 isupperintmy_isupper(intc){return(cAcZ);}3. 字符转换函数ctype.h字符转换函数用于改变字符的大小写。函数原型功能描述int tolower(int c);将大写字母转换为小写字母int toupper(int c);将小写字母转换为大写字母3.1 使用示例#includestdio.h#includectype.hintmain(){charstr[]Hello World!;for(inti0;str[i]!\0;i){str[i]tolower(str[i]);}printf(转换为小写: %s\n,str);// 输出: hello world!for(inti0;str[i]!\0;i){str[i]toupper(str[i]);}printf(转换为大写: %s\n,str);// 输出: HELLO WORLD!return0;}3.2 模拟实现转换的原理是基于大小写字母在ASCII码表中相差32。// 模拟实现 tolowerintmy_tolower(intc){if(cAcZ){returnc(a-A);// 等价于 c 32}returnc;}// 模拟实现 toupperintmy_toupper(intc){if(cacz){returnc-(a-A);// 等价于 c - 32}returnc;}4. 字符串函数string.h4.1strlen- 求字符串长度函数原型size_t strlen(const char *str);功能计算字符串str的长度即从起始地址到第一个\0字符串结束符之前的字符个数不包括\0本身。而且我们要注意函数的返回值的size_t类型是无符号的。使用示例#includestdio.h#includestring.hintmain(){charstr[]Hello, CSDN!;size_tlenstrlen(str);printf(字符串 \%s\ 的长度是 %zu。\n,str,len);// 输出: 13return0;}模拟实现//计数器方式size_tmy_strlen(constchar*str){size_tcount0;assert(str);while(*str!\0){count;str;}returncount;}//不创建临时变量计数器intmy_strlen(constchar*str){assert(str);if(*str\0){return0;}elsereturn1my_strlen(str1);}//指针intmy_strlen(char*s){assert(s);char*ps;while(*p!\0){p;}returnp-s;}4.2strcpy- 字符串拷贝函数原型char *strcpy(char *dest, const char *src);功能将src指向的字符串包括结束符\0复制到dest指向的数组中。目标数组dest必须足够大,并且能够修改以确保能容纳源字符串。使用示例#includestdio.h#includestring.hintmain(){charsrc[]Copy this.;chardest[20];strcpy(dest,src);printf(目标字符串: %s\n,dest);// 输出: Copy this.return0;}模拟实现char*my_strcpy(char*dest,constchar*src){char*retdest;assert(dest!NULL);assert(src!NULL);while((*dest*src)!\0){// 空循环体完成拷贝}returnret;// 返回目标字符串的起始地址}4.3strcat- 字符串连接函数原型char *strcat(char *dest, const char *src);功能将src字符串追加到dest字符串的末尾。dest的结束符\0会被覆盖连接后的新字符串末尾会自动添加\0。dest必须有足够的空间。源字符串和目标字符串都必须有‘/0’。使用示例#includestdio.h#includestring.hintmain(){chardest[50]Hello, ;charsrc[]World!;strcat(dest,src);printf(连接后: %s\n,dest);// 输出: Hello, World!return0;}模拟实现char*my_strcat(char*dest,constchar*src){char*retdest;// 1. 找到 dest 的末尾assert(dest!NULL);assert(src!NULL);while(*dest!\0){dest;}// 2. 将 src 拷贝到 dest 末尾while((*dest*src)!\0){// 空循环体}returnret;}4.4strcmp- 字符串比较函数原型int strcmp(const char *str1, const char *str2);功能按字典序比较两个字符串。返回值 0str1小于str2返回值 0str1等于str2返回值 0str1大于str2使用示例#includestdio.h#includestring.hintmain(){charstr1[]apple;charstr2[]banana;intresultstrcmp(str1,str2);if(result0){printf(\%s\ 小于 \%s\\n,str1,str2);}elseif(result0){printf(\%s\ 大于 \%s\\n,str1,str2);}else{printf(\%s\ 等于 \%s\\n,str1,str2);}return0;}模拟实现intmy_strcmp(constchar*str1,constchar*str2){while(*str1(*str1*str2)){str1;str2;}return*(constunsignedchar*)str1-*(constunsignedchar*)str2;}4.5strncpy- 受限长度的字符串拷贝函数原型char *strncpy(char *dest, const char *src, size_t n);功能从src复制最多n个字符到dest。如果src的长度小于n则拷贝整个src并用\0填充剩余部分。如果src的长度大于或等于n则只拷贝前n个字符并且不会自动添加\0。使用示例#includestdio.h#includestring.hintmain(){charsrc[]Hello, World!;chardest1[10];chardest2[20];strncpy(dest1,src,5);dest1[5]\0;// 手动添加结束符printf(dest1: %s\n,dest1);// 输出: Hellostrncpy(dest2,src,sizeof(dest2));printf(dest2: %s\n,dest2);// 输出: Hello, World!return0;}模拟实现char*my_strncpy(char*dest,constchar*src,size_tn){char*retdest;size_ti;for(i0;insrc[i]!\0;i){dest[i]src[i];}for(;in;i){dest[i]\0;// 填充 \0}returnret;}4.6strncat- 受限长度的字符串连接函数原型char *strncat(char *dest, const char *src, size_t n);功能将src的前n个字符追加到dest末尾并在追加的字符后自动添加\0。dest必须有足够的空间。使用示例#includestdio.h#includestring.hintmain(){chardest[50]Hello, ;charsrc[]World! This is a test.;strncat(dest,src,6);// 只追加 World!printf(连接后: %s\n,dest);// 输出: Hello, World!return0;}模拟实现char*my_strncat(char*dest,constchar*src,size_tn){char*retdest;// 找到 dest 末尾while(*dest!\0){dest;}// 拷贝最多 n 个字符size_ti;for(i0;insrc[i]!\0;i){dest[i]src[i];}dest[i]\0;// 确保以 \0 结尾returnret;}4.7strncmp- 受限长度的字符串比较函数原型int strncmp(const char *str1, const char *str2, size_t n);功能比较两个字符串的前n个字符。使用示例#includestdio.h#includestring.hintmain(){charstr1[]applepie;charstr2[]applejam;intresultstrncmp(str1,str2,5);// 只比较前5个字符 appleif(result0){printf(前5个字符相同。\n);}return0;}模拟实现intmy_strncmp(constchar*str1,constchar*str2,size_tn){if(n0)return0;while(--n*str1(*str1*str2)){str1;str2;}return*(constunsignedchar*)str1-*(constunsignedchar*)str2;}4.8strstr- 查找子串函数原型char *strstr(const char *haystack, const char *needle);功能在haystack字符串中查找第一次出现needle子串的位置返回指向该位置的指针如果未找到则返回NULL。使用示例#includestdio.h#includestring.hintmain(){charstr[]This is a simple string.;char*substrstr(str,simple);if(sub!NULL){printf(找到子串: %s\n,sub);// 输出: simple string.}else{printf(未找到子串。\n);}return0;}模拟实现暴力匹配法char*my_strstr(constchar*haystack,constchar*needle){if(*needle\0){return(char*)haystack;}for(;*haystack!\0;haystack){constchar*hhaystack;constchar*nneedle;while(*h!\0*n!\0*h*n){h;n;}if(*n\0){return(char*)haystack;}}returnNULL;}4.9strtok- 字符串分割函数原型char *strtok(char *str, const char *delim);功能将字符串str按分隔符delim分割成一系列子串。第一次调用时str指向待分割的字符串。后续调用时str应设为NULL函数会继续从上一次保存的位置开始分割。每次调用返回一个指向当前子串的指针如果没有更多子串则返回NULL。注意strtok会修改原始字符串用\0替换分隔符。使用示例#includestdio.h#includestring.hintmain(){charstr[]apple,banana,orange,grape;constchardelim[],;char*tokenstrtok(str,delim);while(token!NULL){printf(分割结果: %s\n,token);tokenstrtok(NULL,delim);}return0;}/* 输出: 分割结果: apple 分割结果: banana 分割结果: orange 分割结果: grape */模拟实现简化版// 静态变量用于保存上次分割的位置staticchar*save_ptrNULL;char*my_strtok(char*str,constchar*delim){char*start;// 如果是第一次调用或str不为NULL则从str开始if(str!NULL){save_ptrstr;}// 如果save_ptr为NULL或指向结束符返回NULLif(save_ptrNULL||*save_ptr\0){returnNULL;}// 跳过开头的分隔符while(*save_ptr!\0strchr(delim,*save_ptr)!NULL){save_ptr;}if(*save_ptr\0){save_ptrNULL;returnNULL;}startsave_ptr;// 找到下一个分隔符或字符串结尾while(*save_ptr!\0strchr(delim,*save_ptr)NULL){save_ptr;}if(*save_ptr!\0){*save_ptr\0;save_ptr;}else{save_ptrNULL;}returnstart;}4.10strerror- 错误信息描述函数原型char *strerror(int errnum);功能根据错误编号errnum返回对应的错误描述字符串。通常与errno全局变量配合使用
返回列表