各类字符串函数和内存函数的使用以及模拟(万字解析)

news2025/2/28 15:37:23

函数

  • 一.字符串函数(使用都需要包含string.h)
    • 1.求字符串长度—strlen
    • 2.长度不受限制的字符串函数
      • 1.strcpy-字符串拷贝
      • 2.strcat-追加字符串
      • 3.strcmp-字符串比较
      • 4.为什么长度不受限制
    • 3.长度受限制的字符串函数—strncopy,strncat,strncmp
    • 4.字符串查找
      • 1.strstr-判断是否为子字符串
      • 2.strtok-一个奇怪的函数
    • 5.错误信息查找-strerror
  • 二.内存函数(也需要包含string.h)
    • 1.内存拷贝函数—memmove
    • 2.内存填充函数-memset
    • 3.内存比较函数-memcmp

在这里插入图片描述

一.字符串函数(使用都需要包含string.h)

1.求字符串长度—strlen

库函数的使用

字符串的结尾标志是\0,而strlen统计的就是\0出现之前字符的个数。(不包含\0)

在这里插入图片描述

注意,在库里该函数的参数是字符串的起始地址,返回类型是size_t,也就是无符号整形。在设计者的眼中,长度肯定是非负数,所以这样设计。但实际这样会有一些使用上细节的差异,例如你就不能如下使用。

在这里插入图片描述

模拟实现(ps:有多种方法,这里写最简单的方法)


#include<assert.h>
#include<stdio.h>
#include<string.h>
int my_strlen(const char* s)
{
	assert(s);
	int n = 0;
	while (*s != '\0')
	{
		s++;
		n++;
	}
	return n;
}
int main()
{
	char* s = "abcd";
	int p = my_strlen(s);
	printf("%d", p);
}

可以看到,我加上了const和assert。其实这两个东西是来保障代码的安全性的,当然不加也能运行。但作为一个好的程序员,应该经常使用const和assert。const的作用是保证所对应的变量不能被修改。asser是断言,在这就是如果s为空指针,编译器就会发出警告以方便程序员查找问题。

返回型是int还是size_t没有好坏之分,要看使用的场景。

2.长度不受限制的字符串函数

1.strcpy-字符串拷贝

库函数的使用

在这里插入图片描述

将后面的拷贝到前面的字符串里。

在这里插入图片描述

1.源字符串必须以 ‘\0’ 结束。
2.会将源字符串中的 ‘\0’ 拷贝到目标空间。
3.目标空间必须足够大,以确保能存放源字符串。
4.目标空间必须可变。

前三点很好理解,拷贝首先得需要知道要拷贝的长度啊,那就必须得有\0。然后拷贝字符时会将\0一起拷过去。

在这里插入图片描述

第四点空间必须可被改变的意思是不能为常量字符串。

在这里插入图片描述

模拟实现

char* my_strcpy(char* s1, const char* s2)//要拷贝的字符串不可被改变
{
	char* ret = s1;
	assert(s1 && s2);//都不为空指针
	while (*s1++ = *s2++)
	{
		;
	}
	return ret;
}
int main()
{
	char s1[20] = "abcdefg";
	char s2[] = "hello";
	my_strcpy(s1, s2);
	printf("%s", s1);
	return 0;
}

2.strcat-追加字符串

在这里插入图片描述

把后面的字符加到前面来。

在这里插入图片描述

从源字符串的\0开始追加,并且会覆盖掉源字符串的\0。

在这里插入图片描述

模拟实现

char* my_strcat(char* s,const char* str)
{
	assert(s && str);
	char* ret = s;
	//找到源字符串\0
	while (*s != '\0')
	{
		s++;
	}
	//追加
	while (*s++ = *str++)
	{
		;
	}
	return ret;
}
int main()
{
	char s[20] = "hello ";//我想在后面追加一个world
	my_strcat(s, "world");
	printf("%s", s);
	return 0;
}

3.strcmp-字符串比较

在这里插入图片描述
在这里插入图片描述

第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字

该函数是按字典序来比较的。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在VS上返回大于0的是1,小于0的是-1。

模拟实现

int my_strcmp(const char* s1,const char* s2)
{
	assert(s1 && s2);
	while (*s1 == *s2)
	{
		if (*s1 == '\0')
		{
			return 0;
		}
		s1++;
		s2++;
	}
	if (s1 > s2)
		return 1;
	else
		return -1;
}
int main()
{
	char s1[] = "abcd";
	char s2[] = "abc";
	int ret =my_strcmp(s1, s2);
	printf("%d", ret);
	return 0;
}

