模拟实现strncpy函数

本文介绍了C语言中strncpy函数的功能和用途,通过模拟实现strncpy,详细分析了不同拷贝字符数情况下可能出现的问题及解决方案,重点讨论了当拷贝字符数大于源串长度时的处理策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     初学C语言的人应该对strcpy和strncpy函数很熟悉。strcpy函数用于实现字符串的复制,打开msdn可以看到它的函数原型为char *strcpy( char *strDestination, const char *strSource);然而它的功能仅限于拷贝整个字符串,属于不受限制的字符串函数;那么如果我们想拷贝有限的字符该怎么办?strncpy函数刚好提供了此功能。下面我们就来模拟实现strncpy。

首先来看它的参数设置,经过上篇博客中对strcpy实现原理的讲解,很容易得出只需要在strcpy函数参数中再加入一个变量,用于控制拷贝字符的个数。那么strncpy的函数模型就显而易见,即为char *strncpy( char *dest, const char *src, size_t n );

再来看函数的实现部分,相比于strcpy的实现,strncpy的参数部分只是多了一个变量n,但原理类似;那么不难想到只需把strcpy函数中用while控制的循环条件稍作修改(strcpy中是以源字符串中的结束字符‘\0’为控制条件,而strncpy中利用n做循环的控制条件),然后每次拷贝,n递减即可。

有了初步思路,很容易地写下如下代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
char*my_strncpy(char*dest, const char*src, size_t n)
{
	assert(dest != NULL);
	assert(src != NULL);
	char*ret = dest;
	while (n)
	{
		*dest = *src;
		src++;
		dest++;
		n--;
	}
	return ret;
}

int main()
{
	char arr[20] = {0 };
	char*p = "bit";
	int n = 0;
	printf("请输入拷贝的字符个数:\n");
	scanf("%d", &n);
	char*ret = my_strncpy(arr, p,n);
	printf("%s\n", ret);
	return 0;
}

接下来我们对这个程序进行测试,即分别令其拷贝字符的个数小于,等于,大于源串长度;测试所得结果如下:

情况(1)-->“小于”源串长度


情况(2)-->“等于”源串长度(注:我们这里所说的“长度”不是指strlen(str),而是把字符串尾部的‘\0’也算在内了,即为4)


情况(3)-->“大于”源串长度


可以发现,这三种情况下的字符拷贝看似没有什么问题,接下来我们对第二和第三种情况进行分析:

当拷贝的字符个数为4时,输出的是bit,这里为了可以看清楚数组中具体发生的变化,我们把数组的初始化内容稍作修改,即为char arr[20]={'a','b','c','d','e','f','g','h'};

然后F10逐步执行调用监视,查看数组中每个字符的替换情况,如下图:


可以看到前三个字符‘a’,'b','c'分别被替换为‘b’,'i','t';第四个字符‘d’被‘\0’替换掉,可见的确把字符串bit后边的‘\0’也拷贝了进去,而往后存储的字符并没有发生变化;


那么,当拷贝的字符个数大于源串长度(此处拷贝的字符个数为5)时,又是怎样的情况呢?同样,调用监视,结果如下:


很明显发现arr[4]出现了奇怪的值,说明程序本身逻辑就有问题,这时我们不妨用标准库里的strcncpy函数来测试第三种情况,结果如下


可以看到arr[4]的值为‘\0’,并没有出现上述情况,这里我们不妨打开msdn,仔细看一下它的实现原理,如下图:


可见当要拷贝的字符个数n大于源串长度时,进行拷贝时字符串的尾部会附加若干‘空字符’直到长度等于n;到这上述arr【4】=‘%’的出错点相信你应该很明白了:当n递减为2时,进入while循环内部,把‘\0’拷贝进去,然后src指针再向后移动,而此时指针就指向一块未知的空间;当n再递减为1时,拷贝的是未知空间里的那块内容,这就是为什么出现arr【4】=‘%’的原因,具体可看下图:


找到了问题出处,那么接下来我们就对本题的思路重新整合:在模拟实现strncpy中,不再像strcpy那样用*src等于‘\0’来作为循环的结束条件,而是分成了两种情况:

这里字符个数用count代替:

(1)当count小于src所指向的串时,由count决定拷贝什么时候停下来

(2)当count大于src所指向的串时,拷贝是否停下来由*src决定,如果*src=='\0',这时*src后面的内容将不再往dest中拷贝,那么接下来的任务就是往进去拷贝‘\0’,'\0'。。。

例如,源串只有三个字符,而count=6,这时先把这三个字符(包括‘\0’)拷贝进去,这时*src等于‘\0’停了下来,停下来后再进行后边内容的拷贝,也就是一直拷贝‘\0’直到长度等于6。讲到这里不难得出此处(即*src等于‘\0’停下来)的注意点:必须对count的值进行检查,如果大于0,才进行后续‘\0’的不断拷贝。


于是我们可以写出如下代码:

char*my_strncpy(char*dest, const char*src,int count)
{
	assert(dest != NULL);
	assert(src != NULL);
	char*ret = dest;
	while (count&&*src)      
	{
			*dest = *src;
			src++;
			dest++;
			count--;
	}
		if (count != 0)
		{
			while (count > 0)
			{
				*dest = '\0';
				dest++;
				count--;
			}
		}
  return ret;
}

可以看出上面的代码虽然逻辑正确,但整体简洁度很差,其实可以把第一个while循环体部分加入判断条件中,第二个while循环也稍作改变,这样代码更简洁明了,最终的完整代码如下:

#include <stdio.h>
#include <string.h>
#include <assert.h>
char *my_strncpy(char *dest,const char *src, int count)
{
	char *ret = dest;
	assert(dest);
	assert(src);
    while(count && (*dest++ = *src++))
	{
		count--;
	}
	if(count > 0)
	{
		while(--count)
		{
			*dest++ = '\0';
		}
	}
	return ret;
}
int main()
{
		char arr[20] = { 'a', 'b', 'c', 'd', 'e', 'f','g','h' };
		char*p = "bit";
		int count = 0;
		printf("请输入拷贝的字符个数:\n");
		scanf("%d", &count);
		char*ret = my_strncpy(arr, p,5);
		printf("%s\n", ret);
		return 0;
}
对之前所说的第三种情况进行测试,再来看数组arr中的字符变化情况,结果如下:

       

可以看到结果完全正确。

=======================================================================================================================

后记:第一次原创博客,难免有很多不足,如果你偶然看到了这篇文章,如发现有错误,请毫不吝啬地指出,也欢迎多多给予建议~


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值