【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";//注意字符串结尾后面还有\0
	char str2[] = "hello world";

	int result1 = strncmp(str1, str2, 5);
	// 只比较前5个字符,结果为0,表示相等
	printf("%d\n", result1);

	int result2 = strncmp(str1, str2, 6);
	// 比较前6个字符,结果为负数,表示str1小于str2
	printf("%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));
    else
        printf("打开文件成功\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:");
    else
        printf("打开文件成功\n");

    return 0;
}

运行结果:

在这里插入图片描述


🚩总结

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

请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智能优化算法应用:基于原子搜索算法3D无线传感器网络(WSN)覆盖优化 - 附代码

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

12.4~12.14概率论复习与相应理解(学习、复习、备考概率论,这一篇就够了)

未分配的题目 概率计算&#xff08;一些转换公式与全概率公式&#xff09;与实际概率 &#xff0c;贝叶斯 一些转换公式 相关性质计算 常规&#xff0c;公式的COV与P 复习相关公式 计算出新表达式的均值&#xff0c;方差&#xff0c;再套正态分布的公式 COV的运算性质 如…

FastAPI访问/docs接口文档显示空白、js/css无法加载

如图&#xff1a; 原因是FastAPI的接口文档默认使用https://cdn.jsdelivr.net/npm/swagger-ui-dist5.9.0/swagger-ui.css 和https://cdn.jsdelivr.net/npm/swagger-ui-dist5.9.0/swagger-ui-bundle.js 来渲染页面&#xff0c;而这两个URL是外网的CDN&#xff0c;在国内响应超…

五、Java核心数组篇

1.数组 概念&#xff1a; ​ 指的是一种容器&#xff0c;可以同来存储同种数据类型的多个值。 ​ 但是数组容器在存储数据的时候&#xff0c;需要结合隐式转换考虑。 比如&#xff1a; ​ 定义了一个int类型的数组。那么boolean。double类型的数据是不能存到这个数组中的&…

【RocketMQ】顺序消费消息实现原理分析

一、顺序消息概述 1.1、什么是顺序消息 顺序消息是指对于一个指定的 Topic &#xff0c;消息严格按照先进先出&#xff08;FIFO&#xff09;的原则进行消息发布和消费&#xff0c;即先发布的消息先消费&#xff0c;后发布的消息后消费。 1.2、顺序消息的类型 全局顺序消息 …

经典深度学习算法【1】:K-近邻算法(KNN)概述

最简单最初级的分类器是将全部的训练数据所对应的类别都记录下来&#xff0c;当测试对象的属性和某个训练对象的属性完全匹配时&#xff0c;便可以对其进行分类。但是怎么可能所有测试对象都会找到与之完全匹配的训练对象呢&#xff0c;其次就是存在一个测试对象同时与多个训练…

JVM虚拟机系统性学习-JVM调优之GC日志分析

JVM 调优 首先&#xff0c;为什么要 JVM 调优呢&#xff1f; JVM 调优的目的就是为了让应用程序使用最小的硬件消耗来承载更大的吞吐量 什么情况下需要 JVM 调优呢&#xff1f; 系统吞吐量下降&#xff0c;或系统延迟较高出现 OOMFull GC 频繁GC 停顿时间过长&#xff08;超…

UE虚幻引擎中程序无需运行也可调试

首先先新建一个蓝图类&#xff0c;在蓝图类中创建一个Custom event 事件&#xff0c;然后在右侧细节面板中搜索call in editor&#xff0c;编译保存之后&#xff0c;将该蓝图类拖拽到关卡场景中&#xff0c;在细节面板中即可看到该事件的按钮。

PE文件格式-PE文件头部

文章目录 PE文件头基本概念简介地址对齐 PE结构概述PE文件头部DOS MZ 头PE头&#xff08;NT头&#xff09;PE头标识 Signature标准PE头 IMAGE_FILE_HEADERMachineNumberOfSectionsTimeDateStampPointToSymbolTableNumberOfSymbolSizeOfOptionalHeaderCharacteristics 扩展PE头 …

亚信科技AntDB数据库——深入了解AntDB-M元数据锁的相关概念

