C:字符串函数(续)-学习笔记

news2024/9/20 1:56:37

一些闲话:

最近玩了这款饿殍-明末千里行,不知大家是否有听过这款游戏,颇有感触!!!

游戏中最让我难以忘怀的便是饿殍穗线的故事,生在如今时代的我之前无法理解杜甫在目睹人间悲剧时的心情,但现在我似乎有些能够共情了。在游戏中我以第三人称视角目睹了一个美好家庭的支离破碎的过程,那种痛苦难以言表。天灾人祸,饿殍遍野,有人在笑,有人在哭,富人更富,穷人死亡。

前言:

本篇文章将延续上一篇继续介绍字符串函数,希望能够对大家有所帮助!

话不多说,直接上正文

上一篇文章我们将字符串函数分为四类,并介绍了strlen函数

本篇文章小编将会带着大家了解后面三类字符串函数

1、 strcpy与strncpy

1.1  strcpy的使用和模拟

1.1.1  strcpy的使用

strcpy -- string copy   字符串拷贝

这是什么意思呢?

char ch1[] = "Hello,world";
char ch2[20];

比如说你有一个字符串ch1,想要将ch1中的内容拷贝到ch2中,这时候我们就可以使用strcpy函数了

那么这个函数该怎么使用呢?

函数原型:

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

我们来看一下cplusplus上是怎么说明的

 也就是说destination是目标空间,sourse是源头

我们将源头里的内容拷贝到目的地中,在结合我们上面举的例子,我们想将ch1中的内容拷贝到ch2中,那么ch1便是源头,ch2便是目标空间。

代码实现:

#include <stdio.h>
#include <string.h>
int main()
{
	char ch1[] = "Hello,world";
	char ch2[20];
	strcpy(ch2,  ch1);
	printf("%s\n", ch2);
	return 0;
}

 结果展示:

这便是strcpy的作用,当我们学会了strcpy的使用,我们还需要知道为什么,知其然知其所以然

我们验证一下有没有将\0一同拷贝

	char ch1[] = "Hello";
	char ch2[20] = "*************";//为了方便观察是否拷贝了\0
	strcpy(ch2,  ch1);

从调试过程中我们可以发现 \0 的确是拷贝过来了。 

既然strcpy会将源头内容中的\0拷贝过来,那么如果源头不包含\0的话,会有什么结果呢?

char ch1[] = { 'a','b','c'};
char ch2[20] = "*************";//为了方便观察是否拷贝了\0
strcpy(ch2,  ch1);

 

这里为什么会出现这样的错误呢?

就是因为源头内容中不存在\0。没有\0

strcpy拷贝就停不下来,会一直拷贝,直到越界报错。

因此 strcpy函数的使用需要注意几点

  • 源字符串必须以 '\0' 结束。
  • 目标空间必须足够大,以确保能存放源字符串
  • 目标空间必须可修改

 1.1.2  strcpy的模拟实现

当学会strcpy怎么使用后,我们也可以尝试自己写一个函数来模拟一下strcpy

#include <stdio.h>
#include <string.h>
void my_strcpy(char* ch2, char* ch1)
{
	while (*ch1 != '\0')
	{
		*ch2 = *ch1;
		*ch2++;
		*ch1++;
	}
	*ch2 = *ch1;//如果*ch1 = '\0',ch2 = '\0'
}
int main()
{
	char ch1[] = "hello";
	char ch2[20];
	my_strcpy(ch2,  ch1);
	printf("%s\n", ch2);
	return 0;
}

当ch1不等于 '\0' 的时候,就继续向后寻找,且令ch2 = ch1;

当ch1等于 '\0' 的时候,则不再向后寻找,令ch2 = ch1;

void my_strcpy(char* ch2, char* ch1)
{
	while (*ch1 != '\0')
	{
		*ch2 = *ch1;
		*ch2++;
		*ch1++;
	}
	*ch2 = *ch1;//如果*ch1 = '\0',ch2 = '\0'
}

