strcpy这个函数都知道不安全,有多不安全呢,它的底层实现简化一下大概长这样:
void strcpy(char *dst,const char* src )
{
while(*dst++ = *src ++);
}
由于不知道dst的长度,一旦dst < src,就会导致缓冲区溢出。非常危险!
然后有个函数叫 memcpy(),它需要提供一个拷贝长度参数,那么memcpy安全吗?
int main()
{
char src[]="hello,world!111111";
char dst[12];
memcpy(dst,src,12);
cout<<dst<<endl;
return 0;
}
猜猜会输出什么?
答案:
hello,world!111111
很遗憾,这个好像也不太安全。也许可以改成:
int main()
{
char src[]="hello,world!111111";
char dst[12];
memcpy(dst,src,11);
cout<<dst<<endl;
return 0;
}
好像能够正常输出了,但是很遗憾,这样操作的安全性取决于dst[11] 是否是0;如果这样:
int main()
{
char src[]="hello,world!1111";
char dst[12];
dst[11] = 'X';
memcpy(dst,src,11);
cout<<dst<<endl;
return 0;
}
猜会输出什么?
答案:
hello,worldXhello,world!1111 //缓冲区溢出,未定义行为
这又是一个缓冲区溢出的未定义行为。
基于上面,我们可以得出,memcpy函数并不会自动给dst字符串末尾加0;
但是末尾加0真的是我们预期的行为吗,我们只是想copy一个字符串,在目的字符串末尾加0会不会超出使用者的预期,导致编码结果与程序员预期不一样?
int main()
{
int src[]= {1,2,3,4,5,6,7,8,};
int dst[100];
memcpy(dst + 20,src,20);
for(int i =20;i < 25;i++)
{
cout<<dst[i]<<endl;
}
return 0;
}
我用memcpy对数组赋值,如果末尾加0;在以后我要操作时,或许就会出现一些意想不到的情况。
事实上一些所谓安全函数就是在dst字符串末尾加0,来达到所谓得安全。举个例子:
memcpy_s(void *restrict dest, rsize_t dmax, const void *restrict src, rsize_t slen)
这个安全函数,会对两个缓冲区的大小,以及各自指针指向的位置是否合法、是否会产生重叠等进行检查。
显然,这些检查会带来更多开销,而且最重要的,即使有这些检查也不能完全保证这个赋值就是安全的。如果传递的参数 max大于了dest缓冲区的长度,照样会出现缓冲区的溢出。
我们会犯这种低级错误吗? 呵呵。程序的世界越低级的错误越难以debug。如果我们能保证传入的dst和src是有效合理的,即使用strcpy也未尝不可。