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

【C语言】字符函数和内存操作函数

大家好,我是苏貝,本篇博客带大家了解字符函数和内存操作函数,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
在这里插入图片描述


目录

  • 一.字符函数
    • 1.1 字符分类函数
    • 1.2 字符转换函数
  • 二.内存操作函数
    • 2.1 memcpy
    • 2.2 memmove
    • 2.3 memset
    • 2.4 memcmp

一.字符函数

1.1 字符分类函数

下面函数的头文件都是<ctype.h>

函数如果他的参数符合下列条件就返回真即非0,不符合则返回0
iscntrl任何控制字符
isspace空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’
isdigit十进制数字 0~9
isxdigit十六进制数字,包括所有十进制数字,小写字母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任何可打印字符,包括图形字符和空白字符

其实字符分类函数很简单,下面就挑两个作为范例
范例1:

int main()
{int ret1 = islower('X');//X不是小写字母,返回0int ret2 = islower('f');//f是小写字母,返回非0的数printf("%d %d", ret1, ret2);return 0;
}

在这里插入图片描述

范例2:

int main()
{int ret1 = isxdigit('a');//a是16进制数字int ret2 = isxdigit('z');//z不是16进制数字,返回0int ret3 = isxdigit('4');//4是16进制数字printf("%d %d %d", ret1, ret2, ret3);return 0;
}

在这里插入图片描述

1.2 字符转换函数

字符转换函数只有两个,分别是tolower和toupper,它们的函数原型为:

int tolower ( int c ); //将大写字母转化为小写字母
int toupper ( int c );//将小写字母转化为大写字母

其中,参数的类型为int,是将字符的ASCII码值传过去,返回的也是字符的ASCII码值,所有返回类型也是int

范例1:

int main()
{int x = toupper('a');printf("%c\n", x);x = tolower(x);printf("%c\n", x);return 0;
}

在这里插入图片描述

范例2:
将数组arr中的字符全都变成小写字母

int main()
{char arr[] = "ABcdeFGhIjk";char* p = arr;while (*p){if (isupper(*p)){*p = tolower(*p);}p++;}printf("%s", arr);return 0;
}

在这里插入图片描述


二.内存操作函数

2.1 memcpy

①函数介绍

void * memcpy ( void * destination, const void * source, size_t num );

memcpy函数的功能:从source的位置开始向后复制num个字节的数据到destination的内存位置。返回值:目标空间的起始位置。这与strcpy函数的功能很相似,那为什么我们已经有了strcpy函数还要设计memcpy函数呢?因为strcmp函数只能拷贝字符串,而内存空间可不是只有字符的,所有我们需要能拷贝非字符类型的函数,memcpy函数应运而生

细节:destination和source的类型都为void * :因为设计该函数的程序员不知道用户想要拷贝的类型,所有用void * 来接收所有类型的指针。source被const修饰:源内存块的内容不会被修改。num是指num个字节

注意:
1.这个函数在遇到 ‘\0’ 的时候并不会停下来。
2.如果source和destination有任何的重叠,复制的结果都是未定义的

范例:
把arr2中的前5个整型的数据拷贝放在arr1中

int main()
{int arr1[10] = { 0 };int arr2[] = { 1,2,3,4,5 };memcpy(arr1, arr2, 20);//20:5*sizeof(int)=5*4int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}

在这里插入图片描述

②模拟实现

模拟函数的三个参数没有改变,依旧是只需要源内存块的起始地址和目标空间的起始地址以及要拷贝的字节数。先对dest和src断言,避免它们为空指针。用while循环num次,当num==0时退出循环。接下来就是将src的内容拷贝到dest中。因为src和dest指针都是void * 类型的,所有需要先强制类型转化为char * ,拷贝一次后,两指针都要指向下一个字节,因为强制类型转化是暂时的,所有完成赋值语句后,两指针还是void * 类型,所有为了指向下一个字节,我们需要再对它们强制类型转化为char * 类型,写成dest=(char*)dest + 1;那可以写成(char*)dest++;吗?不能,因为++的优先级高于强制类型转化。那可以写成++(char*)dest;吗?最好不要,有些编译器会报错

void* my_memcpy(void* dest, const void* src, size_t num)
{assert(dest && src);void* ret = dest;while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;
}

③我们是否可以用所写的模拟函数让下面代码的数组arr从第5个元素开始往后的5个字符作为源内存块,将第3个元素开始往后的5个字符作为目标内存块,从source的位置开始向后复制num个字节的数据到destination的内存位置呢?即将数组arr变为{ 1,2,3,4,3,4,5,6,7,10}

void* my_memcpy(void* dest, const void* src, size_t num)
{void* ret = dest;assert(dest && src);while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;
}int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };my_memcpy(arr + 4, arr + 2, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}