AntDB-M在架构上分为两层&#xff0c;服务层和存储引擎层。元数据的并发管理集中在服务层&#xff0c;数据的存储访问在存储引擎层。为了保证DDL操作与DML操作之间的一致性&#xff0c;引入了元数据锁&#xff08;MDL&#xff09;。 AntDB-M提供了丰富的元数据锁功能&#xff…

【Hadoop】执行start-dfs.sh启动hadoop集群时,datenode没有启动怎么办

执行start-dfs.sh后&#xff0c;datenode没有启动&#xff0c;很大一部分原因是因为在第一次格式化dfs后又重新执行了格式化命令&#xff08;hdfs namenode -format)&#xff0c;这时主节点namenode的clusterID会重新生成&#xff0c;而从节点datanode的clusterID 保持不变。 在…

认识缓存,一文读懂Cookie,Session缓存机制。

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)十四:系统设置模块相关功能实现

一、本章内容 本章使用已实现的公共组件实现系统管理中的系统设置模块相关功能,包括菜单管理、角色管理、日志管理、用户管理、系统配置、数据字典等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 3.1 B站视频地址:

硬件基础常识【4】--利用戴维宁定理求运放复杂反馈电阻网络的增益

最近学到了一种求带T型电阻网络反馈运放增益的方法 如图所示为T型电阻网络反馈的反相放大器 求解思路 沿X-Y断开&#xff0c;右侧利用戴维宁定理等效成电压源串电阻的形式 由戴维宁定理可得&#xff1a; V T H V o u t ∗ R 4 / ( R 3 R 4 ) ( 式 1 ) VTHVout*R4/(R3R4)…

布局前沿技术,紫光展锐推动6G创新融合发展

随着5G进入规模化商用阶段&#xff0c;6G研究已在全球范围内拉开帷幕。2023年6月&#xff0c;ITU发布了《IMT面向2030及未来发展的框架和总体目标建议书》&#xff0c;在升级5G三大应用场景的同时&#xff0c;扩展出三个跨领域场景&#xff0c;形成6G的六大应用场景&#xff0c…

unity—UGUI 点击按钮后,持续点击空格键会持续出发按钮

在unity开发当中&#xff0c;使用UGUI开发&#xff0c;无论是你代码绑定按钮事件&#xff0c;还是在Inspector窗口直接拖拽绑定的事件&#xff0c;点击按钮事件后&#xff0c;按空格键都会再次执行相关的方法。 默认情况下&#xff0c;Unity将空格键映射为UI按钮的Submit提交操…

【Hadoop】

Hadoop是一个开源的分布式离线数据处理框架&#xff0c;底层是用Java语言编写的&#xff0c;包含了HDFS、MapReduce、Yarn三大部分。 组件配置文件启动进程备注Hadoop HDFS需修改需启动 NameNode(NN)作为主节点 DataNode(DN)作为从节点 SecondaryNameNode(SNN)主节点辅助分…

Ubuntu18.04.6下samba服务的安装及配置

目录 01 安装samba服务&#xff1a; 03 重启samba服务 04 设置samba登录密码 05 测试 前言 虚拟机下Ubuntu18.04.6samba服务的安装及配置 01 安装samba服务&#xff1a; 命令行中输入 sudo apt-get install samba 02 配置 2.1 先创建一个需要共享的目录&#xff0c…

利用高级 CSPM 应对现代攻击

混合云和多云环境的快速增长造成了跨架构的复杂性&#xff0c;使得人们很难清楚、完整地了解技术堆栈中的各种平台。最近云攻击和破坏的激增引起了人们对团队应如何有效地保护和运行云中的应用程序的关注。 因为错误配置是云环境中安全威胁的首位&#xff0c;并且是基于云的攻…

kotlin 基础概览

继承类/实现接口 继承类和实现接口都是用的 : &#xff0c;如果类中没有构造器 ( constructor )&#xff0c;需要在父类类名后面加上 () &#xff1a; class MainActivity : BaseActivity(), View.OnClickListener 空安全设计 Kotlin 中的类型分为「可空类型」和「不可空类型」…