【c语言】字符串函数和内存函数

🌟🌟作者主页ephemerals__

🌟🌟所属专栏C语言

目录

前言

一、字符串函数

1.strlen的使用和模拟实现

2.strcpy的使用和模拟实现

3.strcat的使用和模拟实现

4.strcmp的使用和模拟实现

5.strstr的使用和模拟实现

6.strncmp、strncpy、strncat的使用

7.strtok的使用

二、内存函数

1.memcpy的使用和模拟实现

2.memmove的使用和模拟实现

3.memset的使用

总结


前言

        在编程的过程中,我们经常要对字符串和内存进行各种各样的处理,c语言提供了一系列字符串函数和内存函数,便于我们对字符串或者内存空间进行操作。本篇文章我们就来学习其中的一些函数。

一、字符串函数

1.strlen的使用和模拟实现

        c语言中,strlen函数用于计算一个字符串的长度。它的原型如下:

size_t  strlen ( const char * str ) ;

这里需要注意的是:

1.字符串以\0为结束标志,这个函数返回值是字符串的长度,不包括\0。

2.传参时要传入字符、0串的首元素地址,指向的字符串要以\0为结尾。

3.函数返回值类型是size_t,是一个无符号整数

4.strlen函数使用需要引头文件string.h

接下来我们试着使用一下strlen函数:

#include <stdio.h>
#include<string.h>int main()
{char str[] = "hello";int ret = strlen(str);printf("%d\n", ret);return 0;
}

运行结果:

可以看到,"hello"一共有五个字符,所以字符串的长度就是5。也就是说,这个函数的原理就是通过统计出\0之前的字符个数。这样,我们尝试模拟实现一下它:

方法1:计数器

size_t my_strlen(const char* str)
{size_t count = 0;//定义变量统计字符个数while (*str != '\0'){str++;count++;}return count;
}

这里我们定义了一个变量count,负责统计\0之前的字符个数。当指针走到\0处时,循环停止。

方法2:递归

size_t my_strlen(const char* str)
{if (*str == '\0'){return 0;}else{return 1 + my_strlen(str + 1);}
}

方法3:指针减指针

size_t my_strlen(const char* str)
{char* p = str;while (*str++);//指针持续往后走,知道遇到\0为止return str - p - 1;//后置++使得指针多走了一步,多减一个1
}

这里先记录一下首元素地址,然后让str跑到字符串末尾,再减去首元素地址就得到两地址之间的元素个数,也就是字符串长度。

2.strcpy的使用和模拟实现

        strcpy这个函数的作用是将源字符串中的内容拷贝到目标字符串中。它的原型如下:

char* strcpy ( char* dest, const char* src ) ;

这里需要注意:

1.第一个参数是目标字符串的首地址,第二个参数是源字符串的首地址。

2.源字符串必须以\0结尾。

3.目标字符串的空间必须足够大,能够包含整个源字符串;目标字符串不能是常量字符串。

4.函数返回值是目标字符串的首元素地址。

5.源字符串中的\0也会一同拷贝。

我们尝试使用一下它:

#include <stdio.h>
#include <string.h>int main()
{char str1[] = "xxxxxxxxxxxx";char str2[] = "hello";printf("%s\n", strcpy(str1, str2));return 0;
}

运行结果:

模拟实现

char* my_strcpy(char* dest,const char* src)
{char* ret = dest;//记录首元素地址while (*dest++ = *src++);return ret;
}

这里我们首先记录了原字符串的首地址,然后循环赋值完成拷贝,最后返回首地址。

3.strcat的使用和模拟实现

        strcat函数的作用是将源字符串内容追加到目标字符串上。原型如下:

char * strcat ( char * dest, const char * src ) ;

要注意的是:

1.第一个参数是目标字符串的首地址,第二个参数是源字符串的首地址。

2.源字符串和目标字符串都必须以\0结尾。

3.目标字符串的空间必须足够大;目标字符串不能是常量字符串。

4.函数返回值是目标字符串的首地址。

接着我们使用一下这个函数:

#include <stdio.h>
#include <string.h>int main()
{char str1[20] = "hello ";char str2[] = "world";printf("%s\n", strcat(str1, str2));return 0;
}

运行结果:

模拟实现

char* my_strcat(char* dest, const char* src)
{char* ret = dest;//记录目标字符串首地址while (*dest){dest++;}//使指针指向目标字符串末尾while (*dest++ = *src++);//循环追加,直到源字符串末尾return ret;
}

4.strcmp的使用和模拟实现

        strcmp函数是一个很重要的函数,它用于比较两个字符串的大小。字符串的比较在很多实例中会使用到,它的比较规则如下:

在了解了比较规则之后,我们来看一下函数原型:

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

1.参数传入要比较的两个字符串的首地址。

2.两个字符串都要以\0结尾。

3.如果str1大于str2,函数返回正数,否则返回负数,相等则返回0。

我们写一个程序使用该函数,实现两个字符串比较:

#include <stdio.h>
#include <string.h>int main()
{char str1[] = "abcd";char str2[] = "abdc";int flag = strcmp(str1, str2);if (flag > 0){printf("str1大于str2\n");}else if (flag < 0){printf("str1小于str2\n");}else{printf("两者相等\n");}return 0;
}

运行结果:

接着,我们尝试模拟实现strcmp:

int my_strcmp(const char* str1, const char* str2)
{while (*str1 == *str2){if (*str1 == '\0')//处理出现空字符串或者字符比较到末尾的情况{return 0;}str1++;str2++;}return *str1 - *str2;
}

5.strstr的使用和模拟实现

        接下来,我们学习一下strstr函数。strstr函数的作用是判断一个字符串是否是另一个字符串的子字符串(是否为包含关系)

它的原型如下:

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

1.两个字符串必须要以\0结尾。

2.函数的返回值是str2在str1中第一次出现的位置,如果没找到,返回空指针。

我们尝试使用这个函数:

#include <stdio.h>
#include <string.h>int main()
{char str1[] = "123456789";char str2[] = "3456";printf("%s\n", strstr(str1, str2));return 0;
}

运行结果:

可以看到,函数确实找到了第二个字符串在第一个字符串中的位置。接下来我们模拟实现一下它:

char* my_strstr(const char* str1, const char* str2)
{char* cur = str1;if (*str2 == '\0')//空字符串的情况{return str1;}while (*cur != '\0'){char* s1 = cur;char *s2 = str2;while (*s1 && *s2 && (*s1 - *s2) == '\0')//遍历相等的部分{s1++;s2++;}if (*s2 == '\0')//遍历结束后如果str2已结束,说明是子串{return cur;}cur++;}return NULL;
}

6.strncmp、strncpy、strncat的使用

        这三个函数是在原来功能的基础之上,限定了比较/拷贝/追加的字符个数。函数的参数部分多了一个num,表示限定的字符个数。举个例子:

#include <stdio.h>
#include <string.h>int main()
{char str1[] = "xxxxxxxxx";char str2[] = "hehe";strncpy(str1, str2, 2);printf("%s\n", str1);char str3[20] = "hello ";char str4[] = "world";strncat(str3, str4, 3);printf("%s\n", str3);return 0;
}

运行结果:

可以看到,程序根据我们限定的字符个数完成了拷贝和追加。

7.strtok的使用

        strtok函数也叫做字符串分割函数。顾名思义,它的作用就是根据给出的特定字符来分割字符串。下面是它的原型:

char* strtok ( char* str, const char* sep ) ;

这里需要注意以下几点:

1.第一个参数是要被分割的字符串,第二个参数是由分隔符组成的字符串。

2.strtok函数会在str中寻找限定的分隔符,遇到分隔符就会在字符串中将其变为\0,完成分割。函数返回上一次分割后\0的下一个字符的地址;如果是第一次分割,则返回字符串首地址。

3.对一个字符串进行多次分割时,需要多次调用该函数:第一次调用时str传入要被分割的字符串,函数会使用静态变量保存被分割的位置,之后调用时str要传入NULL,函数就会从保存的位置开始进行分割。

4.当字符串按照分隔符完成了所有分割之后,就会返回NULL。

我们写代码体现一下它的使用效果:

#include <stdio.h>
#include <string.h>int main()
{char str[] = "123.12.14.1.50";char* p = NULL;printf("%s\n", str);for (p = strtok(str, "."); p != NULL; p = strtok(NULL, ".")){printf("%s\n", p);}return 0;
}

运行结果:

可以看到字符串成功被分割成几部分。不难发现,strtok函数可以和for循环结合使用,达到分割字符串的效果

二、内存函数

        在学习了这些字符串函数之后,我们可以发现,它们虽然实用,但是却只能对字符串进行操作。那么是否有一些函数可以让我们对任何类型也进行相似操作呢?它就是内存函数。这些内存函数的使用也需要引头文件string.h

1.memcpy的使用和模拟实现

        memcpy函数也叫做内存拷贝函数,它能够将特定字节的内存值拷贝到其他内存位置中。它的原型:

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

其中的前两个参数分别表示目标空间和源空间的首地址,第三个参数表示要拷贝的字节数。函数的返回值是目标空间的首地址。我们来使用一下它:

#include <stdio.h>
#include <string.h>int main()
{int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };memcpy(arr2, arr1, 5 * sizeof(int));for (int i = 0; i < 10; i++){printf("%d ", arr2[i]);}return 0;
}