答案是不能的,因为当程序想将5赋值给元素7时,5已经被3覆盖。同理,6已经被4覆盖,7已经被3覆盖
在这里插入图片描述
我们注意到这次的拷贝的源内存块和目标内存块是重叠的,而我们上面写的模拟函数的源内存块和目标内存块要求是不重叠的,所以使用失败。那我们如何解决源内存块和目标内存块重叠时成功拷贝呢?别急,有一个函数可以解决,那就是memmove函数

补充:其实有一些编译器的memcpy函数可以处理源内存块和目标内存块重叠的情况,但是并非所有,因此遇见两内存块重叠的情况时最好还是选择memmove函数


2.2 memmove

①函数介绍

void * memmove ( void * destination, const void * source, size_t num );

1.和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的,其余都相同
2.如果源空间和目标空间出现重叠,就得使用memmove函数处理

你瞧,memcpy解决不了的问题memmove轻松搞定

int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr + 4, arr + 2, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}

在这里插入图片描述

②模拟实现
以上面代码的arr数组举例,如果源内存块是从元素3开始的5个元素,目标内存块是从元素5开始的5个元素,此时如果要拷贝,为避免有些重要的值事先被覆盖,我们就要从后往前拷贝,即从源内存块的最后一个字节开始拷贝
在这里插入图片描述

如果源内存块是从元素3开始的5个元素,目标内存块是从元素1开始的5个元素,此时如果要拷贝我们就要从前往后拷贝
在这里插入图片描述

如果源内存块和目标内存块无重叠,那么拷贝既可以从前往后也可以从后往前
在这里插入图片描述

所以我们可以这样想:
1.dest<src时,从前往后拷贝
2.dest>src时,从后往前拷贝

dest<src时,与上面的memcpy模拟实现的思路相同,不再赘述。现在我们来思考如何从后往前拷贝。我们依旧用while循环,循环num次,当num==0时退出循环,while(num)。从后往前拷贝,就是将源内存块的最后一个字节拷贝到目标内存块的最后一个字节,再将源内存块的倒数第二个字节拷贝到目标内存块的倒数第二个字节……本题中,num=20,所以源内存块的最后一个字节的地址是(char*)src+19,目标内存块的最后一个字节的地址是(char*)dest+19。源内存块的倒数第二个字节的地址是(char*)src+18,目标内存块的倒数第二个字节的地址是(char*)dest+18。我们发现,第一次循环的时候19=num(20)-1,经历一次循环,num–=19;第二次循环的时候18=num(19)-1,经历一次循环,num–=18;那我们是不是在while循环的条件中写成while(num–)呢?这样的话每次循环源内存块字节的地址=(char*)src+num,所以写成如下写法

void* my_memmove(char* dest, char* src, size_t num)
{assert(dest && src);void* ret=dest;if (dest < src){while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}}else{while (num--){*((char*)dest + num) = *((char*)src + num);}}return ret;
}

2.3 memset

memset是以字节为单位设置内存的。比如我可以让字符数组arr=“hello world"中从第3个元素开始的5个字符都变成字符’x’,即变成"hexxxxxorld”。

void * memset ( void * ptr, int value, size_t num );

范例1:

int main()
{char arr[] = "hello world";memset(arr + 2, 'x', 5);printf("%s", arr);return 0;
}

范例2:
下面代码的目的是用memset函数将arr数组中的元素全部改成1,可以做到吗?

int main()
{int arr[5] = { 0 };memset(arr, 1, 20);int i = 0;for (i = 0; i < 5; i++){printf("%d ", arr[i]);}return 0;
}

如果你觉得可以的话,请看看最后的结果吧!为什么不是我们所想的全是1呢?

在这里插入图片描述
经过调试我们可以看到,调用完成memset后,数组arr的每个字节都由0变1,导致每个整型元素都变为0x01010101,所以不能用memset函数将arr数组中的元素全部改成1。所以memset更适用于字符数组
在这里插入图片描述

但是可以将数组中的每个元素都变为0,因为将每个字节都变为0即每个整形元素都为0

int main()
{int arr[5] = { 1,2,3,4,5 };memset(arr, 0, 20);int i = 0;for (i = 0; i < 5; i++){printf("%d ", arr[i]);}return 0;
}

在这里插入图片描述


2.4 memcmp

int memcmp ( const void * ptr1,const void * ptr2,size_t num );

