【C语言】超详解strncpystrncatstrncmpstrerrorperror的使⽤和模拟实现

  🌈write in front :

🔍个人主页 : @啊森要自信的主页

✏️真正相信奇迹的家伙,本身和奇迹一样了不起啊!

欢迎大家关注🔍点赞👍收藏⭐️留言📝>希望看完我的文章对你有小小的帮助,如有错误,可以指出,让我们一起探讨学习交流,一起加油鸭。 请添加图片描述

文章目录

  • 📝前言
  • 🌠 库函数strncpy
    • 🌉strncpy 模拟实现
  • 🌠strncat 函数的使⽤
    • 🌉strncat 模拟实现
  • 🌠strncmp函数的使⽤
    • 🌉strncmp模拟实现
  • 🌠strerror
    • 🌉 perror
  • 🚩总结


📝前言

本小节,阿森继续和你一起学习5个字符串函数:strncpystrcnatstrncmp的使用和两种模拟实现方法,他们和strcpy等函数比较多了一个n ,实现方法有很大区别,还有strerrorperror的使用,学习这些库函数,可以更好的方便操作字符和字符串,文章干货满满,接下来我们就学习一下这些函数吧!


strcpystrcat这类函数不安全,因为它们在复制字符串时不检查目标缓冲区的大小,可能会导致缓冲区溢出
strncpystrncatstrncmp这类函数相对来说更安全,因为它们在复制/追加字符串时会限定最大长度参数n,避免无限制地写入目标缓冲区。
点击—>手把手教你配置VS的常见函数如何不报错!

在这里插入图片描述

🌠 库函数strncpy

strncpy函数用于将一个字符串拷贝到另一个字符串中,可以限定拷贝的字符数
函数原型:

 char * strncpy ( char * destination, const char * source, size_t num );dest - 目标字符串,用于接收拷贝内容。src - 源字符串,从中拷贝内容。 num - 要拷贝的字符数。

返回值:
返回目标字符串dest的指针。

注意点:

  • 检查dest空间是否足以容纳srcn个字符及结尾'\0'strncpy不会检查dest的长度,如果dest空间不足可能会导致缓冲区溢出。

  • 拷⻉num个字符从源字符串到⽬标空间。拷贝num个就num个,不会拷贝多,也不会自己添加\0
    在这里插入图片描述

  • 如果源字符串的⻓度⼩于num,则拷⻉完源字符串之后,在⽬标的后边追加0,直到num个。
    在这里插入图片描述
    例子:

# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{char str1[] = "Hello World";char str2[7];str2[5] = '\0';strncpy(str2, str1, 5);printf("str1: %s\n", str1);printf("str2: %s\n", str2);return 0;
}

输出:

str1: Hello World
str2: Hello

🌉strncpy 模拟实现

对于strncpy函数,阿森给你带来了两种模拟实现方法,详解如下:

  • 主函数(两种模拟实现都可以用这个进行测试)
int main() 
{char dest[20]="xxxxxxxxxxxxxxxxx";char src[] = "hello";size_t set = strlen(src);printf("%d\n", set);my_strncpy(dest, src, 3);printf("%s\n", dest);my_strncpy(dest, src, 9);printf("%s\n", dest);return 0;
}
  1. 数组模拟实现
char* my_strncpy(char* dest, const char* src, size_t n) 
{if (dest == NULL || src == NULL) //检查dest和src参数是否合法,如果任意一个为NULL则直接返回NULL。{return NULL;}char* result = dest;// 保存dest的地址值,后面返回时使用size_t i;for ( i = 0; i < n && src[i] != '\0'; i++)  //使用for循环复制字符。{									     	 // i < n判断是否已经复制n个字符dest[i] = src[i];				          //  src[i] != '\0' 判断当前源字符串字符是否结束判断是否已经复制n个字符}//复制源字符串当前字符到目标字符串// 添加'\0'填充   (如果源字符串的⻓度⼩于`num`,则拷⻉完源字符串之后,在⽬标的后边追加`0`,直到`num`个。)while (i < n) 如果for循环结束但i未达到n,使用while循环填充'\0'。{dest[i++] = '\0';//将目标字符串当前位置字符填充为'\0'}							//dest[i++] = '\0'先dest[i]='\0',后i++return result;
}

