strcpy函数小结


前不久去腾讯一面,又遇到这个老生常谈的strcpy函数的实现,一听到HR说了这个函数,心里就暗暗吐槽,前一天还想到这个函数,觉得应该不会考就没细究。回来查了很多篇博客,测试了一下,发现还是有些问题,总结一下。

先从这篇博文开始,http://blog.csdn.net/gpengtao/article/details/7464061,“strcpy函数的实现”。

char *my_strcpy(char *dst,const char *src)
{
	assert(dst != NULL);
	assert(src != NULL);
	char *ret = dst;
	while((* dst++ = * src++) != '\0') 
		;
	return ret;
}

这个基本的函数包括了指针有效性检测、目标指针返回和末尾添'\0',但是没有考虑内存覆盖问题。

使用下面的测试用例,程序就会因为覆盖了原指针字符串的'\0',导致while循环访问到数组外面的内存而崩溃:

char str[10]="abc";
my_strcpy(str + 1, str);

文中说了“调用系统的strcpy函数程序正常运行”,于是我在VS2010上测试了一下,结果如下:

int main()
{
	char str[20] = "abcdefgh";
	printf("before: %s\n", str);
	strcpy(str + 1, str);
	printf("after: %s\n", str);
}

输出:

before: abcdefgh
after: aabcddfghh

虽然程序没有崩溃,但输出也不是正常的,其中出现了两个d和两个h,所以库函数中的strcpy也不能处理内存重叠的问题。接着就讲到用memcpy函数解决内存重叠,但是《C和指针》中写着“如果src和dst以任何形式重叠时,它的结果是未定义的”,那就再测试一下吧。

char *my_strcpy(char *dst,const char *src)
{
	assert(dst != NULL);
	assert(src != NULL);
	char *ret = dst;
	memcpy(dst, src, strlen(src) + 1);
	return ret;
}

对于这个程序,用如下测试用例:

int main()
{
	char str[20] = "abcdefgh";
	printf("before: %s\n", str);
	my_strcpy(str + 1, str);
	printf("after: %s\n", str);
}

VS2010的输出:

before: abcdefgh
after: aabcdefgh

是正确的。

Debian-GCC4.6.3中的输出:

before: abcdefgh
after: aaaaaefgg

是错误的。

VS2010上的memcpy看结果应该是考虑了内存重叠的问题,但Debian的gcc下输出则没考虑内存重叠导致重复输出a的效果,看来这就是结果未定义的表现。

 在这儿http://bbs.chinaunix.net/thread-1305706-1-1.html也提到了,“cpy类的函数对于内存重叠都是未定义”。

 插播一句,在vs2010目录的src中的memcpy源码中,也没有内存重叠的处理,写着“This routine does NOT recognize overlapping buffers, and thus can lead to propogation.” 会不会是因为安装了MinGW的原因,但是在MinGW中找不着源码,那就只能先搁着了。

那还是老老实实用memmove函数吧,memmove函数的实现方式就是第一篇文中的my_memcpy函数,当源地址和目的地址不重叠,低字节向高字节拷贝,否则高字节向低字节拷贝。于是strcpy的实现方式为:

 
char *my_strcpy(char *dst,const char *src)
{
	if(NULL == dst || NULL == src)
	{
		return NULL;
	}
		
	if(src == dst)
	{
		return dst;
    }

	int length = strlen(src);
	char *ret = dst;

	if(src < dst || src > dst + length)//注意'\0'
	{
		while((*dst++ = *src++) != '\0');
	}
	else //内存重叠
	{
		while(length >= 0)
		{
			*(dst + length) = *(src + length);
			length--;
		}
	}

	return ret;

}


当然,这个程序还是不能保证目标指针指向的内存区间是否会越界,再加个size变量做越界检查貌似是画蛇添足了,在这儿也做个记号,有空在看一下strcpy_s、memcpy_s一类的函数。

 最后,”卓越的教练是如何训练高手的?”这的确是一篇非常好的文章,从一个最简单的内存移动函数开始不断完善到完成memcpy的各个细节,以及测试用例的讲解,像我这样的初学者还是得多多学习,在这也姑且总结一下:实现最基础的功能->用void指针提高通用性->使用const修饰减少犯错->NULL指针检查->使用assert调试->内存重叠处理->完善测试用例。还真是写10分钟程序,先思考20分钟,多借鉴优秀程序。

绕了一个大圈,小结到这儿,以后再来自检。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值