memcmp函数是比较从ptr1和ptr2指针开始的num个字节的大小,如果 * ptr1> * ptr2,返回一个正数;如果 * ptr1== * ptr2,返回0;如果 * ptr1< * ptr2,返回一个负数。依旧是比较以字节为单位进行比较

int main()
{int arr1[] = { 1,2,3,4,5 };//假如是小端存储//01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00int arr2[] = { 1,2,0x11223304 };//01 00 00 00 02 00 00 00 04 33 22 11int ret = memcmp(arr1, arr2, 9);printf("%d", ret);return 0;
}

用memcmp函数时,切记不要拿数组中的元素直接比较,我们要看它们各自的存储情况。本题两个数组在前8个字节都相等,看第9个字节,03<04,所以前面<后面,返回一个负数
在这里插入图片描述


好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️

相关文章:

【C语言】字符函数和内存操作函数

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解字符函数和内存操作函数&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 一.字符函数1.1 字符分类函数1.2 字符转换函数 二.内存操作函数2.1 memcpy2.2…...

SpringBoot大文件上传实现分片、断点续传

大文件上传流程 客户端计算文件的哈希值&#xff0c;客户端将哈希值发送给服务端&#xff0c;服务端检查数据库或文件系统中是否已存在相同哈希值的文件&#xff0c;如果存在相同哈希值的文件&#xff0c;则返回秒传成功结果&#xff0c;如果不存在相同哈希值的文件&#xff0…...

React 注意事项

在使用 React 进行开发时&#xff0c;有一些注意事项可以帮助你更好地使用这个JavaScript库。以下是一些需要注意的事项&#xff1a; 组件结构和组织 尽量保持组件简单和可复用&#xff1a;将组件拆分为较小和独立的部分&#xff0c;以提高代码的可维护性和可测试性。遵循单一…...

常见排序算法Java版(待续)

