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

一些闲话:

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

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

前言:

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

话不多说,直接上正文

上一篇文章我们将字符串函数分为四类,并介绍了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] = "*************";//为了方便观察是否拷贝了\0strcpy(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.找目标空间中的\0while (*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");elseprintf("=\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;elsereturn -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");elseprintf("=\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");elseprintf("=\0");return 0;
}

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


结语:

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

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

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

相关文章

【网络原理】❤️Tcp 连接管理机制❤️ “三次握手” “四次挥手”的深度理解, 面试最热门的话题,没有之一, 保姆式教学 !!!

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

华为 HCIP 认证费用和报名资格

在当今竞争激烈的信息技术领域&#xff0c;华为 HCIP认证备受关注。它不仅能提升个人的技术实力与职业竞争力&#xff0c;也为企业选拔优秀人才提供了重要依据。以下将详细介绍华为 HCIP 认证的费用和报名资格。 一、HCIP 认证费用 华为HCIP认证的费用主要由考试费和培训费构成…

电气自动化入门01:电工基础

视频链接&#xff1a;1.1 电工知识&#xff1a;电工基础_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1PJ41117PW?p2&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5 1.电能和电力系统 2.电工常用物理量及其应用 2.1电阻&#xff1a; 2.2电流&#xff1a; 2.3电压&…

【C++】入门基础(下)

Hi&#xff01;很高兴见到你~ 目录 7、引用 7.3 引用的使用&#xff08;实例&#xff09; 7.4 const引用 【第一分点】 【第二分点1】 【第二分点2】 7.5 指针和引用的关系&#xff08;面试点&#xff09; 8、inline 9、nullptr Relaxing Time&#xff01; ———…

系统 IO

"裸奔"层次&#xff1a;不带操作系统的编程 APP(应用程序) -------------------------------- Hardware(硬件) 特点&#xff1a;简单&#xff0c;应用程序直接操作硬件(寄存器) 缺点&#xff1a; 1. 搞应用开发的必须要了解硬件的实现细节&#xff0c;能够看懂原理图…

MyBatis 增删改查【后端 17】

MyBatis 增删改查 引言 MyBatis 是一个优秀的持久层框架&#xff0c;它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解用于配置和原始映射&#xff0c;将接口和 Java 的 POJOs (…

yolo训练出现Could not load library libcudnn_cnn_train.so.8问题及解决方法

问题场景&#xff1a; 训练yolov5或者yolov8时候会报错&#xff1a; Could not load library libcudnn_cnn_train.so.8. Error: /usr/local/cuda-12.1/lib64/libcudnn_cnn_train.so.8: uined symbol: _ZN5cudnn3cnn34layerNormFwd_execute_internal_implERKNS_7backend11Vari…

java技术栈介绍

Java技术栈是一个庞大而丰富的生态系统&#xff0c;它包含了从基础语言特性到高级框架、库和工具的整个集合。这个技术栈为开发者提供了构建各种类型应用&#xff08;包括企业级应用、Web应用、移动应用、大数据应用等&#xff09;所需的全部组件。以下是对Java技术栈的一个更详…

zip压缩包的格式不标准导致C++开源unzip.cpp解压失败问题的排查

目录 1、问题描述 2、初步排查 3、查看错误码512对应的含义 4、直接将解压zip包的函数拷贝过来,并将无法解压的zip取来,直接编写测试代码去调试解压过程,最终定位问题 4.1、调试开源unzip.cpp源码的准备工作 4.2、刚解压zip包中最顶层的文件夹就失败了 4.3、是不是zi…

深度学习之微积分预备知识点

极限&#xff08;Limit&#xff09; 定义&#xff1a;表示某一点处函数趋近于某一特定值的过程&#xff0c;一般记为 极限是一种变化状态的描述&#xff0c;核心思想是无限靠近而永远不能到达 公式&#xff1a; 表示 x 趋向 a 时 f(x) 的极限。 知识点口诀解释极限的存在左…

【CSS】 Grid布局:现代网页设计的基石

引言 最近接到一个网页布局比较复杂的页面&#xff0c;看了半天还是决定用grid布局来写&#xff0c;记录一下 布局是构建用户界面的关键部分。CSS Grid布局提供了一种简单而强大的方式来创建复杂的网格布局&#xff0c;它让设计师和开发者能够更直观、更灵活地控制网页的结构。…

MySQL 子查询全解析:执行、性能影响与优化策略

在 MySQL 数据库的操作中&#xff0c;子查询是一个强大而又复杂的工具。今天&#xff0c;我们就来深入探讨 MySQL 如何执行子查询、其性能影响、优化方法以及哪些情况下应避免使用子查询。 一、MySQL 如何执行子查询 非相关子查询 非相关子查询也被称为独立子查询&#xff0c;…

网络安全学习(三)Hydra破解密码

接下来看一下Hydra工具&#xff0c;这是一个暴力破解密码的工具。 使用命令&#xff08;注意区分大小写&#xff09;。 hydra -L user.txt账号字典 -P pass.txt密码字典 IP地址 smb协议名称 hydra -l administrator指定账号 -P pass.txt密码字典 IP地址 smb协议名称 hydra -…

数据结构(Day13)

一、学习内容 内存空间划分 1、一个进程启动后&#xff0c;计算机会给该进程分配4G的虚拟内存 2、其中0G-3G是用户空间【程序员写代码操作部分】【应用层】 3、3G-4G是内核空间【与底层驱动有关】 4、所有进程共享3G-4G的内核空间&#xff0c;每个进程独立拥有0G-3G的用户空间 …

【Go】Go语言介绍与开发环境搭建

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

OpenHarmony鸿蒙( Beta5.0)智能加湿器开发详解

鸿蒙开发往期必看&#xff1a; 一分钟了解”纯血版&#xff01;鸿蒙HarmonyOS Next应用开发&#xff01; “非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线&#xff01;&#xff08;从零基础入门到精通&#xff09; “一杯冰美式的时间” 了解鸿蒙HarmonyOS Next应用开发路…

算法提高模板强连通分量tarjan算法

AC代码&#xff1a; #include<bits/stdc.h>using namespace std;typedef long long ll; const int MOD 998244353; const int N 2e5 10;//强联通分量模板 //tarjan算法 vector<int>e[N]; int n, m, cnt; int dfn[N], low[N], ins[N], idx; int bel[N];//记录每…

【C++】—— 内存管理

【C】—— 内存管理 1 C/C 的内存划分 1.1 C/C 的内存分布1.2 C/C 的内存分布练习 2 C语言 中动态内存管理方式&#xff1a;malloc/calloc/realloc/free3 C 内存管理方式3.1 new / delete 操作内置类型3.2 new 和 delete 操作自定义类型3.2.1 new 和 delete 操作自定义类型基础…

【Java】了解线程 Thread 类的使用,如何创建、终止、等待一个线程以及获取线程的状态

线程是什么 线程是操作系统中调度的基本单位&#xff0c;是比进程更小的执行单元。线程在进程内部运行&#xff0c;共享该进程的资源&#xff0c;如内存和文件句柄&#xff0c;但每个线程都有自己的执行栈和程序计数器。 线程的主要特点包括&#xff1a; 轻量级&#xff1a;…

1.1 计算机网络基本概述

欢迎大家订阅【计算机网络】学习专栏&#xff0c;开启你的计算机网络学习之旅&#xff01; 文章目录 前言一、网络的基本概念二、集线器、交换机和路由器三、互连网与互联网四、网络的类型五、互连网的组成1. 边缘部分2. 核心部分 六、网络协议 前言 计算机网络是现代信息社会…