输出:

5
helxxxxxxxxxxxxxx
hello

调试界面:
在这里插入图片描述

  1. 指针实现

char* my_strncpy(char* dest, const char* src, size_t n)
{assert(dest);//利用断言需要使用头文件#include<assert.h>assert(src);char* destPtr = dest;//定义dest和src的指针变量destPtr和srcPtr,用于遍历字符串。const char* srcPtr = src;while (n-- > 0) //使用while循环遍历n个字符{if (*srcPtr != '\0')  //检查当前源字符串srcPtr指向的字符是否为'\0'结束符{*destPtr++ = *srcPtr++;//如果不是结束符,就将源字符串当前字符复制到目标字符串,}										//并同时将两个指针前移到下一个字符。else //如果是结束符,进入else块{*destPtr++ = '\0';//将目标字符串当前字符设置为结束符'\0'}							  //然后destPtr再++}return dest; //返回目标字符串首地址。
}

输出:
在这里插入图片描述

*destPtr++ = *srcPtr++先进行一次赋值(*dest = *src),然后并使指针后移(dest=dest+1,src=src+1)
*destPtr++ = ‘\0’将目标字符串当前字符设置为结束符'\0',然后destPtr++

🌠strncat 函数的使⽤

strncat函数用于连接两个字符串,将源字符串src连接到目标字符串dest的结尾,最多连接n个字符。

strncat函数的原型:

char *strncat(char *dest, const char *src, size_t n);dest:目标字符串,其内容将在其后追加源字符串内容。src:源字符串,其内容将被追加到目标字符串结尾。 n:要从源字符串中追加到目标字符串中的最大字符数。
  • 返回值:
    函数返回目标字符串dest的指针。

例子:

#include <string.h>
int main()
{char dest[100] = "Hello";char src[] = " World";strncat(dest, src, 6);printf("%s\n", dest);
}

输出:

输出 Hello World

🌉strncat 模拟实现

  • 主函数
int main()
{char str1[100] = "hello";char str2[100] = " world";my_strncat(str1, str2, 5);printf("%s\n", str1);return 0;}
  1. 数组模拟实现
char* my_strncat(char* dst, const char* src, size_t n)
{char* tmp = dst;while (*dst)//使用while循环遍历dst字符串。{dst++;//找到字符串结束位置'\0'。}int i;for (i = 0; src[i] && i < n; i++)//  i < n 判断是否超过最大复制长度n{											 //src[i] 判断源字符串是否结束dst[i] = src[i];}dst[i] = 0;//在目标字符串末尾添加字符串结束标记'\0'。return tmp;
}

输出:

hello worl

在这里插入图片描述

  1. 指针实现
char* my_strncat(char* dest, const char* src, size_t n)
{//参数检查if (dest == NULL || src == NULL){return NULL;}char* ptr = dest;//找到目标字符串结尾while (*dest != '\0'){dest++;}while (n-- > 0 && *src != '\0'){*dest++ = *src++;}*dest = '\0';return ptr;//添加字符串结束符
}

输出:
在这里插入图片描述

首先,n-- 表示先使用 n 的值来进行比较是否>0,因为&& 是逻辑与运算符,*src != ‘\0’ 表示判断指针 src 所指向的字符是否为字符串的结束符 \0这两个条件验证真假后,最后 n 的值才减 1

🌠strncmp函数的使⽤

strncmp用于比较两个字符串的前n个字符。(比较的不是字符串的长度无关,只与对应位置的字符内容有关。)

strncmp函数原型:

int strncmp(const char *str1, const char *str2, size_t n);
str1 - 要比较的第一个字符串的指针
str2 - 要比较的第二个字符串的指针  
n - 将被比较的最大字符数

返回值:

  • 如果str1小于str2,返回值小于0
  • 如果str1大于str2,返回值大于0
  • 如果str1等于str2,返回值等于0