冒泡排序O(n^2) public class Main {public static void main(String[] args) {Random random new Random();int[] nums new int[]{random.nextInt(100), random.nextInt(100), random.nextInt(100), random.nextInt(100), random.nextInt(100), random.nextInt(100)};for (i…...

Jmeter 多实例压测

Apache JMeter 是一个开源的 Java 应用程序&#xff0c;用于性能测试和负载测试。它最初是为测试 Web 应用程序而创建的&#xff0c;但现在已广泛用于测试各种不同类型的应用程序&#xff0c;包括数据库、消息队列、FTP 服务器等。JMeter 提供了丰富的功能&#xff0c;使您能够…...

线程安全问题 --- 内存可见性问题

小王学习录 本月鸡汤:什么是内存可见性问题引起内存可见性问题的原因如何解决内存可见性问题volatile使用规范编外: 工作内存(工作存储区)由前面文章介绍可以知道, 引起线程安全问题有 五个原因, 分别是: 线程抢占式执行, 随即调度(根本原因); 多个线程对同一变量执行 修改操…...

消息队列 Kafka

Kafka Kafka 是一个分布式的基于发布/订阅模式的消息队列&#xff08;MQ&#xff0c;Message Queue&#xff09;&#xff0c;主要应用于大数据实时处理领域 为什么使用消息队列MQ 在高并发环境下&#xff0c;同步请求来不及处理会发生堵塞&#xff0c;从而触发too many conne…...

抽象轻松的java-mybatis简单入门

第一步&#xff1a;用IDEA新建一个java包 第二步&#xff1a;在IDEA中添加数据库&#xff08;ps&#xff1a;自己百度&#xff09; 点击数据库 第二步&#xff0c;新建数据库 选择你使用的数据库 用户与密码根据自己的设置进行配置 为了更方便的查看数据库&#xff0c;可以像图…...

012-第二代硬件选型

第二代硬件选型 文章目录 第二代硬件选型项目介绍重新换平台缘由X86 && Arm 架构切换 ARM Linux 硬件选型系统确定Qt 版本确定总结一下 关键字&#xff1a; Qt、 Qml、 Arm、 X86、 linux 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QM…...

Spring中的设计模式

目录 工厂模式 组合模式 适配器模式 代理模式 单例模式 观察者模式 模板方法模式 责任链模式 Spring有着非常优雅的设计&#xff0c;很多地方都遵循SOLID原则&#xff0c;里面的设计模式更是数不胜数大概有以下几种&#xff1a; 工厂模式 所谓的工厂模式&#xff0c;核…...

软考 系统架构设计师系列知识点之软件质量属性(1)

这个十一注定是一个不能放松、保持“紧”的十一。由于报名了全国计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试&#xff0c;11月4号就要考试&#xff0c;因此8天长假绝不能荒废&#xff0c;必须要好好利用起来。现在将各个核心知识点一一进行提炼并做记录。 所…...

GPT系列论文解读:GPT-1

GPT系列 GPT&#xff08;Generative Pre-trained Transformer&#xff09;是一系列基于Transformer架构的预训练语言模型&#xff0c;由OpenAI开发。以下是GPT系列的主要模型&#xff1a; GPT&#xff1a;GPT-1是于2018年发布的第一个版本&#xff0c;它使用了12个Transformer…...

数学分析:含参变量的积分

同样很多收敛性的证明不是重点&#xff0c;但里面的知识还是需要适当掌握&#xff0c;知道中间的大致思考和解决路径即可。 本质还是极限的可交换性&#xff0c;求导可以换到积分里面去操作。 这里要注意变量的区别&#xff0c;首先积分的被积变量是x&#xff0c;但是函数的变量…...

关于一篇ElementUI之CUD+表单验证

目录 一.CUD增删改查简述 1.1.增删改功能实现 二.表单验证 前端所有代码&#xff1a; 好啦今天就分享到这了&#xff0c;希望能帮到你哦&#xff01;&#xff01;&#xff01; 以下的代码基于我博客中的代码进行续写 : 关于ElementUI之动态树数据表格分页实例 一.CUD增删改…...

VUE模板编译的实现原理

前言 在Vue.js 2.0中&#xff0c;模板编译是通过将模板转换为渲染函数来实现的。渲染函数是一个函数&#xff0c;它返回虚拟DOM节点&#xff0c;用于渲染实际的DOM。Vue.js的模板编译过程可以分为以下几个步骤&#xff1a; 将模板解析为抽象语法树&#xff08;AST&#xff09…...

基础算法之——【动态规划之路径问题】1

今天更新动态规划路径问题1&#xff0c;后续会继续更新其他有关动态规划的问题&#xff01;动态规划的路径问题&#xff0c;顾名思义&#xff0c;就是和路径相关的问题。当然&#xff0c;我们是从最简单的找路径开始&#xff01; 动态规划的使用方法&#xff1a; 1.确定状态并…...

三十三、【进阶】索引的分类

1、索引的分类 &#xff08;1&#xff09;总分类 主键索引、唯一索引、常规索引、全文索引 &#xff08;2&#xff09;InnoDB存储引擎中的索引分类 2、 索引的选取规则(InnoDB存储引擎) 如果存在主键&#xff0c;主键索引就是聚集索引&#xff1b; 如果不存在主键&#xff…...

VBox启动失败、Genymotion启动失败、Vagrant迁移

VBox启动失败、Genymotion启动失败、Vagrant迁移 2023.10.9 最新版本vbox7.0.10、Genymotion3.5.0 Vbox启动失败 1、查看日志 Error -610 in supR3HardenedMainInitRuntime! (enmWhat4) Failed to locate ‘vcruntime140.dll’ 日志信息查看方法->找到虚拟机所在位置->…...

一篇短小精悍的文章让你彻底明白KMP算法中next数组的原理

以后保持每日一更&#xff0c;由于兴趣较多&#xff0c;更新内容不限于数据结构&#xff0c;计算机组成原理&#xff0c;数论&#xff0c;拓扑学......&#xff0c;所谓&#xff1a;深度围绕职业发展&#xff0c;广度围绕兴趣爱好。往下看今日内容 一.什么是KMP算法 KMP&#x…...

CSS盒子定位的扩张

定位的扩展 绝对定位&#xff08;固定定位&#xff09;会完全压住盒子 浮动元素不会压住下面标准流的文字&#xff0c;而绝对定位或固定位会压住下面标准流的所有内容 如果一个盒子既有向左又有向右&#xff0c;则执行左&#xff0c;同理执行上 显示隐藏 display: none&…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…...

MFC 抛体运动模拟:常见问题解决与界面美化

在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...

腾讯云V3签名

想要接入腾讯云的Api&#xff0c;必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口&#xff0c;但总是卡在签名这一步&#xff0c;最后放弃选择SDK&#xff0c;这次终于自己代码实现。 可能腾讯云翻新了接口文档&#xff0c;现在阅读起来&#xff0c;清晰了很多&…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...

日常一水C

多态 言简意赅&#xff1a;就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过&#xff0c;当子类和父类的函数名相同时&#xff0c;会隐藏父类的同名函数转而调用子类的同名函数&#xff0c;如果要调用父类的同名函数&#xff0c;那么就需要对父类进行引用&#…...