4.为什么长度不受限制

前面三个函数压根不关心到底拷贝,追加,比较了几个字符。它们只关心是否找到了\0,一旦找到了\0就会停止。这样的话如果目标空间不够大,会造成越界。这些特点就会让人们决定它是不安全的,下面介绍安全的函数。

3.长度受限制的字符串函数—strncopy,strncat,strncmp

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

可以看到其实这些函数的原理并没有改变,只是多出了一个参数。这个参数就是用来限制它们的长度的。

一个例子

在这里插入图片描述

这里只拷贝了三个字符,自然没拷贝\0,所以字符串结束的\0在s1里。其他函数也是如上使用。

4.字符串查找

1.strstr-判断是否为子字符串

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

模拟实现

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

在这里插入图片描述


char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	if (*str2 == '\0')
	{
		return (char*)str1;//如果传的是空字符串就毫无意义,直接返回,str1类型是const char*,这里强转一下避免警告
	}
	const char* s1 = str1;//s1代表str1正在匹配的位置
	const char* s2 = str2;//s2代表str2正在匹配的位置
	const char* cp = str1;//cp代表开始匹配的位置
	while (*cp != '\0')
	{
		s1 = cp;//让str1从cp位置再开始匹配
		s2 = str2;//str2回到初始位置,再从第一个元素开始匹配
		while ((*s1!='\0')&&(*s2!='\0') &&(* s1 == *s2))
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return (char*)cp;//查找成功,cp类型是const char*,这里强转一下避免警告
		}
		if (*s1 == '\0')
		{
			break;
		}
		cp++;//像后移一位,避免重复元素影响判断
	}
	return NULL;//没有找到
}
int main()
{
	char s1[] = "abbbbbcdef";
	char s2[] = "bbcdq";
	char* ret = my_strstr(s1, s2);//判断s2是否为s1的子字符串
	if (ret == NULL)//如果是则返回s2在s1里第一次出现时的地址否则就返回空指针
	{
		printf("找不到\n");
	}
	else
	{
		printf("%s\n", ret);
	}
	return 0;
}

2.strtok-一个奇怪的函数

在这里插入图片描述

1.sep参数是个字符串,定义了用作分隔符的字符集合第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。

2.strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
3. strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
4.strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
5.如果字符串中不存在更多的标记,则返回 NULL 指针。

用来分割字符串。一个例子,例如我的邮箱是xxxxx@163.com。这个邮箱起始由三部分组成,一个是xxxxxx,一个是163,一个是com。我现在想把这三部分分开。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

以下可以简写

在这里插入图片描述
在这里插入图片描述

5.错误信息查找-strerror

在这里插入图片描述

在这里插入图片描述

具体使用(稍微有些超纲,只是用来展示使用方法)

在这里插入图片描述
在这里插入图片描述

此时我们只看到打开文件失败,但是不知道具体原因,所以需要使用该函数。

在这里插入图片描述

在这里插入图片描述

因为我的路径下没有这个文件,所以就是找不到。

另一个相对应的函数-perror

这个函数很简单,就是printf+strerrror的结合。

在这里插入图片描述

二.内存函数(也需要包含string.h)

1.内存拷贝函数—memmove

在这里插入图片描述

前面介绍过strcpy,将一个字符串的内容拷贝到另一个字符串里,但它只能作用于字符串。但memmove能够作用于任意类型,它的拷贝单位是字节。

该函数也是将后面的拷贝进前面的,第三个参数是拷贝的字节多少。

在这里插入图片描述

在这里插入图片描述

模拟实现

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

其实这样写是不严谨的,如果我们只在一个字符串里操作就会出现问题。例如我想把arr1里的1,2,3,4,5拷贝到3,4,5,6,7上就,理论上arr1[]应该变为1,2,1,2,3,4,5,8,9。

在这里插入图片描述

很明显这是不符合预期的,因为前面复制到后面的会直接将后面的覆盖掉,所以我们得完善判断方法。

在这里插入图片描述

对于这种情况,我们考虑从后往前拷贝。

在这里插入图片描述

是不是我们所有的直接从后往前拷贝就行了呢?当然也不行,还有以下这种情况。

在这里插入图片描述

所以接下来分情况讨论。

在这里插入图片描述

在这里插入图片描述

符合我们的预期。

以下是源码

