字符串和字符串函数(2)

news2024/11/20 23:35:11

前言:

        在字符串和字符串函数(1)-CSDN博客中,已将将字符串和字符函数的使用了解,并且实现模拟了一些字符串的库函数。        

        接下来将继续深入学习字符串和字符串函数。并且模拟实现一些较为复杂的函数。

可控制字符长的的函数

介绍并使用strncpy函数

       相同点 strncpy函数和strcpy函数的形同点是都可以实现字符串的拷贝,可以将一个字符串中的内容拷贝到另一个字符串中。

       不同点strnpy和strcpy函数的区别在于,strcpy只能将一个字符串中的所有内容全部拷贝到另一个字符串中,但是strnpy可以将一个字符串中的内容,有选择的拷贝到另一个字符串当中,参数也从2个变成了3个。   

例如:我要将arr1[]中的前3个字符拷贝到arr2[]中:

        可不可以这样实现,其实问题就在于传完3个字符之后,有没有将\0也传进去?

int main()
{
	char arr1[] = { "ab cd ef" };
	char arr2[20];
	strncpy(arr2, arr1, 3);
	printf("%s\n", arr2);
	return 0;
}

通过调试可以看到:

        在arr1中:

拷贝之后,arr2中是这样:

发现:并没有将'\0'考进去!

所以,分两种情况:

    1、如果我想把一个字符串中几个字符给拿出来,这时候需要注意我们要手动将拿出来的字符串中的最后一个字符后面的一个字符加上'\0'

     2、如果我只是想将一个字符串中的前几个元素做一个替换,此时就不需要手动加上'\0'。

例1:拿出arr1中的3个字符。

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = { "ab cd ef" };
	char arr2[20];
	strncpy(arr2, arr1, 3);
	arr2[3] = '\0';
	printf("%s\n", arr2);
	return 0;
}

打印结果:

        

例2:替换arr2中的前三个字符
        

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = { "ab cd ef" };
	char arr2[20] = {"hjklo"};
	strncpy(arr2, arr1, 3);
	printf("%s\n", arr2);
	return 0;
}

模拟实现strncpy函数:

        我们可以在strcpy的基础上再进行一些修改:

#include<stdio.h>
#include<assert.h>
char* my_strnpy( char* arr2, const char* arr1, size_t num)
{
	assert(arr1 != NULL);
	assert(arr2 != NULL);
	char* start = arr2;
	while (num)
	{
		*arr2++ = *arr1++;
		num--;
	}
	return start;
}

int main()
{
	char arr1[] = {"love you"};
	char arr2[] = { "like me" };
	my_strnpy(arr2, arr1,4);
	printf("%s\n", arr2);
	return 0;
}

介绍并使用strncat函数:

与strcat相比较:

相同点都可以实现对字符串的追加。

不同点strncat按自我需求调整所追加的字符串的长度。

                strcat只能将一个字符串全部内容追加到另一个字符串当中。

追加的时候是从目的地字符数组的'\0'处开始追加。

看一组代码:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = {"abcd"};
	char arr2[20] = { "asdf " };
	strncat(arr2, arr1, 4);
	printf("%s\n", arr2);
	return 0;
}

通过调试来观测一下:

        初始化之后:

进入函数之后:

        

注意:这是后也没有主动追加'\0',可以最后手动追加

模拟实现strncat函数:

        

char* my_strncat(char* arr1, const char* arr2, size_t num)
{
	assert(arr1 != NULL);
	assert(arr2 != NULL);
	char* start = arr1;
	while (*arr1)
	{
		arr1++;
	}
	while (num)
	{
		*arr1++ = *arr2++;
		num--;
	}
	return start;
}
int main()
{
	char arr1[20] = {"love me "};
	char arr2[] = { "like you" };
	my_strncat(arr1, arr2, 4);
	printf("%s\n", arr1);
	return 0;
}

介绍并使用strncmp函数:

        

相关介绍和返回值,发现和strcmp函数的返回值是一样的。

区别:除了多了一个参数num,也就是你要比较几个字符之外,其他的都是一样的。

int main()
{
	char arr1[] = {"abcdef"};
	char arr2[] = { "abcds" };
	int a =  strncmp(arr1, arr2,5);
	if (a == 0)
	{
		printf("=");
	}
	else if (a < 0)
	{
		printf("<");
	}
	else
	{
		printf(">");
	}

	return 0;
}