上面那个模拟实现还可以进行一些优化,我们可以再进行简化一下

 比如说*ch2++和*ch1++可以与上面的合并一下

*ch2++ = *ch1++;	

后置++是用完之后再++,所以和前面的表示是一样的。

 前面我们将*ch1 分为是否等于 '\0',这里也可以合并一下

void my_strcpy(char* ch2, char* ch1)
{
	while (*ch2++ = *ch1++ )
	{
		;
	}
}

分析一下: 

 循环结束是因为\0 的ASCLL值为0,0为假,循环终止。

这样写的好处是既可以赋值,赋完的值还可以用来判断。

还有一个问题,我们使用strcpy函数主要是为了将源头内容拷贝到目标空间中,因此,我们不希望源头被改动,因此,可以使用const修饰一下源头

void my_strcpy(char* ch2, const char* ch1)

这样的话我们就不能够在my_strcpy函数中修改ch1中的内容了, 使得程序更加稳定。

关于指针我们最担心的是什么?我们会担心它是不是空指针,因此可以在解引用之前使用assert断言一下。

	assert(ch2 && ch1 );//只要有一个是空指针就会报错

注意,assert使用需要添加头文件<assert.h>

最终版本:

#include <stdio.h>
#include <string.h>
#include <assert.h>
void my_strcpy(char* ch2, const char* ch1)
{
	assert(ch2 && ch1 );//只要有一个是空指针就会报错
	while (*ch2++ = *ch1++ )
	{
		;
	}
}
int main()
{
	char ch1[] = "hello";
	char ch2[20];
	char* ret = my_strcpy(ch2,ch1);
	printf("%s\n", ret);
	return 0;
}

1.2 strncpy的使用 

这个函数和strcpy是不是很相似,它们不仅名字相似,用法也及其相似

他们之间就相差了一个变量,strncpy比strcpy多了一个变量num,那么num这个变量的作用是什么呢?

  num的作用

其实它们之间唯一的区别就是strncpy是长度受限制的字符串函数,而strcpy是长度不受限制的字符串函数,也就是说你源头字符串长度不论多少,都会全部给你 拷贝到目标空间中;而strncpy则会通过num的值来拷贝源头字符串长度到目标空间。

//我们要拷贝arr1中的前五个元素
char arr1[] = "hello world";
char arr2[30] = { 0 };
strncpy(arr2,arr1,5);
printf("%s ", arr2);

最终打印出来的便是我们想要的前五个字符。

有没有觉得strncpy函数比strcpy函数要灵活一些,使用strncpy函数我们可以根据自己的需求来决定拷贝多少字符,但是使用strcpy函数却只能拷贝全部的字符。

strncpy加上了一个长度的限制使得拷贝变的更加灵活。

char arr1[] = "he";
char arr2[30] = "xxxxxxxxx";
strncpy(arr2,arr1,5);
printf("%s ", arr2);

如果我们源头字符串不足所要拷贝的长度,会怎样呢?

从上图中我们可以看到,如果不满足条件,后面会自动补充上\0。

2、strcat与strncat

2.1 strcat的使用与模拟

2.1.1 strcat的使用

strcat的作用是实现字符串的链接 

那么什么是字符串的链接呢?

比如说有一个字符数组arr,内容是"hello",想要在后面追加world,这时候就需要使用strcat函数了

char arr[20] = "hello ";
strcat(arr, "world");

这样我们就可以将world链接到arr中,打印arr,可以看到此时arr的内容变成了 "hello world"

函数原型:

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

cplusplus中对strcat的解释:

这里有两个问题,第一个问题是strcat函数的追加是不是从我们字符串遇到的第一个\0开始追加?

第二个问题是 上图中的 '\0' 是原来的函数把 "world" 字符串中的' \0 '追加到arr中的吗?

我们来验证一下

char arr[20] = "hello\0xxxxxxx ";
strcat(arr, "world")

 

 

上图就可以解决我们的两个问题了,strcat函数的追加是从我们字符串遇到的第一个\0开始追加