#include<assert.h>
#include<stdio.h>
#include<string.h>
void* my_memmove(void* dest, const void* stc, size_t num)
{
	void* ret = dest;
	assert(dest && stc);
	if (dest < stc)//dest在stc左边,从前往后拷贝
	{
		while (num--)
		{
			*(char*)dest = *(char*)stc;
			dest = (char*)dest + 1;
			stc = (char*)stc + 1;
		}
	}
	else//从后往前拷贝
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)stc + num);
		}
	}
	return ret;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9 };
	my_memmove(arr1+2, arr1, 20);//把一个数组前面的拷贝到后面来

	return 0;
}

追加个知识,memcopy在vs上跟memmove没有区别。在其他环境下可能不能实现上面的重复问题。

2.内存填充函数-memset

在这里插入图片描述

在dest数组里,把count个字节设置成c。

在这里插入图片描述

但要注意的是memset是按照字节来改变的,也就是说如果我们要改变一个int类型的数组,可能就会出现错误。

在这里插入图片描述

这里是按16进制显示,每两个数表示一个字节。我们可以看到每个字节都变为了01。实际结果并不是我们所期望的1.

3.内存比较函数-memcmp

在这里插入图片描述

按字节比较大小,如果大于返回大于0的数;如果小于,返回小于0的数;如果等于,返回0.

在这里插入图片描述

在这里插入图片描述

这里也能比较任意类型数据,因为是按照字节大小来比较的,所有数据都会转化成字节来比较。

在这里插入图片描述

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

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

相关文章

Linux 文件句柄导致系统压力测试时出现错误率

最近&#xff0c;在对一个golang写的获取商品详情信息的接口做压力测试时&#xff0c;tps 单机可以达到1400多&#xff0c;但是发现每当压力测试开始2分钟多时就会出现502或504 错误&#xff0c;整体的错误率在0.5%左右。一开始是怀疑代码写的效率不高&#xff0c;是不是协程开…

【SAP Hana】SAP HANA SQL 进阶教程

SAP HANA SQL 进阶教程5、HANA SQL 进阶教程&#xff08;1&#xff09;Databases&#xff08;2&#xff09;User & Role&#xff08;3&#xff09;Schemas&#xff08;4&#xff09;Tables&#xff08;5&#xff09;Table Index&#xff08;6&#xff09;Table Partitions&…

于仕琪C/C++ 学习笔记

C函数指针有哪几类&#xff1f;函数指针、lambda、仿函数对象分别是什么&#xff1f;如何利用谓词对给定容器进行自定义排序&#xff1f;传递引用和传递值的区别&#xff1f;传递常引用和传递引用之间的区别&#xff1f;传递右值引用和传递引用之 间的区别&#xff1f;函数对象…

【PWA学习】6. 使用 Service Worker 进行后台同步

引言 你一定遇到过类似这样的场景&#xff1a; 当用手机填写完一张信息表单点击"提交"时&#xff0c;恰好手机网络很差或没有网络&#xff0c;这时候只能盯着手机看着旋转的小圆圈。经过长时间等待后依然没有结果&#xff0c;这时候关闭浏览器&#xff0c;请求也被终…

红外传感器使用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、红外传感器&#xff1f;二、使用步骤1.确保驱动已经安装2.安装GPIO工具3.安装GPIO的Python支持4.Python3代码5.测试结果总结前言 最近在做一个项目需要用到…

Linux命令学习

1、linux目录结构 linux目录结构是一个树状结构 当我们直接打开ubuntu的控制台&#xff0c;进入的是 home 目录下的创建的用户&#xff0c;这里是真正的 家 目录 或者在安装 ssh 服务器之后可以直接通过 windows 命令行 访问 ubuntu 的ssh服务器&#xff0c;进入的是 home 目录…

【规范】我们是怎么做MySQL数据库安全管理的?

一、背景说明 MySQL作为数据库管理系统&#xff0c;里面保存企业的重要业务数据&#xff0c;因此保证数据库的安全性非常重要&#xff0c;如何保证数据库的安全性呢&#xff1f;用户和用户权限管理是一个很重要的方面。 MySQL数据库具有非常高的安全性&#xff0c;为我们提供…

Vue 2 即将成为过去

自从 2020 年 9 月 18 日 Vue 3 正式发布以来&#xff0c;已经有两年多时间了&#xff0c;终于在 2022 年 2 月 7 日 Vue 作者发布了一则消息&#xff1a;Vue 3 将成为新的默认版本。与此同时&#xff0c;Vue 相关官方周边的核心库 latest 发布标签将指向其 Vue 3 的兼容版本。…