模拟实现strncmp函数

        

int my_strncmp(const char* arr1, const char* arr2, size_t num)
{
    assert(arr1 != NULL);
    assert(arr2 != NULL);
	while ( *arr1 - *arr2 == 0)
	{
		arr1++;
		arr2++;
		num--;
		if (num-1 == 0)
		{
			break;
		}
	}
	return *arr1-*arr2;
}
int main()
{
	char arr1[] = {"abcd"};
	char arr2[] = {"abfde"};
	int a = my_strncmp(arr1,arr2,4);
	 if (a == 0)
			{
				printf("=");
			}
			else if (a < 0)
			{
				printf("<");
			}
			else
			{
				printf(">");
			}
	return 0;
}

其他类型库函数

介绍并使用strstr函数:

        

返回指向 str1 中首次出现的 str2 的指针,如果 str2 不是 str1 的一部分,则返回空指针.

        

int main()
{
	char arr1[] = {"love you best"};
	char *arr =  strstr(arr1, "you");
	printf("%s\n", arr);
	return 0;
}

   在arr1中找you,找到第一次出现的地方返回第一次出现的首字符的地址。

打印结果:

如果找不到,就返回NULL;

例如:

int main()
{
	char arr1[] = {"love you best"};
	char *arr =  strstr(arr1, "yoo");
	printf("%s\n", arr);
	return 0;
}

打印结果:

注:这里找相同的字符串必须完全相同,差一个字符都不行!!

模拟实现strstr函数:

        首先通过画图来讲解:

        

char* my_strstr(const char* arr1, const char* arr2)
{
	char* cp = (char*)arr1;//表示arr1的红色指针
	char* a;//表示arr1的绿色指针
	char* b;//表示arr2的绿色指针
	while (*cp)
	{
		if (*cp == *arr2)
		{
			a = cp;
			b = (char*)arr2;
			while (*cp == *b)
			{
				cp++;
				b++;
				if (*b == '\0')
					return a;
			}
		}
		cp++;
	}
	return NULL;
}
int main()
{
	char arr1[] = { " welcom next new world" };
	char arr2[] = {"new"};
	 char *str = my_strstr(arr1, arr2);
	 printf("%s\n", str);
	return 0;
}

介绍并使用memcpy(内存拷贝函数)函数:

        

之前探讨了strcpy和strncpy,这两个库函数是用来拷贝字符串的,其它类型的不能拷贝,那么其他类型的数据该如何拷贝呢?

        就需要用到memcpy函数,可以拷贝任意类型的数据!

例如:

int main()
{
	int arr1[] = {1,2,3,4,5};
	int arr2[20];
	memcpy(arr2,arr1, 20);
	return 0;
}

需要三个参数,目的地地址,源头地址,需要拷贝的字节数,

        通过调试发现:

拷贝成功!

模拟实现memcpy函数

void* my_memcpy(void* str1, const void *str2, size_t sz)
{
	void* start = str1;
	assert(str1 && str2);
	while (sz)
	{
		*(char*)str1 = *(char*)str2;
		str1 = (char*)str1 + 1;
		str2 = (char*)str2 + 1;
		sz--;
	}
	return start;
}
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[10];
	my_memcpy(arr2,arr1,20);
	return 0;
}

拷贝完成!

但是,这回突发奇想,想将arr1中的前4个拷贝到从第二个整形开始。可不可以呢?

void* my_memcpy(void* str1, const void *str2, size_t sz)
{
	void* start = str1;
	assert(str1 && str2);
	while (sz)
	{
		*(char*)str1 = *(char*)str2;
		str1 = (char*)str1 + 1;
		str2 = (char*)str2 + 1;
		sz--;
	}
	return start;
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9 };
	my_memcpy(arr1+2,arr1,16);
	return 0;
}

发现是不可以的,那么如果我要实现重叠部分的拷贝,应该怎么办,可以使用库函数中的memmove函数!

        接下来就来探究一下memmove函数!

介绍并使用memmove函数

和memcopy相比,memmove可以完成重叠部分的拷贝工作!

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9 };
	memmove(arr1+2,arr1,16);
	return 0;
}

发现可以进行重叠部分的拷贝!

模拟实现memmove函数:

        那么应该怎样拷贝,才不会出现在memcpy中的情况呢,这里分为三种情况:

1、源头地址在目的地的左侧,源头低地址,目的地高地址。

此时不会再出现被覆盖的情况。

2、源头地址在目的地的左右侧,源头高地址,目的地低地址。

3、源头和目的地没有重叠部分

这种情况怎么拷贝都行,只要空间够用即可。

以上这几种情况就是memmove函数的思路!

以下是代码:

void* my_memmove(void* str1, void* str2, size_t num)
{
	void* start = str1;
	int sz = num;
	if (str1 > str2)
	{
		while (sz)
		{
			str2 = (char*)str2+1;
			str1 = (char*)str1 + 1;
			sz--;
		}
		while (sz != num)
		{
			*(char*)str1 = *(char*)str2;
			str1 = (char*)str1-1;
			str2 = (char*)str2 - 1;
			sz++;
		}
	}
	else 
	{
		while (sz--)
		{
			*(char*)str1 = *(char*)str2;
				str1 = (char*)str1 + 1;
				str2 = (char*)str2 + 1;
		}
	}
	return start;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9 };
	my_memmove(arr1,arr1+2,12);
	return 0;
}

        

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

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

相关文章

香港云服务器好还是国内的好?

香港云服务器与国内云服务器各有其优点和缺点&#xff0c;选择哪种类型的云服务器主要取决于业务需求、用户群体、网络需求以及成本考虑。以下是对两者进行详细比较的内容。 首先&#xff0c;从网络速度和稳定性来看&#xff0c;香港云服务器具有独特的优势。由于香港是全球数据…

day18

第一题 493. 翻转对 这道题我们采用分治的思想&#xff0c;通过单调性和双指针的策略来解决&#xff1a; 策略一&#xff1a; 我们通过递归将每一个分的左区域和右区域变成降序排列&#xff0c;最后在同一层的左右区域进行判断&#xff0c;当前数组为降序时&#xff0c;固定每…

Redis-Cluster模式基操篇

一、场景 1、搞一套6个主节点的Cluster集群 2、模拟数据正常读写 3、模拟单点故障 4、在不停服务的情况下将集群架构改为3主3从 二、环境规划 6台独立的服务器&#xff0c;端口18001~18006 192.169.14.121 192.169.14.122 192.169.14.123 192.169.14.124 192.169.14.125 192…

.NET数据交互之生成和读取YAML文件

最近在项目中&#xff0c;业务上需要与Python进行交互&#xff0c;而Python程序用的配置文件主要是YAML&#xff0c;程序以命令行形式运行&#xff0c;前端页面由C#通过WPF开发完成。现在需要通过C#生成YAML配置文件&#xff0c;并经过Python读取和修改后&#xff0c;再次由C#进…

【数据结构】详解二叉树

文章目录 1.树的结构及概念1.1树的概念1.2树的相关结构概念1.3树的表示1.4树在实际中的应用 2.二叉树的结构及概念2.1二叉树的概念2.2特殊的二叉树2.2.1满二叉树2.2.2完全二叉树 2.3 二叉树的性质2.4二叉树的存储结构2.4.1顺序结构2.4.2链表结构 1.树的结构及概念 1.1树的概念…

从人工向智能化转变,企业级指标管理平台建设实战

随着大数据技术和人工智能的发展&#xff0c;企业逐渐意识到构建一个集中化的指标管理平台的必要性。这样的平台旨在解决几个核心问题&#xff1a;首先&#xff0c;确保所有部门都能通过统一的入口提交指标需求&#xff0c;实现需求的透明化管理&#xff1b;其次&#xff0c;建…

短剧、全家桶和大模型,谁是“史上最难618”解药?

今年618一如既往地难&#xff0c;不仅竞争加剧&#xff0c;声量也不尽如人意。618大促话题整体关注量也就略胜“京东亲严查考勤”事件一筹。 图源&#xff1a;微博话题数据 大环境如此&#xff0c;大促时间越来越长&#xff0c;平台竞争愈演愈烈。今年多数平台5月20日前后就抢…

Docker 环境下 3D Guassian Splatting 的编译和配置

Title: Docker 环境下 3D Guassian Splatting 的编译和配置 文章目录 前言I. 宿主系统上的安装配置1. 安装 nvidia driver2. 安装 docker3. 安装 nvidia-container-toolkit II. Docker 容器安装配置1. 拉取 ubuntu 22.042. 创建容器3. 进入容器4. 容器中安装 cuda SDK5. 容器中…