注意点:

  1. 如果n的值大于两个字符串中任意一个字符串的长度,比较将会超出字符串的范围,可能导致内存访问错误。因此,在使用strncmp函数时,需要确保n的值不会超过任意一个字符串的长度。

  2. strncmp函数返回的结果是一个整数,可以通过结果的正负值来判断两个字符串的大小关系。

  3. 比较规则与strcmp函数一致,按ASCII码顺序比较每个字符。

使用示例:

int main()
{char str1[] = "hello";//注意字符串结尾后面还有\0char str2[] = "hello world";int result1 = strncmp(str1, str2, 5);// 只比较前5个字符,结果为0,表示相等printf("%d\n", result1);int result2 = strncmp(str1, str2, 6);// 比较前6个字符,结果为负数,表示str1小于str2printf("%d\n", result2);}

输出:
在这里插入图片描述

🌉strncmp模拟实现

int my_strncmp(const char* s1, const char* s2, size_t n)
{int i = 0;//这是一个 for 循环,用于迭代比较两个字符串中的字符。for (; i < n && s1[i] != '\0' && s2[i] != '\0'; i++){if (s1[i] != s2[i]){return s1[i] - s2[i];//如果当前位置的两个字符不相等,返回它们的差值。}}if (i <= n){return s1[i] - s2[i];//如果 i 小于等于 n,但是循环结束了(即至少一个字符串已经达到结束符 ‘\0’),则返回当前位置字符的差值。}return 0;
}
int main()
{char s1[] = "hello";char s2[] = "helloworld";int result = my_strncmp(s1, s2, 5);printf("result = %d\n", result);return 0;
}

运行:
在这里插入图片描述

监视:
在这里插入图片描述

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

🌠strerror

错误码错误描述
0No error
1Operation not permitted
2No such file or directory
3No such process
4Interrupted function call
5Input/output error
6No such device or address
7Arg list too long
8Exec format error
9Bad file descriptor

strerror函数用于将错误码转换为对应的错误信息字符串。
函数原型如下:

char *strerror(int errnum);
errnum: 错误码号,通常是系统调用或库函数返回的错误号。   

strerror函数接受一个整型参数errnum,表示错误码。它会返回一个指向错误信息字符串的指针。

注意点:

  • 在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码,⼀般是放在 errno.h 这个头⽂件中#include <errno.h>
  • C语⾔程序启动的时候就会使⽤⼀个全⾯的变量errno来记录程序的当前错误码,只不过程序启动的时候errno0,表⽰没有错误。
  • 当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会讲对应
    的错误码,存放在errno
  • 以每⼀个错误码都是有对应的错误信息的
  • strerror函数返回的是一个静态字符串指针,不需要手动释放内存。

举栗子:

#include <errno.h>
#include <string.h>
#include <stdio.h>
int main()
{int i = 0;for (i = 0; i < 10; i++){printf("%d: %s\n",i, strerror(i));}return 0;
}

输出:
在这里插入图片描述

如何使用strerror函数打印打开文件失败的错误信息:

int main()
{FILE* pFile;pFile = fopen("unexist.txt", "r");//使用fopen函数打开文件"unexist.txt",以只读方式打开。if (pFile == NULL)//判断打开结果pFile是否为NULL,NULL表示打开失败。printf("Error opening file unexist.ent: %s\n", strerror(errno));elseprintf("打开文件成功\n");return 0;
}

输出:
在这里插入图片描述
分析:

Error opening file unexist.ent: No such file or directory说明打开文件"unexist.txt"失败,失败原因是文件不存在(ENOENT错误码)
在这里插入图片描述
如果加上"unexist.txt"该文件,就会显示打开成功!
在这里插入图片描述
在这里插入图片描述

🌉 perror

perror函数用于打印错误信息。它的功能与strerror函数类似,但打印方式不同。

perror函数原型:

void perror(const char *s);
s: 可选的错误前缀信息。

简意:

perror函数直接打印到标准错误输出,打印完参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息。(此代码结果为下面代码运行)
在这里插入图片描述

详解:
errno设置的错误号转换为错误描述字符串,然后打印到标准错误输出stderr上。如果s不为空,则在错误描述前加上s后跟 冒号 ":"。(stderr是预定义的一个文件输出流,它用于输出错误和诊断信息。stderr默认连接到控制台,输出到屏幕。所以向stderr输出的信息直接打印在屏幕上。)

使用perror函数需要包含错误头文件errno.h

栗子:

int main()
{FILE* pFile;pFile = fopen("unexist.txt", "r");if (pFile == NULL)//printf("Error opening file unexist.ent: %s\n", strerror(errno));perror("Error opening file:");elseprintf("打开文件成功\n");return 0;
}

运行结果:

在这里插入图片描述


🚩总结

这次阿森和你一起学习6个C语言中常用的基本字符操作函数,但阿森会慢慢和你一起学习。感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,可以给博主点一个小小的赞😘

请添加图片描述

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

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

相关文章

张驰课堂:如何应用六西格玛法则优化你的工作流?

作为职场发展的坚实支柱&#xff0c;六西格玛不仅是企业提质增效的利器&#xff0c;同时也是那些渴望在职业生涯中脱颖而出的专业人士的的秘密武器。以下是通过培养个人技能&#xff0c;借助六西格玛优化工作与人生的途径。 团队合作&#xff1a;聚沙成塔的力量 六西格玛教我…

SQL进阶理论篇(四):索引的结构原理(B树与B+树)

文章目录 简介如何评价索引的数据结构设计好坏二叉树的局限性什么是B树什么是B树总结参考文献 简介 我们在上一节中说过&#xff0c;索引其实是一种数据结构&#xff0c;那它到底是一种什么样的数据结构呢&#xff1f;本节将简单介绍一下几个问题&#xff1a; 什么样的数据结…

Vue3-18-侦听器watch()、watchEffect() 的基本使用

什么是侦听器 个人理解&#xff1a;当有一个响应式状态&#xff08;普通变量 or 一个响应式对象&#xff09;发生改变时&#xff0c;我们希望监听到这个改变&#xff0c;并且能够进行一些逻辑处理。那么侦听器就是来帮助我们实现这个功能的。侦听器 其实就是两个函数&#xff…

Process On在线绘制流程图

目录 一.ProcessOn 1.1.介绍 1.2.直接网上使用 二.绘制门诊流程图 三.绘制住院流程图 四.绘制药库采购入库流程图 五.绘制OA会议流程图 今天就到这里了哦!!!希望能帮到你哦&#xff01;&#xff01;&#xff01; 一.ProcessOn 1.1.介绍 ProcessOn&#xff08;流程&#…

智能优化算法应用:基于蝠鲼觅食算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于蝠鲼觅食算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于蝠鲼觅食算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.蝠鲼觅食算法4.实验参数设定5.算法结果6.…

Vue学习计划-Vue2--VueCLi(五)全局事件总线、消息订阅与发布(pubsub)

抛出问题:我们多级组件&#xff0c;或者任意不想关的子组件如何传递数据呢&#xff1f; 1. 全局事件总线&#xff08;$bus&#xff09; 一种组件间通信的方式&#xff0c;适用于任意组件间通信 全局事件总线示意图&#xff1a; 安装全局事件总线&#xff1a; new Vue({..…

无人机高空巡查+智能视频监控技术,打造森林防火智慧方案

随着冬季的到来&#xff0c;森林防火的警钟再次敲响&#xff0c;由于森林面积广袤&#xff0c;地形复杂&#xff0c;且人员稀少&#xff0c;一旦发生火灾&#xff0c;人员无法及时发现&#xff0c;稍有疏忽就会酿成不可挽救的大祸。无人机高空巡查智能视频监控是一种非常有效的…

MySQL如何进行Sql优化

&#xff08;1&#xff09;客户端发送一条查询语句到服务器&#xff1b; &#xff08;2&#xff09;服务器先查询缓存&#xff0c;如果命中缓存&#xff0c;则立即返回存储在缓存中的数据&#xff1b; &#xff08;3&#xff09;未命中缓存后&#xff0c;MySQL通过关键字将SQ…

19.(vue3.x+vite)v-if和v-for哪个优先级更高

前端技术社区总目录(订阅之前请先查看该博客) v-if和v-for哪个优先级更高 (1)实践中不应该把v-for和v-if放一起,可以包一层template (2)在vue2中,v-for的优先级是高于v-if (3)在vue3中,v-for的优先级是低于v-if 组件代码 <template><div><!--包一…

Hazel引擎学习(十二)

我自己维护引擎的github地址在这里&#xff0c;里面加了不少注释&#xff0c;有需要的可以看看 参考视频链接在这里 这是这个系列的最后一篇文章&#xff0c;Cherno也基本停止了Games Engine视频的更新&#xff0c;感觉也差不多了&#xff0c;后续可以基于此项目开发自己想要…

树莓派,opencv,Picamera2利用舵机云台追踪人脸

一、需要准备的硬件 Raspiberry 4b两个SG90 180度舵机&#xff08;注意舵机的角度&#xff0c;最好是180度且带限位的&#xff0c;切勿选360度舵机&#xff09;二自由度舵机云台&#xff08;如下图&#xff09;Raspiberry CSI 摄像头 组装后的效果&#xff1a; 二、项目目标…

数据结构与算法—排序算法(一)时间复杂度和空间复杂度介绍

排序算法 文章目录 排序算法1.排序算法的介绍1.1 排序的分类 2.算法的时间复杂度2.1 度量一个程序(算法)执行时间的两种方法2.2 时间频度2.2.1 忽略常数项2.2.2 忽略低次项2.2.3 忽略系数 2.3 时间复杂度2.3.1 常见的时间复杂度2.3.1.1 常数阶 O ( 1 ) O(1) O(1)2.3.1.2 对数阶…

Excel高效办公:文秘与行政办公的智能化革新

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f91f; 代理 IP 推荐&#xff1a;&#x1f449;品易 HTTP 代理 IP &#x1f485; 想寻找共同学习交流的小伙伴&#xff0c…

Java版本+鸿鹄企业电子招投标系统源代码+支持二开+Spring cloud +鸿鹄电子招投标系统

​项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。为了符合国家电子招投标法律法规及相关规范&#xff0…

java设计模式学习之【代理模式】

文章目录 引言代理模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用图片加载示例代码地址 引言 在现实生活中&#xff0c;我们经常使用代理来处理我们不想直接参与或无法直接参与的事务&#xff0c;例如&#xff0c;使用律师来代表法庭上的案件。在软件开发…

克隆图[中等]

一、题目 给你无向 连通 图中一个节点的引用&#xff0c;请你返回该图的 深拷贝&#xff08;克隆&#xff09;。图中的每个节点都包含它的值val&#xff08;int&#xff09;和其邻居的列表list[Node]。 class Node {public int val;public List<Node> neighbors; }测试…

计算机如何看待内存

计算机如何看待内存&#xff1b; 对象在内存中如何表示&#xff0c;如何操纵对象&#xff1b;

使用 Axios 进行网络请求的全面指南

使用 Axios 进行网络请求的全面指南 本文将向您介绍如何使用 Axios 进行网络请求。通过分步指南和示例代码&#xff0c;您将学习如何使用 Axios 库在前端应用程序中发送 GET、POST、PUT 和 DELETE 请求&#xff0c;并处理响应数据和错误。 准备工作 在开始之前&#xff0c;请…

Java 反射:探索运行时行为的强大工具

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、浅层理解 1.1 定义 1.2 我的理解 二、反射基础与应用 2.1 反射基础 2.2 反射的高级应用 三、一些反射的例子 3.1 获取类…

GitHub帐户管理更改电子邮件

登录到您的 GitHub 帐户&#xff1a; 前往 GitHub 网站并使用您的凭据登录。 访问个人设置&#xff1a; 单击右上角的您的头像&#xff0c;然后选择“Settings”&#xff08;设置&#xff09;。 选择电子邮件选项卡&#xff1a; 在左侧边栏中选择“Emails”&#xff08;电子邮…