strcpy,strncpy函数详解

 strcpy函数

在C语言中,strcpy()函数用于将一个字符串复制到另一个字符串中。

函数原型如下:

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

参数解释:

  • destination:目标字符串,将会被复制到。
  • source:源字符串,将会被复制到目标字符串中。

函数返回值:

  • 返回指向目标字符串的指针。

场景一:

#include <stdio.h>
#include <string.h>int main()
{char arr[10] = "########";printf("%s\n", strcpy(arr,"hello"));return 0;
}

输出的结果

hello

场景二: 

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

输出结果

abcd

注意点

1.源字符必须以 '\0'结束:

#include <stdio.h>
#include <string.h>int main()
{char arr1[10] = "**********";char arr2[10] = { 'a','b','c','d' };printf("%s\n", strcpy(arr1,arr2));return 0;
}


这里的代码会出错,'\0'是停止拷贝的终止条件,arr2字符数组所在的内存空间后面存储的内容并不知道,不遇到 '\0' 拷贝就不会停止,这就会导致越界访问,程序就会出现问题。

2.目标空间必须足够大,以确保能存放得下放源字符串

#include <stdio.h>
#include <string.h>int main()
{char arr1[5] = "*****";char arr2[] = "hello world";printf("%s\n", strcpy(arr1,arr2));return 0;
}


 这里虽然拷贝成功并将结果输出了,但程序却崩溃了。目标空间太小,不足以放置拷贝的源字符串,会造成溢出的情况

3.目标空间必须可变

#include <stdio.h>
#include <string.h>int main()
{char* str1 = "hello world";char str2[10] = "*********";printf("%s\n", strcpy(str1,str2));return 0;
}


这里的程序也出现了错误。str1指向的是常量字符串,是不可以被修改掉的,目标空间必须是可以被修改的,因为要将拷贝的字符串放在目标空间中。而源字符串可以是能够修改的、也可以是不能修改的,因为strcpy函数的第二个参数已经用const关键字修饰了,保证了拷贝过程中不会被修改。

模拟实现

strcpy函数的模拟实现方式可以有多种,这主要取决于你对函数安全性、性能以及内存操作的不同需求。以下是几种常见的实现方式:

1. 基本实现

