#include
#include
int main(void)
{
char s[] = "123456789";
char d[] = "123";
strcpy(d,s);
printf ("%s,\n%s",d,s);
return 0;
}
输出是:
123456789,
56789
======>
原因:当开辟出s[]和d[]两个数组的内存单元时,内存是连续的,存放的方式如下:
s数组地址:0x0012ff74,存的值:123456789(隐含值'\0')
d数组地址:0x0012ff70,存的值:123(隐含值'\0')
即内存中的情况是:123\0123456789\0,strcpy以后就变成了123456789\07890
而此时,s和d的地址是没变的 依次指向:
1 2 3 \0 1 2 3 4 5 6 7 8 9 \0 ====> 1 2 3 4 5 6 7 8 9 \0 7 8 9 0
d s d s
无论输出d数组还是s数组都是遇到'\0'结束,因此输出自然是123456789和56789啦~~
知识链接:
微软的strcpy函数是这么写的:
char *cdecl strcpy(char *dst, const char * src)
{
char *cp = dst;
while(*cp++ = * src++)
;
return (dst);
}
分析可知,这样安全漏洞太多了,所以用这个函数必须小心,给目标字符串分配足够的空间才行~~
从上面我们可以看到这样的代码有问题有:
1.没有检查输入的两个指针是否有效。
2.没有检查两个字符串是否以NULL结尾。
3.没有检查目标指针的空间是否大于等于原字符串的空间。
后续思考:
牛人这么多的微软,怎么就没想到这个小小的拷贝函数会造成这么大的安全隐患呢?答案很简单:为了提高性能,减去那些啰嗦的安全检查是必要的。而且,程序员应该知道哪些条件会发生访问违例。这种做法就是把责任推给了程序员,让他来决定安全与性能的取舍(摘自程序员面试宝典)。我想,这也就是C,C++和java等语言的最大区别吧。。真的很深奥又很美好的一门语言啊。
一个拷贝函数的完整的标准写法是这样滴:
#include
#include
#include
void stringcopy(char *to, const char *from)
{
assert(to != NULL && from != NULL);
while(*from != '\0')
{
*to ++ = *from++;
}
*to = '\0';
}
int main(void)
{
char *f;
f = (char *) malloc (15);
stringcopy (f, "hello stringcopy!");
printf ("%s\n",f);
return 0;
}
但是这里有个疑问,严重的疑问。既然是一个完整的标准写法,应该涉及到检查to也就是目标字符串的可预存空间,并只能将值赋在这个空间内,不可越界,这才是真正的安全吧。。。那就涉及到要给stringcopy函数传一个目标字符串预存空间的大小值了。。哦哦,是不是又太麻烦呢。。欢迎各位指正~