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

C语言第19讲

目录

字符函数和字符串函数

       字符分类函数

        字符转换函数

        strlen 的使用和模拟实现

        strcpy 的使⽤和模拟实现

strcat 的使用和模拟实现

strcmp 的使用和模拟实现

strlen strcpy strcat 和 strnlen strncpy strncat 的区别

        strncpy的特性:

        strncat的特性:

strncpy的特性:

strstr 函数

strtok 函数


字符函数和字符串函数

       字符分类函数

                        C语⾔中有⼀系列的函数是专⻔做字符分类的,也就是⼀个字符是属于什么类型的字符的。 这些函数的使⽤都需要包含⼀个头⽂件是 ctype.h

        isalnum() - 检查字符是否是字母或数字
        isalpha() - 检查字符是否是字母
        iscntrl() - 检查字符是否是控制字符
        isdigit() - 检查字符是否是十进制数字
        isgraph() - 检查字符是否是可打印字符(不包括空格)
        islower() - 检查字符是否是小写字母
        isprint() - 检查字符是否是可打印字符(包括空格)
        ispunct() - 检查字符是否是标点符号
        isspace() - 检查字符是否是空白字符
        isupper() - 检查字符是否是大写字母
        isxdigit() - 检查字符是否是十六进制数字

这类函数的用法都类似,所以只举一个例字

例:利用islower写⼀个代码,将字符串中的⼩写字⺟转⼤写,其他字符不变。

代码如下:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <ctype.h>int main()
{char ch[] = "Hello World";int i = 0;while (ch[i]){if (islower(ch[i])){ch[i] -= (char)32;}i++;}printf("%s ", ch);return 0;
}

                传参的字符为小写 islower 返回为非0,

                                  为大写 islower 返回为0

        字符转换函数

                int tolower ( int c ); // 将参数传进去的⼤写字⺟转⼩写

                int toupper ( int c ); // 将参数传进去的⼩写字⺟转⼤写

                现可以将上方代码进行改进