"world" 字符串中的' \0 '也会追加到arr中

因此,我们可以整理一下strcat函数的追加方式:

1.找到目标空间中的第一个'\0';

2.然后从\0的位置开始追加源头字符串;

3.源头字符串的内容包括\0都会追加到目标空间。

2.1.2 strcat的模拟实现

学会了strcat函数的使用,接下来我们尝试模拟一下;

//dest指目标空间也就是arr;src指源头,也就是所要追加的字符串首字符的地址
char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest && src);
	//1.找目标空间中的\0
	while (*dest != '\0')
		dest++;
	//2.拷贝数据
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr[20] = "hello ";
	my_strcat(arr, "world");
	printf("%s ", arr);
	return 0;
}

提出一个问题,我们能不能用上面自己模拟的my_strcat来实现自己给自己追加的操作? 

因此,使用上面自己定义的函数来实现自己给自己追加最终会出现死循环问题。 

如果我们使用库里面的strcat函数呢?

当前程序可以,但是呢,没法保证所有程序都可以成功运行,一般我们不会使用这个函数给自己追加。 一般自己自己追加都使用 strncat 函数

2.2 strncat的使用 

strncat函数与strcat函数的用法也是一样的,都是实现字符串的链接,不过前者会加上链接字符的长度限制。

 

strncpy用法展示: 

char arr[30] = "hello ";
strncat(arr,"liangjing",5 );
printf("%s ", arr);

如果我们要追加的字符串少于所规定的长度呢?

char arr[30] = "hello\0xxxxxx ";
strncat(arr,"lj",5);
printf("%s ", arr);

我们可以发现strncat函数不会和strncpy函数一样,不满足会给你补\0, strncat函数只会把最多追加到目标空间。

3、strcmp与strncmp

3.1 strcmp的使用与模拟

3.1.1 strcmp的使用

strcmp -- string compare -字符串的比较

 strcmp函数的作用是比较两个字符串的大小

对应位置上的字符进行比较,按照ASCII值的大小,注意比较的不是字符串的长度。

比如说:

 函数原型:

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

我们来看一看这个函数的参数

const char * str1是一个字符指针,const char * str2 也是一个字符指针,该函数将两个指针指向的字符串进行比较,比较后,在通过返回值来告诉我们谁大谁小

这是什么意思呢?如果说str1指向的字符串比str2指向的字符串大,则返回一个大于0的数字,如果相等,返回0,如果str1指向的字符串比str2指向的字符串小,则返回一个小于0的数字。

char arr1[ ] = "abcdef ";
char arr2[ ] = "abc";
int ret = strcmp(arr1, arr2);
printf("%d",ret);

我们来看一下返回值大小:

返回1,也就是说明字符串arr1大于字符串2

我们也可以将这个代码换一种表示,或许会更加直观

int main()
{
	char arr1[ ] = "abcdef ";
	char arr2[ ] = "abc";
	int ret = strcmp(arr1, arr2);
	if (ret > 0)
		printf(">\n");
	else if (ret < 0)
		printf("<\n");
	else
	    printf("=\0");
	return 0;
}

这样使用比较符号可以更加直观。

注意:strcmp函数是通过比较两个字符串对应位置ASCII值的大小来判断两个字符串的大小,而不是通过字符串长度。 

3.1.1 strcmp的模拟实现

学会了使用strcmp,我们可以尝试自己来模拟实现该函数

int my_strcmp(const char* str1, const char* str2)
{
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
		
	}
	if (*str1 > *str2)
		return 1;
	else
		return -1;
}
int main()
{
	char arr1[ ] = "abcdef ";
	char arr2[ ] = "abc";
	int ret = my_strcmp(arr1, arr2);
	if (ret > 0)
		printf(">\n");
	else if (ret < 0)
		printf("<\n");
	else
	    printf("=\0");
	return 0;
}

3.2 strncmp的使用 

与上类似,直接展示用法:

#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
	char arr1[30] = "abcdef ";
	char arr2[30] = "abcmne";
	int ret = strcmp(arr1,arr2,3);
		if (ret > 0)
		printf(">\n");
	else if (ret < 0)
		printf("<\n");
	else
	    printf("=\0");
	return 0;
}

 这里的3是指只比较字符串arr1,arr2中前三个字符


结语:

本篇文章主要是介绍了strcpy,strcat,strcmp以及与它们极其相似的strncpy,strncat,strncmp函数,下篇文章将会结束字符串函数模块。

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

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

相关文章

低级编程语言和高级编程语言

一.区分低级编程语言和高级编程语言的方法 1.低级编程语言 低级编程语言,并不是简单的编程语言,而是写起来很费事的编程语言,如所有编程语言的"祖宗":汇编语言,写起来极其麻烦,说不定一个 int a1; 它就得写好几行,甚至十几行 这样麻烦的编程语言为什么还没消失那,因…

蓝桥杯省赛真题——大臣的旅费

输入样例&#xff1a; 5 1 2 2 1 3 1 2 4 5 2 5 4 输出样例&#xff1a; 135分析&#xff1a; 本题实际上要求我们去求在图中最远两点之间的距离&#xff0c;也就是树的直径 我们先从某一个点出发&#xff0c;到达离其最远的点&#xff0c;然后再重复操作一次即可 #inclu…

1184. 公交站间的距离(24.9.16)

题目 环形公交路线上有n个站&#xff0c;按次序从 0 到n - 1进行编号。已知每一对相邻公交站之间的距离&#xff0c;distance[i]表示编号为i的车站和编号为(i 1) % n的车站之间的距离。环线上的公交车都可以按顺时针和逆时针的方向行驶。要求返回乘客从出发点start到目的地de…

C++11(3)

目录 可变参数模版 获取参数包值的方式 1.递归方式展开参数包 2.使用数组逗号表达式展开 emplace_back函数 lambda表达式 C98中的例子 lambda表达式 语法 lambda表达式和函数比较 包装器 function包装器 bind绑定器 可变参数模版 C11 的新特性可变参数模板能够让您…

Java--String类

前言&#xff1a; 在之前的学习中&#xff0c;学习了和了解了一些类的基本使用&#xff0c;例如object类等等&#xff0c;但是我们用String这个引用或者说这个类其实我们已经用了好久&#xff0c;只不过没有具体分析过&#xff01; 对于String类&#xff0c;它可以引用一个字符…

VTD激光雷达(1)——01_OptiX_RayTracing-笔记

文章目录 前言一、文档介绍1、 总结 前言 不想学习怎么办 感谢VTD官方视频指导 一、文档介绍 1、 1 2 站在光的角度上考虑问题&#xff0c;如果用光源发出的&#xff0c;好多没到传感器上&#xff0c;这样会导致计算量很大&#xff0c;我们用传感器的trace 3 4 5 6 7 8 …

如何在 Vue 3 + Element Plus 项目中实现动态设置主题色以及深色模式切换

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、引言二、项目依赖和环境配置1. VueUse2. use-element-plus-theme3. 安装依赖 三、实现深色模式切换1. 设置深色模式状态2. 模板中的深色模式切换按钮3. 深色模式的效果展示 四、动态切换主题色五、总结 一、引言 在现代…

平安养老险阜阳中心支公司开展金融教育宣传专项活动

为全面深入开展“金融教育宣传月”的各项工作&#xff0c;不断完善金融惠民利民举措&#xff0c;提升金融服务质效&#xff0c;帮助基层群众增强维权意识、防非反诈的自我保护能力。近日&#xff0c;平安养老保险股份有限公司&#xff08;以下“平安养老险”&#xff09;阜阳中…

神经网络_使用tensorflow对fashion mnist衣服数据集分类

from tensorflow import keras import matplotlib.pyplot as plt1.数据预处理 1.1 下载数据集 fashion_mnist keras.datasets.fashion_mnist #下载 fashion mnist数据集 (train_images, train_labels),(test_images, test_labels) fashion_mnist.load_data()print("t…

