【C语言】strncpy详解

1.不考虑内存重叠的strncpy

网上很多博客也写了这个函数,面试也常常会遇到,但是,我发现网上的很多代码都是有问题的,我们先看下大部分网上博客的实现:

char *strncpy(char *dst, const char *src, size_t len)
{
	assert(dst != NULL && src != NULL);
	char *res = dst;
	while (len--)
	{
		*dst++ = *src++;
	}
	return res;
}

看着好像没啥问题,但是,当src的长度小于len呢?这份代码没有处理这个问题。当src的长度小于len时,应该如何处理?《C和指针》p179给出的答案是:
“ 和strcpy一样,strncpy把源字符串的字符复制到目标数组。然而,它总是正好向dst写入len个字符。如果strlen(src)的值小于len,dst数组就用
额外的NUL字节填充到len长度,如果strlen(src)的值大于或等于len,那么只有len个字符被复制到dst中。”
注意!它的结果将不会以NUL字节结尾。(NUL即‘\0’).

由此可见,我们还需要判断strlen(src)是否小于len,如果是,还需要在dst后面添加NUL,因此,正确的代码应该如下:

char *strncpy(char *dest, const char *src, size_t len)
{
	assert(dest != NULL && src != NULL);
	char *res = dest;
	int offset = 0;
	if (strlen(src) < len)//src长度小于len
	{
		offset = len - strlen(src);
		len = strlen(src);
	}

	while (len--)
	{
		*dest++ = *src++;
	}
	while (offset--)
	{
		*dest++ = '\0';
	}
	return res;
}

使用这个函数,尤其需要注意,不要出现len>strlen(dst)的情况,如果len>strlen(dst),那么会破坏dst后面的内存:【C语言】strncpy详解
我们假设前面红色部分是dst,然后strncpy(dst,src,10);那么后面黄色部分的内存就被破坏了。strncpy是不负责检测len是否大于dst长度的。

总的来说,strncpy总是复制len个字符到dst指向的内存!!!

所以,还会出现下面的情况

char message[] = "abcd";
strncpy(message, "abcde",5);
cout << message;

输出是abcde烫烫烫烫烫烫烫烫烫烫烫烫烫烫 (结果不唯一)

message的内存是有5个字节的,但是将abcde拷贝过去时,最后面的‘\0’被覆盖了,strncpy并不会负责添加‘\0’到dst结尾,因此,输出该字符串是,会在e字符后面一直找到‘\0’才结束,因此就会出现乱码。

2.考虑内存重叠的strncpy

面试中经常会遇到让你写一个能够处理内存重叠的strncpy,标准库中的strncpy是不考虑内存重叠的,如果出现内存重叠,结果将是未定义的。
网上的很多博客也有这个代码的实现,其实很多也是有问题的,没有考虑src长度小于len的问题:

char *strncpy(char *dst, const char *src, size_t len)
{
	assert(dst != NULL && src != NULL);
	char *res = dst;
	if (dst >= src && dst <= src + len - 1)//重叠,从后向前复制
	{
		dst = dst + len - 1;
		src = src + len - 1;
		while (len--)
			*dst-- = *src--;
	}
	else
	{
		while (len--)
			*dst++ = *src++;
	}
	return res;
}

那么,如果要处理内存重叠,该怎么办?如果内存重叠和src的长度小于len这两种情况同时出现,又如何处理?【C语言】strncpy详解
见图,假设红色部分为src,黄色为dst。如果出现内存重叠,我们很容易想到:从后往前拷贝。如果src的长度小于len,则在后面补NUL。

char *strncpy(char *dst, const char *src, size_t len)
{
	assert(dst != NULL && src != NULL);
	char *res = dst;
	int offset = 0;
	char *tmp;
	if (strlen(src) < len)//src长度小于len
	{
		offset = len - strlen(src);
		len = strlen(src);
	}

	if (dst >= src && dst <= src + len - 1)//重叠,从后向前复制
	{
		dst = dst + len - 1;
		src = src + len - 1;
		tmp = dst;
		while (len--)
			*dst-- = *src--;
	}
	else
	{
		while (len--)
			*dst++ = *src++;
		tmp = dst;
	}
	while (offset--)
	{
		*tmp++ = '\0';
	}
	return res;
}

那么,如果len的值大于dst的值,就会破坏dst后面的内存空间,这应该是要避免的。

最后,我们看一个有意思的东西:(此处strncpy是考虑内存重叠的版本)
【C语言】strncpy详解
message的长度增加了0.0 当然 ,它后面的内存被破坏了,这可能带来严重的后果。
最后,使用strncpy时,最好自动添加‘\0’在结尾。

上一篇:从一个有序数列中移除重复出现的数字


下一篇:warpAffine 仿射变换图像填充算法对比