代码如下:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <ctype.h>int main()
{char ch[] = "Hello World";int i = 0;while (ch[i]){if (islower(ch[i])){ch[i] = toupper(ch[i]);		// 这是传值调用不会将传参的变量// 直接修改掉还需要接受返回值}i++;}printf("%s ", ch);return 0;
}

        strlen 的使用和模拟实现

作用:由传参的地址开始计算至\0前的字符个数

使用方式如下:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>int main()
{char ch[] = "Hello World";printf("%d", strlen(ch));return 0;
}

模拟及实现:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>
#include <assert.h>size_t my_strlen(const char* p)
{assert(p);const char* p2 = p;while (*p){p++;}return p - p2;
}int main()
{char ch[] = "Hello World";printf("%zd", my_strlen(ch));return 0;
}

        strcpy 的使⽤和模拟实现

strcpy的作用:将一个字符数组的复制到另一个字符数组中

声明:

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

                destination 为要粘贴处

                source 为复制处

                char* 返回粘贴处的首元素地址

ctrcpy的使用:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>int main()
{char ch1[] = "hello world";     // 复制数组具有\0char ch2[30] = { 0 };			// 要确保空间最够// 避免越界访问char* ret = strcpy(ch2, ch1);    printf("%s\n", ret);printf("%s\n", ch2);return 0;
}

strcpy的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>
#include <assert.h>char* my_strcpy(char* p1, const char* p2)   // p1为粘贴处 需要修改不用const// p2为复制处 不需要修改 保证安全使用const
{char* ret = p1;							// 确保返回值为粘贴处首元素地址assert(p1 && p2);						// 确保 p1 和 p2都不为空指针 NULL = (void*)0while (*p1++ = *p2++)					// 当*p2都为字符时,赋值给*p1 导致*p1不为\0,判断为真;									// 当*p2为\0时,赋值给*p1 导致*p1为\0,判断为假,跳出循环// 可以直接 后置加的原因是,跳出循环后函数结束// 所以p1 p2指向\0后的空间不会被使用return ret;
}int main()
{char ch1[] = "hello world";char ch2[30] = { 0 };				char* ret = my_strcpy(ch2, ch1);printf("%s\n", ret);printf("%s\n", ch2);return 0;
}

strcat 的使用和模拟实现

作用:将一个一串字符串链接到另一字符串上

声明:

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

                和strcpy相同

                destination 为修改处

                source 为复制处

                返回 修改处整改数组的首元素地址

运行逻辑:

                先找到 修改处\0结尾处

                开始将复制处的字符复制到修改处

                直到复制处遇上\0停止

其实仔细琢磨后两步骤会发先和strcpy的操作一致,只是多了一步寻找\0的地址

strcat函数的使用:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>int main()
{char ch1[30] = "hello ";char* p = "world";char * ret = strcat(ch1, p);printf("%s\n", ret);printf("%s\n", ch1);return 0;
}

strcat的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>
#include <assert.h>char* my_strcat(char* p1, const char* p2)
{assert(p1 && p2);char* ret = p1;while (*p1)p1++;					// ++ 不能写在判定表达式中// 因为*p1为\0跳出循环会多一次++,导致替换不完整while (*p1++ = *p2++);return ret;
}int main()
{char ch1[30] = "hello ";char* p = "world";char * ret = my_strcat(ch1, p);printf("%s\n", ret);printf("%s\n", ch1);return 0;
}

问题:如果传参同一字符串呢?

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>
#include <assert.h>char* my_strcat(char* p1, const char* p2)    // const char* p2// 只是禁止通过p2去修改指针内容// 不是禁止指向内容不能被修改;程序问题不在这
{assert(p1 && p2);char* ret = p1;while (*p1)p1++;					while (*p1++ = *p2++);return ret;
}int main()
{char ch1[30] = "hello";char * ret = my_strcat(ch1, ch1);return 0;
}

                当p1找到\0后开始替换,\0被替换成 h导致 p2字符串没有\0

                由于 p1 和 p2 的偏移速度一致会导致,整个系统循环下去

                直到越界访问,程序崩溃

strcmp 的使用和模拟实现

strcmp的作用:比较两字符串大小

声明:

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

                str1 和 str2 为进行比较的两字符串首元素地址

                int 会根据 str1 和 str2的大小返回值

                        *str1 大 返回    > 0      VS 的实现         1

                         相等     返回    = 0                               0

                        *str1 小  返回    < 0                              -1

strcmp的运行逻辑:

                本质是利用字符在ASCII表中的数字大写比较大小(\0 为 0)

                按顺序取出字符进行比较,出现不相等时则出现大小

                同时出现 \0则为相等

strcmp的使用:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>int main()
{char ch1[30] = "abd";char* p = "abc";if (strcmp(ch1, p) > 0){printf("ch1大");}else if (strcmp(ch1, p) == 0){printf("相等");}elseprintf("ch1小");return 0;
}

strcmp的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>
#include <assert.h>int my_strcmp(const char* p1, const char* p2)
{assert(p1 && p2);while (*p1 == *p2 )    // 将相同剔除{					   if (*p1 == '\0')   // 相同的情况为{				   // 1.字符相同 2.字符为\0return 0;      // P1 \0 或 P2 \0 都为不同归纳出大小比较}p1++;p2++;}return (int)(*p1 - *p2);
}int main()
{char ch1[30] = "abd";char* p = "abc";if (my_strcmp(ch1, p) > 0){printf("ch1大");}else if (my_strcmp(ch1, p) == 0){printf("相等");}elseprintf("ch1小");return 0;
}

strlen strcpy strcat 和 strnlen strncpy strncat 的区别

                strcpy                strncpy

                strcat                 strncat

                strcmp               strncmp

后者只是多了个产数去限制函数内部进行的次数

后者声明:

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

              区别只是多了次数参数,所以后置比较安全

        strncpy的特性:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>int main()
{char arr1[] = "abcdef";char arr2[20] = "xxxxxxxxxxxxxxx";strncpy(arr2, arr1, 3);                // 次数小于arr字符个数 不会在后方加\0printf("%s\n", arr2);char arr3[] = "abcdef";char arr4[20] = "xxxxxxxxxxxxxxx";strncpy(arr4, arr3, 8);                // 次数大于arr字符个数 才在后面加\0printf("%s\n", arr4);return 0;
}

        strncat的特性:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>int main()
{char arr1[] = "abcdef";char arr2[20] = "xx\0xxxxxxxxxxxx";			// 会在后方放\0strncat(arr2, arr1, 3);printf("%s\n", arr2);char arr3[] = "abcdef";char arr4[20] = "xx\0xxxxxxxxxxxx";			// 在arr3第7位时会放\0 第8位不会管了strncat(arr4, arr3, 8);printf("%s\n", arr4);return 0;
}

strncpy的特性:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>int main()
{char arr1[] = "abq";char* p = "abc";if (strncmp(arr1, p, 2) > 0){printf("arr1大");}else if (strncmp(arr1, p, 2) == 0){printf("一样大");}else{printf("arr1小");}return 0;
}

                只进行2次判断

strstr 函数

作用:在一个字符串中查找另一个字符串

声明:

char * strstr ( const char * str1, const char * str2);

                str2 查找的模版字符串

                str1 被查找的字符串

                char* 根据成功与否返回

                       失败 返回NULL

                        成功 返回标记地址 

                        例:在abbc中查找bc 返回为abbc字符串第3个元素的地址

strstr 的使用:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>int main()
{char ch1[] = "abbcooobcox";char* p = "bc";char* ret =strstr(ch1, p);printf("%s", ret);return 0;
}

strstr 的模拟实现:

                情形1:

char* my_strstr(const char* p1, const char* p2)
{const char* s1 = p1;		// 后期写函数如果常该传参过来的指针的话很难找回const char* s2 = p2;		// 所以创建其余指针进行移动while(*s1){;}s1++;return NULL;
}

                情形2:

char* my_strstr(const char* p1, const char* p2)
{const char* s1 = p1;		const char* s2 = p2;		const char* ret = NULL;while(*s1){ret = s1;while(*s1 && *s2 &&*s1 == *s2)		// 只有不等和 出现\0才会跳出循环{									// *s1出现\0;外层循环会跳出,返回NULLs1++;							// *s2出现\0;内存有if判断,返回标记指针s2++;}if (*s2 == '/0')return	ret;				    // 现在成功找寻到了,但未有标记最开始==时的指针s1++;	}										// 还需要多创建个指针用于保存 成功时的返回值return NULL;
}

                情形3:

char* my_strstr(const char* p1, const char* p2)
{const char* s1 = p1;		const char* s2 = p2;		const char* ret = NULL;while(*s1){ret = s1;while(*s1 && *s2 &&(*s1 == *s2))		{									s1++;							s2++;}if (*s2 == '\0')			// *s1 *s2 都不是 \0 时 *s1 == *s2return	ret;			// 所以s1 要恢复到 ret去// s2     要恢复到 p2去s1 = ret;s2 = p2;s1++;}										return NULL;
}

运行逻辑为:

                外循环对被查字符串一个字符一个字符检查

                一旦发现相同进入内循环,跳出内循环的结果只有:

                                1.*s1 为 \0 查找失败,没找到

                                2.*s2 为 \0 查找成功,返回开始ret

                                                因为ret一直处于外循环

                                3. *s1 != *s2 找到了相同处,

                                但并不完全相同;需要重置外循环指针; 

                情形4:  

                str2传过来一个空字符串时

加上if后并稍作加强安全性,代码如下:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <assert.h>char* my_strstr(const char* p1, const char* p2)
{if (*p2 == '\0')return (char*)p1;assert(p1 && p2);const char* s1 = p1;		const char* s2 = p2;		const char* ret = NULL;while(*s1){ret = s1;while(*s1 && *s2 &&(*s1 == *s2))		{									s1++;							s2++;}if (*s2 == '\0')			return	(char*)ret;			s1 = ret;s2 = p2;s1++;}										return NULL;
}
int main()
{char ch1[] = "abbcooobcox";char* p = "bc";char* ret =my_strstr(ch1, p);printf("%s", ret);return 0;
}

strtok 函数

声明:

char * strtok ( char * str1, const char * str2);

                str1 待被切割的字符串

                str2 定义什么字符为分割符

strtok 的使用:

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>int main() {char str[] = "this is an apple"; const char a[] = " "; // 分隔符是空格// 第一次调用char* ret1 = strtok(str, a);printf("%s\n", ret1); // 输出 "this"// 第二次调用char* ret2 = strtok(NULL, a);               // 它内部有静态指针来记录上次切割的位置printf("%s\n", ret2); // 输出 "is"          // 所以后续调用第一个参数传 NULL// 第三次调用char* ret3 = strtok(NULL, a);printf("%s\n", ret3); // 输出 "an"// 第四次调用char* ret4 = strtok(NULL, a);printf("%s\n", ret4); // 输出 "apple"// 第五次调用char* ret5 = strtok(NULL, a);printf("%s\n", ret5); // 输出 "null"        // 没有更多字符串了 返回NULLreturn 0;
}

                 这有个很巧妙的写法            

#define _CRT_SECURE_NO_WARNINGS 
#include <stdio.h>
#include <string.h>int main() {char str[] = "this is an apple";const char a[] = " ";for (char* ret = strtok(str, a); ret != NULL; ret = strtok(NULL, a)){printf("%s\n", ret);}return 0;
}

                strtok(str, a) 只会掉用一次后调用都是strtok(NULL,a)

                创建 ret 是为了让一次打印只一段字符串

                如果不创建 ret 的话,在判断需要再写一次strtok(NULL,a)

                这样 就会出现 一次循环调用两次strtok函数

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

相关文章:

  • 【含文档+PPT+源码】基于springboot+ssm的智能人脸识别养老系统的设计与开发
  • Linux-> UDP 编程3
  • 分库分表后ID冲突怎么解决?分布式ID生成方案。保证ID全局唯一性。
  • 域名如何解析家庭ip
  • LeetCode 2460.对数组执行操作
  • Unity Time.time 详解
  • LeetCode 922.按奇偶排序数组 II
  • 请简要谈谈Android系统的架构组成?
  • LeetCode 面试经典 150_哈希表_两数之和(44_1_C++_简单)
  • Kafka是什么,架构是什么样的?Kafka概述
  • TCN时序卷积网络、CNN、RNN、LSTM、GRU神经网络工业设备运行监测、航空客运量时间数据集预测可视化|附代码数据
  • 【HarmonyOS】HMRouter关键原理-动态import
  • 【Python】面向对象(三)
  • 05-django项目的跨域处理
  • go语言并发
  • Qt QSS 美化完整教程文档
  • jwt与token+redis,哪种方案更好用?
  • 基于react的前端项目开发和实战总结(umi框架)
  • 【iOS】YYModel
  • 修改docker配置使其支持本机tcp连接
  • ReportFragment:Android 生命周期的桥梁与兼容性解决方案
  • 力扣Hot100--234.回文链表
  • 视觉语言大模型(VLM)的产业落地:从Qwen-VL技术解析到医疗、车险行业革新
  • 零基础新手小白快速了解掌握服务集群与自动化运维(七)Nginx模块--Nginx Web服务
  • 一个硬盘选MBR 还是GPT
  • 【含文档+PPT+源码】基于GPT+SpringBoot的个人健康管理与咨询系统设计与实现
  • 【项目实战 Day5】springboot + vue 苍穹外卖系统(Redis + 店铺经营状态模块 完结)
  • 旧衣回收小程序:非技术视角下的价值重构与发展前景
  • 使用vue-i18n实现语言切换
  • 做小程序找哪家公司,解析小程序开发定制公司哪家适合你