食品包装识别系统源码分享

食品包装识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

IDEA复制代码到MD笔记格式还手动调,赶紧试试这个功能,一步到位

你是否曾经有过这种复制代码到笔记代码块的经历&#xff0c;选中后代码左侧有一些空格 然后粘到Markdown笔记里除第一行外&#xff0c;其他几行都要手动向前缩进&#xff0c;真是逼死强迫症啊 但是&#xff0c;其实idea工具中有一个“列选择模式”的功能&#xff0c;我们可以…

51单片机-LCD1602(液晶显示屏)- 写驱动

时间永远是检验真理唯一标准&#xff01;Whappy&#xff01; 主要简单写出几个驱动 初始化、显示字符、显示字符串、显示整形数据、有符号数据、十六进制、二进制&#xff01; void LCD_Init(); void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char); vo…

【网络安全的神秘世界】csrf客户端请求伪造

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 一、概述 跨站请求伪造&#xff0c;是一种挟持用户在当前已登陆的web应用程序上执行非本意操作的攻击方法&#xff0c;允许攻…

Comsol 利用多孔材料填充复合吸声器,拓宽低频完美吸声

参考文献&#xff1a;Cheng B , Gao N , Huang Y ,et al.Broadening perfect sound absorption by composite absorber filled with porous material at low frequency:[J].Journal of Vibration and Control, 2022, 28(3-4):410-424.DOI:10.1177/1077546320980214. 为了提高低…

端侧大模型系列 | 斯坦福手机端侧Agent大模型,为Android API而生!

0. 引言 峰峦或再有飞来&#xff0c;坐山门老等。泉水已渐生暖意&#xff0c;放笑脸相迎 小伙伴们好&#xff0c;我是微信公众号《小窗幽记机器学习》的小编&#xff1a;卖铁观音的小男孩。今天这篇小作文主要介绍端侧大模型中的函数调用&#xff0c;即常说的Function calling…

即插即用!高德西交的PriorDrive:统一的矢量先验地图编码,辅助无图自动驾驶

Driving with Prior Maps: Unified Vector Prior Encoding for Autonomous Vehicle Mapping 论文主页&#xff1a;https://misstl.github.io/PriorDrive.github.io/ 论文链接&#xff1a;https://arxiv.org/pdf/2409.05352 代码链接&#xff1a;https://github.com/missTL/Pr…

【数据结构】排序算法---直接插入排序

文章目录 1. 定义2. 算法步骤3. 动图演示4. 性质5. 算法分析6. 代码实现C语言PythonJavaCGo 7. 折半插入排序代码实现——C 结语 1. 定义 直接插入排序是一种简单直观的排序算法。它的工作原理为将待排列元素划分为「已排序」和「未排序」两部分&#xff0c;每次从「未排序的」…

PHP Swoole实现简易聊天室,附加小程序端连接websocket简易代码

目录 用到的工具&#xff1a; PHP Swoole拓展 | PHP Redis拓展 | Redis 7 一、安装上述必要工具&#xff08;下面是以宝塔面板中操作为例&#xff09; 给PHP安装Swoole和Redis拓展&#xff1a; 安装Redis软件 二、创建websocket服务器文件"wss_server.php" 具…

node.js+Koa框架+MySQL实现注册登录

完整视频展示&#xff1a;https://item.taobao.com/item.htm?ftt&id831092436619&spma21dvs.23580594.0.0.52de2c1bg9gTfM 效果展示&#xff1a; 一、项目介绍 本项目是基于node.jsKoamysql的注册登录的项目,主要是给才学习node.js和Koa框架的萌新才写的。 二、项目…

java数据结构----图

图的存储结构: 代码实现 public class Graph {// 标记顶点数目private int V;// 标记边数目private int E;// 邻接表private Queue<Integer>[] adj;public Graph(int v) {V v;this.E 0;this.adj new Queue[v];for (int i 0; i < adj.length; i) {adj[i] new Queu…