从0到1完成一个Vue后台管理项目(二十一、网上地图资源、树形控件及路由权限分析、路由守卫)

往期 从0到1完成一个Vue后台管理项目&#xff08;一、创建项目&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;二、使用element-ui&#xff09; 从0到1完成一个Vue后台管理项目&#xff08;三、使用SCSS/LESS&#xff0c;安装图标库&#xff09; 从0到1完成一个Vu…

JAVA SE 详解类和对象

类和对象 面向对象的初步认知 什么是面向对象 Java是一门纯面向对象的语言(Object Oriented Program&#xff0c;简称OOP)&#xff0c;在面向对象的世界里&#xff0c;一切皆为对象。 面 向对象是解决问题的一种思想&#xff0c;主要依靠对象之间的交互完成一件事情。用面向…

ArcGIS基础实验操作100例--实验79分区统计降雨量

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验79 分区统计降雨量 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&am…

【VUE2-02】vue2的指令和vue2的缺点

文章目录一、vue条件渲染 v-if二、vue循环渲染 v-for三、vue的事件 v-on四、vue的双向绑定 v-model五、VUE2的缺点5.1 vue底层原理解决方案在上节 【VUE2-01】vue2的起步,中写hello world&#xff01;例子的时候使用了一个指令 v-bind绑定元素属性一、vue条件渲染 v-if v-if控…

米筐量化终端是什么?

米筐量化终端大家应该也能想象到是应用的终端&#xff0c;是系统执行的终端环节&#xff0c;如果是用在量化方面&#xff0c;那它就是策略定制的终端&#xff0c;是方便投资者输入量化策略执行出来发最终优质目的&#xff0c;精确到细分股票的账户成交量&#xff0c;股价以及融…

java8 第七章-----多线程

7.1、线程基本知识 进程与线程&#xff1a; 进程&#xff08;Process&#xff09;是系统进行资源分配和调度的基本单位&#xff0c;是操作系统结构的基础。在早期面向进程设计的计算机结构中&#xff0c;进程是程序的基本执行实体&#xff1b;在当代面向线程设计的计算机结构…

cobaltstrike的安装与基础使用

数据来源 本文仅用于信息安全学习&#xff0c;请遵守相关法律法规&#xff0c;严禁用于非法途径。若观众因此作出任何危害网络安全的行为&#xff0c;后果自负&#xff0c;与本人无关。 Cobalt Strike是什么? Cobalt Strike&#xff08;简称CS&#xff09;是一款团队作战渗…

Python学习笔记——列表

Python列表函数&方法函数&#xff1a;len(list)&#xff1a;列表元素个数max(list)&#xff1a;返回列表元素最大值min(list)&#xff1a;返回列表元素最小值list(seq)&#xff1a;将元组转换为列表方法list.append(obj)&#xff1a;在列表末尾添加新的对象list.count(obj)…

字典树总结

字典树 一、概念 字典树 (Trie) 是一种用于实现字符串快速存储和检索的多叉树结构&#xff1b; Trie 的每个节点都拥有若干个字符指针&#xff0c;若在插入或检索字符串时扫描到一个字符 ccc &#xff0c;就沿着当前节点的 ccc 字符指针&#xff0c;走向该指针指向的节&…

支持向量机(SVM)入门(六,解决遗留问题)

但也出现了如下结果&#xff0c;看来任务没完没了&#xff08;svm深似海&#xff1f;&#xff09;&#xff0c;还得下功夫&#xff1a;前面我们的支持向量机到此便止步了&#xff0c;一晃两年&#xff0c;这几天有空&#xff0c;顺便研究了一下&#xff0c;问题有进展&#xff…

基于yolov5-v7.0开发构建汽车车损实例分割检测识别分析系统

在之前的文章中我们已经做了很多基于yolov5完成实例分割的项目&#xff0c;感兴趣的话可以自行移步阅读&#xff1a;《基于YOLOv5-v7.0的药片污染、缺损裂痕实例分割检测识别分析系统》《基于yolov5-v7.0开发构建裸土实例分割检测识别模型》《基于yolov5-v7.0开发实践实例分割模…

NSThead的进阶使用和简单探讨

概述 NSThread类是一个继承于NSObjct类的轻量级类。一个NSThread对象就代表一个线程。它需要管理线程的生命周期、同步、加锁等问题&#xff0c;因此会产生一定的性能开销。 使用NSThread类可以在特定的线程中被调用某个OC方法。当需要执行一个冗长的任务&#xff0c;并且不想…