运行结果:

可以看到,有五个元素的内容被拷贝到arr2当中。

接下来我们尝试模拟实现

void* my_memcpy(void* dest, const void* src, size_t num)
{void* ret = dest;while (num--){*(char*)dest = *(char*)src;(char*)dest += 1;//这里由于强制类型转换是临时的,不能直接写++(char*)src += 1;}return ret;
}

可以看出,我们这里使用了泛型编程的思想,将void*指针强转为char*类型,然后根据给定的字节数一个字节一个字节赋值。

2.memmove的使用和模拟实现

        对于刚才的memcpy函数,如果想要拷贝的两个内存空间之间出现重叠情况,那么函数就无法完成正常的拷贝。而memmove函数就弥补了这个缺陷。原型如下:

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

它的参数含义和返回值和memcpy是完全相同的。我们尝试使用它来拷贝重叠的内存:

#include <stdio.h>
#include <string.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr, arr + 2, 5 * sizeof(int));for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}

运行结果:

可以看到,它成功地完成了重叠内存的拷贝。那我们是否可以模拟实现这个函数呢?

我们首先来思考一下,现在有一个数组arr,它的内容如下:

现在我们想要将1,2,3,4,5这五个元素拷贝到3,4,5,6,7的位置上,能否完成呢?

将1赋值到3的位置上,将2赋值到4的位置上,到赋值3的时候,就会发现,此时的3已经被覆盖成1了,这样下去,最终结果就会变成1,2,1,2,1,2,1,8,9,10。

聪明的你肯定会想到,既然从前往后不行,那么我们就从后往前进行拷贝:先将5赋值到7的位置,再将4赋值到6的位置,以此类推...你就会发现结果就是1,2,1,2,3,4,5,8,9,10,说明成功了。

难道所有的情况都可以通过从后往前拷贝的方法赋值成功吗?当我们反过来将3,4,5,6,7拷贝到1,2,3,4,5的位置上,我们就发现从后往前的拷贝就不行了,就要用从前往后拷贝的方式

所以我们就可以得出以下结论在有重叠空间的情况下,如果dest的地址小于src,那么就从前向后拷贝;如果dest的地址大于src,就从后向前拷贝。没有重叠空间时怎样都行。图示:

这里由于右边非重叠空间的部分和中间部分是连续的,所以我们使用从后向前拷贝的方法会更加方便编写代码

代码如下:

void* my_memmove(void* dest, const void* src, size_t num)
{void* ret = dest;if (dest < src)//从前向后{while (num--){*(char*)dest = *(char*)src;(char*)dest += 1;(char*)src += 1;}}else//从后向前{while (num--){*((char*)dest + num) = *((char*)src + num);}}return ret;
}

3.memset的使用

        memset函数也叫做内存设置函数,它可以对特定区域的内存全部赋成你想要的值。它的原型如下:

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

它的第一个参数是要设置的内存空间的首元素地址,第二个参数是要赋的值,第三个参数是内存空间的大小(字节)。

它的返回值是内存空间的首元素地址。

我们来使用一下这个函数:

#include <stdio.h>
#include <string.h>int main()
{char str[] = "hello world";memset(str, 'x', 6);printf(str);return 0;
}

运行结果:

可以看到,前六个字符都被赋值成了'x'。

总结

        本篇文章我们学习了字符串函数和内存函数的相关知识,它们在我们的编程当中十分常见和实用。同时,我们也学会了如何思考问题,解决问题。之后博主会更新数据存储方式相关的内容。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/30824.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springboot3多模块实践

先帖下目录结构&#xff0c;直接在idea里面新建就行&#xff0c;删掉多余的文件 子模块的新建 根目录pom文件&#xff0c;注意modules、packaging&#xff0c;dependencyManagement统一管理依赖&#xff0c;子模块添加依赖的时候就不用加版本号 <?xml version"1.0…

SAP_FICO模块-获利能力段新增特征字段

业务背景&#xff1a; 公司有启用获利能力分析功能&#xff0c;有一个销售订单接口&#xff0c;是通过第三方销售订单管理平台推送数据到SAP的&#xff0c;用户希望对接新增一个编号ID到销售订单上&#xff0c;并且可以用KE24/KE30报表查看显示&#xff1b; 对于我这么一个后勤…

Java面试八股之简述JVM内存结构

简述JVM内存结构 Java虚拟机&#xff08;JVM&#xff09;内存结构主要分为线程私有区域和线程共享区域两大部分&#xff0c;具体组成部分如下&#xff1a; 线程私有区域 程序计数器&#xff08;Program Counter Register&#xff09;&#xff1a; 记录当前线程执行的字节码行…

逻辑整理(光伏发电预测算法部署用的)

def main():while True:if is_true_month(): # 检查是否为每年的1月份、4月份、7月份、10月份的凌晨1&#xff1a;00&#xff0c;判断到minget_new_history_data # 获取最新的3个月左右的历史数据train model # 进行模型训练save model # 保存模型 在之前模型保存的位置直接覆…

三角形法恢复空间点深度

三角形法恢复空间点深度 如下图&#xff0c;以图 I 1 I_1 I1​为参考&#xff0c;图 I 2 I_2 I2​的变换矩阵为 T T T。相机光心为 O 1 O_1 O1​和 O 2 O_2 O2​。在图 I 1 I_1 I1​中有特征点 p 1 p_1 p1​&#xff0c;对应图 I 2 I_2 I2​中有特征点 p 2 p_2 p2​。理论上直…

战略网络优化:网络可观测性的综合方法

在网络成为运营支柱的时代&#xff0c;了解和优化网络性能至关重要。网络可观测性是了解网络性能的关键&#xff0c;它以一种全面、主动的方式超越了传统监控。本文说明了网络可观测性的变革力量&#xff0c;详细介绍了其优势、差异化因素及其在现代网络管理中的关键作用。 什…

vue学习(三)

14.监视属性watch 当被监视的属性发生变化时&#xff0c;回调函数立即调用&#xff0c;进行操作 监视的两种写法&#xff1a;直接配置或者通过vm添加 watch:{isHot:{immediate:true, //首次用到执行handler(newValue,oldValue){console.log("isHot 被修改了",newV…

python如何做报表系统

首先我们安装的python和PyQt5要保持一致&#xff0c;要么都是32位或者都是64位。 下载安装&#xff0c;安装完成之后我们记得要设置环境变量。 一路选择“下一步”就可以了。 安装完成之后我们需要验证是否成功。 pyqt5的安装直接安装就可以的&#xff0c;主要更改环境变量~~\p…

数据结构之B数

目录 1.概述 2.特点 3.诞生 4.优缺点 4.1.优点 4.2.缺点 5.应用场景 6.C语言中的B树实现例子 7.总结 1.概述 B树&#xff08;B-tree&#xff09;是一种自平衡的树数据结构&#xff0c;广泛应用于数据库和文件系统中&#xff0c;以便高效地进行顺序读取、写入以及查找…

使用 C# 进行面向对象编程:第 9 部分

使用 OOP 的用户活动日志 应用程序背后的关键概念 在这一部分中&#xff0c;我们将使用之前学到的一些 OOP 概念。我们将创建一个小型应用程序。在继续之前&#xff0c;请阅读我的文章user-activity-log-using-C-Sharp-with-sql-server/。在本课程中&#xff0c;我们将再次使…

测试人员遇到需求变更 4大处理技巧

测试人员有效的需求变更管理&#xff0c;可以确保即使在需求频繁变化的情况下&#xff0c;测试工作仍然能够覆盖所有必要的功能点&#xff0c;从而保障最终产品的质量。如果没有合理的需求变更处理技巧&#xff0c;可能会造成不必要的返工和重复测试&#xff0c;无法维持项目的…

平安养老险浙江分公司开展防范非法集资宣传,守护群众“钱袋子”

为进一步提高群众对非法集资的防范意识的鉴别能力&#xff0c;近期&#xff0c;平安养老保险股份有限&#xff08;以下简称“平安养老险”&#xff09;浙江分公司以“守住钱袋子、护好幸福家”为宣传主题&#xff0c;深入居民社区、办公职场等公共场所开展的宣传活动。 平安养老…

看完再买不后悔!希喂、小米、霍尼韦尔宠物空气净化器性价比比拼

在忙碌的工作之余&#xff0c;养一只猫真的能治愈一切的不快&#xff0c;让我们的心灵得到片刻的宁静。然而&#xff0c;这份宁静背后&#xff0c;却隐藏着一些不易察觉的烦恼——猫浮毛和异味。 猫浮毛&#xff0c;这个看似微不足道的小问题&#xff0c;实则给许多宠物主人带…

PS选不了颜色和路径描边?PS不知为何才能描边任意路径,这个办法让你秒懂

在选中路径的情况下&#xff0c;按图下操作&#xff0c;即可制作路径&#xff08;不会让你选不了颜色和路径描边&#xff09;

第4章 工程经济评价指标 作业

第4章 工程经济评价指标 作业 一单选题&#xff08;共27题&#xff0c;100分&#xff09; (单选题)利息备付率是指( )与应付利息费用的比值。 A. 息税前利润 B. 利润总额 C. 净利润 D. 营业收入 正确答案: A:息税前利润; (单选题)当净现值( )0时,该项目不可行。 A. < B. …

leetcode:557. 反转字符串中的单词 III(python3解法)

难度&#xff1a;简单 给定一个字符串 s &#xff0c;你需要反转字符串中每个单词的字符顺序&#xff0c;同时仍保留空格和单词的初始顺序。 示例 1&#xff1a; 输入&#xff1a;s "Lets take LeetCode contest" 输出&#xff1a;"steL ekat edoCteeL tsetnoc…

分布式光纤测温DTS使用的单模光纤与多模光纤有何区别?

分布式光纤测温DTS中使用的单模光纤和多模光纤之间存在着本质区别。单模光纤是一种在光纤通信中应用广泛的光纤类型&#xff0c;几乎所有的光纤入户和主干线通信都采用单模光纤。从通信的角度来看&#xff0c;单模光纤就好比一条单行道的高速铁路&#xff0c;而多模光纤则类似于…

Leetcode - 周赛401

目录 一&#xff0c;3178. 找出 K 秒后拿着球的孩子 二&#xff0c;3179. K 秒后第 N 个元素的值 三&#xff0c;3180. 执行操作可获得的最大总奖励 I 四&#xff0c;3181. 执行操作可获得的最大总奖励 II 一&#xff0c;3178. 找出 K 秒后拿着球的孩子 本题可以直接模拟&a…

新手必读:平面设计自学全攻略

据说平面设计的门槛很低&#xff0c;零基础也很容易上手。但是据我所知许多初学者在自学平面设计时面临以下瓶颈&#xff1a;为什么跟着大神自学平面设计帖子依旧学不会呢&#xff0c;明明报了许多平面设计自学课程&#xff0c;但仍然不会自主设计&#xff0c;初学者到底从哪里…

XMind 2024软件最新版下载及详细安装教程

​人所共知的是XMind 在公司和教育领域都有很广泛的应用&#xff0c;在公司中它能够用来进行会议管理、项目管理、信息管理、计划和XMind 被认为是一种新一代演示软件的模式。也就是说XMind不仅能够绘制思维导图&#xff0c;还能够绘制鱼骨图、二维图、树形图、逻辑图、组织结构…