基于ELK的日志管理【开发实践】

文章目录 一、ELK简介1.1 ELK的作用与应用1.2 ELK的组成1.3 Elasticsearch1.4 Logstash1.5 Kibana1.6 ELK架构简述1.7 基础知识1.7.1 数据格式1.7.2 正排索引和倒排索引1.7.3 全文搜索 二、ES入门---基于HTTP的使用方式&#xff08;了解&#xff09;2.1 索引操作2.1.1 创建索引…

React-生成随机数和日期格式化

生成随机数 uuid文档&#xff1a;https://github.com/uuidjs/uuid npm install uuid import {v4 as uuidV4} from uuid 使用&#xff1a; uuidV4() 日期格式化 dayjs文档&#xff1a;安装 | Day.js中文网 npm install dayjs import dayjs from dayjs

【监控】prometheus自定义指标 exporter

一、【写在前面】 prometheus自定义指标本质是用代码自己写一个网络访问的采集器&#xff0c;你可以在官网看到&#xff0c;Client libraries | Prometheus官方支持的语言有GO JAVA PYTHON RUBY RUST, 第三方的库就支持的更多了&#xff0c;有BASH C CPP LUA C# JS PHP R PER…

如何解决研发数据传输层面安全可控、可追溯的共性需求?

研发数据在企业内部跨网文件交换&#xff0c;是相对较为普遍而频繁的文件流转需求&#xff0c;基于国家法律法规要求及自身安全管理需要&#xff0c;许多企业进行内部网络隔离。不同企业隔离方案各不相同&#xff0c;比如银行内部将网络隔离为生产网、办公网、DMZ区&#xff0c…

Superset二次开发之Github项目推送到GitLab仓库

以下是从GitHub克隆Superset项目并将其推送到GitLab的详细操作步骤 lab 地址: xxx lab 配置: 生成SSH密钥 ssh-keygen -t rsa -b 4096 -C "邮箱地址" 默认情况下密钥会生成在~/.ssh (/c/Users/Administrator/.ssh/id_rsa)目录下。 公钥添加到GitLab: 打开公钥文件…

学校机房批量控制关机源码

学校机房批量控制关机源码 效果图源码说明源码领取下期更新预报 效果图 源码说明 本源码由易语言编写&#xff0c;希望各位用在正道上。下面我给你们教一下IP查看方法&#xff1b; 第一步&#xff1a;‘’winr‘’&#xff0c;打开运行框&#xff0c;输入’‘cmd’回车。 输…

【介绍下SCSS的基本使用】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

东莞酷得智能 组装机械狗电子玩具方案

这款机械狗玩具电子方案结合了现代电子技术和人工智能元素&#xff0c;旨在为用户提供一个高科技、互动性强的娱乐体验。通过不断的软件更新和硬件迭代&#xff0c;机械狗的功能将持续扩展。 一、功能特点&#xff1a; 1、自动巡游&#xff1a;机械狗能够自主在房间内巡游&am…

IP地址开启HTTPS方法

可以使用IP地址申请SSL证书&#xff0c;申请之前必须是公网IP地址&#xff0c;不支持内网IP地址申请。 申请过程需要确定IP地址外网可以访问&#xff0c;这里特别注意只是申请过程中可以访问。访问验证过程必须采取80端口、443端口两者选择1个&#xff0c;不可以用其它端口进行…

docker部署owncloud进行管理

目录 一.拉取镜像 1.使用mysql和owncloud最新版镜像&#xff0c;构建个人网盘 2.查看是否已经正确监听端口 二.使用浏览器进行测试 1.使用IP:8080进行访问&#xff0c;用admin运行容器时设置的密码登录 2.查看到已经有的文件 3.文件上传对应的位置 4.在web页面进行简单…

更换固件后飞控OSD叠显不对/叠显不显示/叠显乱码问题

笔者用的飞控型号为SpeedyBeeF405V4的飞控&#xff0c;OSD叠显芯片型号为AT7456E。 我的这款飞控是支持两款固件的&#xff0c;INAV和BetaFlight。 开始飞控的默认固件为BetaFlight&#xff0c;切换INAV固件后&#xff0c;进行OSD调整&#xff0c;但发现水平线无法正常显示&…

【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(二)

Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 5、Spring Data JPA自定…