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

  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值