这是最直接的实现方式,它逐个复制源字符串的字符到目标字符串,直到遇到源字符串的结束符\0。

 char *my_strcpy(char *dest, const char *src) {assert(dest&&str);char *orig_dest = dest; // 保存dest的原始指针  while (*src != '\0') {  *dest++ = *src++;  }  // 添加结束符'\0'  *dest = '\0';  // 返回原始dest指针  return orig_dest;  } 

2. 指针算术实现

这种实现方式使用指针算术来简化复制过程,减少了对dest的多次解引用。

 char *my_strcpy_pointer_arithmetic(char *dest, const char *src) {assert(dest&&str);  char *orig_dest = dest;  while ((*dest++ = *src++) != '\0');  return orig_dest;  } 

3. 考虑安全性的实现

为了避免缓冲区溢出,你可以在实现中检查目标字符串dest的缓冲区大小。然而,strcpy函数本身并不检查缓冲区大小,这是其不安全的地方。一个更安全的版本是strncpy,但如果你想要模拟实现一个带缓冲区大小检查的strcpy,可以这样做:

 char *my_safe_strcpy(char *dest, const char *src, size_t dest_size) {assert(dest&&str);char *orig_dest = dest;  size_t i;  // 确保目标缓冲区至少有1个字节的空间以存储'\0'  if (dest_size == 0) {  return NULL;  }  for (i = 0; i < dest_size - 1 && src[i] != '\0'; ++i) {  dest[i] = src[i];  }  // 添加结束符'\0'并确保不会超出目标缓冲区  dest[i] = '\0';  return orig_dest;  } 

在这个实现中,我们传递了一个额外的参数dest_size来表示目标字符串dest的缓冲区大小。我们确保在复制过程中不会超出这个大小,并在最后添加一个结束符\0。

注意事项:

  1. 基本实现和指针算术实现都没有检查目标缓冲区的大小,因此它们可能导致缓冲区溢出。在实际应用中,应该避免使用这些不安全的实现,除非你能确保目标缓冲区足够大。
  2. 考虑安全性的实现通过检查目标缓冲区的大小来防止缓冲区溢出,但这也增加了函数的复杂性。在性能要求较高的场景中,可能需要权衡安全性和性能。
  3. 在实际编程中,通常建议使用标准库提供的strcpy、strncpy等函数,并根据需要选择适当的安全措施,如确保目标缓冲区足够大或使用更安全的字符串处理函数。

strncpy函数

strncpy 是 C 语言标准库中的一个函数,用于将源字符串的前 n 个字符(包括空字符 \0,如果源字符串在 n 个字符之前结束)复制到目标字符串中。

与 strcpy 不同,strncpy 不会自动在目标字符串的末尾添加空字符 \0,除非源字符串的长度小于或等于 n

因此,在使用 strncpy 时,通常需要手动添加空字符以确保目标字符串是一个有效的 C 字符串。

它的原型如下:

char *strncpy(char *dest, const char *src, size_t n);

其中,dest是目标字符串的指针,src是源字符串的指针,n是要复制的字符数。

strncpy函数会将src指向的字符串的前n个字符复制到dest指向的字符串中,并在复制完成后返回dest的指针。

注意点

strncpy不会自动向目标空间追加‘\0’

如果复制的字符数n大于src指向的字符串的长度,那么dest指向的字符串会被'\0'字符填充,直到复制完n个字符为止。

如果n小于src指向的字符串的长度,那么dest指向的字符串不会被终止符'\0'填充,而且可能不是一个有效的C字符串。

示例代码如下:

#include <stdio.h>
#include <string.h>int main() {char src[] = "Hello, World!";char dest[20];strncpy(dest, src, 5);dest[5] = '\0';printf("Copied string: %s\n", dest);return 0;
}

这段代码将src字符串的前5个字符复制到dest字符串中,并手动添加了终止符'\0'。最后输出的结果为"Hello"。

目标空间必须足够大

如果n小于src指向的字符串的长度,那么dest指向的字符串不会被终止符'\0'填充,而且可能不是一个有效的C字符串。

我们上面举了例子,就不多说了

模拟实现

strncpy函数的模拟实现方式可以有多种,但基本的思路都是按照strncpy的定义来进行:复制源字符串的前n个字符到目标字符串,并确保目标字符串至少包含n个字符(如果需要的话,用空字符\0填充剩余部分)。以下是几种可能的模拟实现方式:

1. 基本实现

这是最直接的模拟实现方式,按照strncpy的定义逐字符复制,并在需要时添加空字符\0。

 #include <stddef.h> // 包含 size_t 的定义  char *my_strncpy_basic(char *dest, const char *src, size_t n) {assert(dest&&str);  char *orig_dest = dest;  size_t i;  for (i = 0; i < n && src[i] != '\0'; ++i) {  dest[i] = src[i];  }  // 如果还没有达到 n 个字符,用 '\0' 填充剩余部分  for (; i < n; ++i) {  dest[i] = '\0';  }  return orig_dest;  } 

2. 优化版实现

在某些情况下,如果知道源字符串的长度不会超过n,可以稍微优化这个过程,避免不必要的空字符填充。

 char *my_strncpy_optimized(char *dest, const char *src, size_t n) { assert(dest&&str);char *orig_dest = dest;  size_t i;  for (i = 0; i < n && src[i] != '\0'; ++i) {  dest[i] = src[i];  }  // 如果源字符串自身长度小于 n,确保目标字符串以 '\0' 结尾  if (src[i] == '\0') {  dest[i] = '\0';  }  return orig_dest;  } 

3. 指针算术实现

使用指针算术可以简化循环内部的索引操作,使得代码更简洁。

 char *my_strncpy_pointer_arithmetic(char *dest, const char *src, size_t n) {assert(dest&&str);char *orig_dest = dest;  const char *src_end = src + n; // 计算 src 中要复制的字符的结束位置  // 复制字符直到达到 n 个字符或遇到 src 的结束符 '\0'  while (src < src_end && *src != '\0') {  *dest++ = *src++;  }  // 用 '\0' 填充剩余部分  while (dest < orig_dest + n) {  *dest++ = '\0';  }  return orig_dest;  } 

注意事项:

  1. 无论采用哪种实现方式,都需要确保目标缓冲区dest有足够的空间来存储至少n个字符,包括结尾的空字符\0。如果dest的空间不足,可能会导致缓冲区溢出。
  2. 在实际应用中,还需要考虑错误处理,比如当dest或src为NULL时应该怎么做。
  3. 在性能敏感的场合,可以根据实际情况选择最合适的实现方式。例如,如果源字符串通常很长,使用指针算术可能更有效率;如果源字符串通常较短,基本的循环实现可能就足够了。
  4. 这些模拟实现都是基于标准strncpy函数的行为,并假设调用者已经正确地处理了缓冲区大小和空指针等问题。在真实环境中使用时,还需要添加适当的错误检查和边界条件处理来确保程序的健壮性。

与strcpy的区别

strncpystrcpy都是C语言中的字符串处理函数,它们的主要功能都是复制字符串,但它们在处理字符串的方式上有一些重要的区别。

  1. 复制长度

    • strcpy函数会复制源字符串的所有字符,直到遇到空字符\0为止。这意味着如果源字符串的长度超过了目标字符串的缓冲区大小,那么strcpy可能会导致缓冲区溢出,这是一个常见的安全问题。
    • strncpy函数则复制源字符串的前n个字符到目标字符串中,无论是否遇到空字符\0。如果源字符串的长度小于n,那么strncpy会在目标字符串的剩余位置填充\0,直到复制了n个字符。如果源字符串的长度大于或等于n,strncpy不会添加额外的\0,这可能会导致目标字符串不是一个有效的C字符串(即不以\0结尾)。因此,在使用strncpy时,通常需要手动检查并添加\0,以确保字符串的正确性。
  2. 安全性

    • 由于strcpy不会检查目标缓冲区的大小,因此它更容易导致缓冲区溢出。这种溢出可以被恶意利用,造成严重的安全问题。
    • strncpy虽然在一定程度上可以避免缓冲区溢出(如果正确使用,并且n不超过目标缓冲区的大小),但它并不总是生成有效的C字符串。因此,在使用strncpy时,需要特别注意确保目标字符串的正确性。

总的来说,虽然strcpystrncpy都用于复制字符串,但它们在处理字符串的方式和安全性上有显著的不同。在编写涉及字符串操作的代码时,应根据具体情况选择适当的函数,并确保正确使用它们,以避免潜在的安全问题。

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

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

相关文章

Servlet(三个核心API介绍以及错误排查)【二】

文章目录 一、三个核心API1.1 HttpServlet【1】地位【2】方法 1.2 HttpServletRequest【1】地位【2】方法【3】关于构造请求 1.3 HttpServletResponse【1】地位【2】方法 四、涉及状态码的错误排查&#xff08;404……&#xff09;五、关于自定义数据 ---- body或query String …

计算机网络 备查

OSI 七层模型 七层模型协议各层实现的功能 简要 详细 TCP/IP协议 组成 1.传输层协议 TCP 2.网络层协议 IP 协议数据单元&#xff08;PDU&#xff09;和 封装 数据收发过程 数据发送过程 1. 2.终端用户生成数据 3.数据被分段&#xff0c;并加上TCP头 4.网络层添加IP地址信息…

nuxt3使用记录五:禁用莫名其妙的Tailwind CSS(html文件大大减小)

发现这个问题是因为&#xff0c;今天我突然很好奇&#xff0c;我发现之前构建的自动产生的200.html和404.html足足290k&#xff0c;怎么这么大呢&#xff1f;不是很占用我带宽&#xff1f; 一个啥东西都没有的静态页面&#xff0c;凭啥这么大&#xff01;所以我就想着手动把他…

Github Action Bot 开发教程

Github Action Bot 开发教程 在使用 Github 时&#xff0c;你可能在一些著名的开源项目&#xff0c;例如 Kubernetes&#xff0c;Istio 中看到如下的一些评论&#xff1a; /lgtm /retest /area bug /assign xxxx ...等等&#xff0c;诸如此类的一些功能性评论。在这些评论出现…

Django-基础篇

Django是一个开放源代码的Web应用框架&#xff0c;由Python语言编写。它遵循MVC&#xff08;Model-View-Controller&#xff09;的软件设计模式&#xff0c;使开发者能够以高效、可扩展和安全的方式构建Web应用程序。 Django具有以下特点和优势&#xff1a; 强大的功能&#x…

面试笔记——线程池

线程池的核心参数&#xff08;原理&#xff09; public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)corePoolSize …

利用word2vec包将中文转变为词向量

代码展示&#xff1a; import jieba import re import json import logging import sys import gensim.models as word2vec from gensim.models.word2vec import LineSentence, loggerpattern u[\\s\\d,.<>/?:;\\"[\\]{}()\\|~!\t"#$%^&*\\-_a-zA-Z&…

C#创建obj三维模型文件

介绍 使用开源库创建obj三维模型文件。 开源库地址&#xff1a;https://github.com/JeremyAnsel/JeremyAnsel.Media.WavefrontObj 相关API地址&#xff1a;https://jeremyansel.github.io/JeremyAnsel.Media.WavefrontObj/api/JeremyAnsel.Media.WavefrontObj.ObjFile.html …

【docker】开放Docker端口

【docker 】 安装docker&#xff08;centOS7&#xff09;-CSDN博客 【docker】常用命令-CSDN博客 【docker】centos7配置docker镜像加速_docker仓库加速地址-CSDN博客 【docker】Hello World-CSDN博客 【docker 】Compose 使用介绍_docker compose 使用-CSDN博客 【docker…

linux(ubuntu18.04.2) Qt编译 MySQL(8.0以上版本)链接库 Qt版本 5.12.12及以上 包含Mysql动态库缺失问题

整理这篇文档的意义在于&#xff1a;自己走了很多弯路&#xff0c;淋过雨所以想为别人撑伞&#xff0c;也方便回顾&#xff0c;仅供参考 一、搭建开发环境&#xff1a; 虚拟机&#xff08;ubuntu-20.04.6-desktop-amd64&#xff09;&#xff1a;Mysql数据库 8.0.36Workbench …

tidb离线本地安装及mysql迁移到tidb

一、背景&#xff08;tidb8.0社区版&#xff09; 信创背景下不多说好吧&#xff0c;从资料上查tidb和OceanBase“兼容”&#xff08;这个词有意思&#xff09;的比较好。 其实对比了很多数据库&#xff0c;有些是提供云服务的&#xff0c;有些“不像”mysql&#xff0c;综合考虑…

【如何使用SSH密钥验证提升服务器安全性及操作效率】(优雅的连接到自己的linux服务器)

文章目录 一、理论基础&#xff08;不喜欢这部分的可直接看具体操作&#xff09;1.为什么要看本文&#xff08;为了zhuangbility&#xff09;2.为什么要用密钥验证&#xff08;更安全不易被攻破&#xff09;3.密码验证与密钥验证的区别 二、具体操作1.生成密钥对1.1抉择&#x…

计算机复试项目:SpringCloud实战高并发微服务架构设计

秒杀购物商城--环境搭建 秒杀购物商城基础服务组件--详细介绍 秒杀购物商城基础服务--权限中心 秒杀购物商城业务服务--收货地址 秒杀购物商城业务服务--秒杀活动服务 秒杀购物商城--购物车的功能设计及分析 秒杀购物商城基础服务-用户中心 秒杀购物商城业务服务--商品中…

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之九 简单进行嘴巴检测并添加特效的功能实现

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之九 简单进行嘴巴检测并添加特效的功能实现 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之九 简单进行嘴巴检测并添加特效的功能实现 一、简单介绍 二、简单…

ArcGIS小技巧—模型构建器快速提取河网

上篇文章介绍的基于DEM的河网提取&#xff0c;需要使用多个工具&#xff0c;整体操作比较繁琐&#xff0c;在日常工作中&#xff0c;使用Arcgis提供的模型构建器可以帮助我们将多个工具整合在一起&#xff0c;在面对大量数据批量处理时&#xff0c;可以大大提高工作效率 利用模…

数字化wms仓库管理软件,实现企业仓储信息共享与智慧运行-亿发

在经济飞速发展的今天&#xff0c;企业面临着客户需求多样化、质量和交期要求提高以及激烈的市场竞争等挑战。在这样的背景下&#xff0c;许多企业开始考虑采用数字化仓储WMS系统来解决这些问题。 数字化仓储WMS系统通过打造高效、规范的仓库管理体系&#xff0c;实现了对产品…

爱普生晶振在物联网LoRa通讯中的应用

LoRa 是LPWAN通信技术中的一种&#xff0c;是美国Semtech公司采用和推广的一种基于扩频技术的超远距离无线传输方案。这一方案改变了以往关于传输距离与功耗的折衷考虑方式&#xff0c;为用户提供一种简单的能实现远距离、长电池寿命、大容量的系统&#xff0c;进而扩展传感网络…

神经网络高效训练:优化GPU受限环境下的大规模CSV数据处理指南

最近训练模型,需要加载wifi sci data 数据量特别大,直接干爆内存,训练也特别慢,快放弃了!随后冷静下来,然后靠着多年的经验,来进行层层优化,随诞生了这篇博客。 背景介绍 机器学习模型的训练通常需要大量的数据,尤其是对于深度神经网络模型。然而,当数据集非常庞大时…

网络之路29:三层链路聚合

正文共&#xff1a;1666 字 17 图&#xff0c;预估阅读时间&#xff1a;3 分钟 目录 网络之路第一章&#xff1a;Windows系统中的网络 0、序言 1、Windows系统中的网络1.1、桌面中的网卡1.2、命令行中的网卡1.3、路由表1.4、家用路由器 网络之路第二章&#xff1a;认识企业设备…

新质生产力实践,我用chatgpt开发网站

是的&#xff0c;我用chatgpt开发了一个网站&#xff0c;很轻松。 我之前一点不懂前端&#xff0c;也没有网站开发的代码基础&#xff0c;纯正的0基础。 从0开始到最后成品上线&#xff0c;时间总计起来大致一共花了2-3周的时间。 初始想法我是想给我公司开发一个网站&#…