对于常用的字符串拷贝函数,常用的有:
Ansi版本如下:
strcpy, strncpy, strcpy_s, strncpy_s, StringCbCopy
Unicode版本为:
wcscpy,wcsncpy,wcsncpy_s,wcsncpy_s,StringCbCopyW
其中最后一个为Windows的API,其余为c运行时函数。
这些函数完成的功能是一样的,然而本质上却有极大区别。
现在我们来看看这些函数分别如何工作。
为简化测试,我们只测试Ansi版本的函数。Unicde版本功能与之如出一辙。
测试代码如下(VS2005,多字节编码):
- char buff1[2] = {'1', '/0'};
- char buff2[2] = {'2', '/0'};
- strcpy(buff1, "1111111111111111");
- strncpy(buff2, "abcdefghijklmnopqratuvwxyz", 2);
- StringCbCopy(buff2, 2, "abcdefghijklmnopqratuvwxyz");
- strcpy_s(buff2, 2, "abcdefghijklmnopqratuvwxyz");
buff2的地址为0x22FE0C,内存分布如下图:
可以看到buff1和buff2之间被填充了0xcc,这是VS为了检测缓冲区运行时整的。
另外注意栈内存分配上的特点,后分配的buff2的地址在先分配的buff1地址之前。
现在执行strcpy,看内存变化:
strcpy强行把16字节写入了buff1为首的内存。然而系统没有任何反映,似乎一切正常。
然而,正式这种侥幸的正常,才会导致日后不可避免的缓冲区溢出导致的崩溃---谁知道buff1+2之后的14个字节是什么内容。
继续执行,看看strncpy的表现会不会好一点:
strncpy的第三个参数接收缓冲区大小,如果待拷贝字符串长度超过缓冲区,则截断超出的字符不拷贝。
这么做似乎很安全。然而致命的问题是这个“截断“太过劣质。
因为buff2失去了'/0'结束符,buff2字符串是从buff2开始直到之后内存中找到的第一个'/0'
在这里,由于buff2内存之后是buff1,buff2已经不再是2个字节的字符串了。缓冲区受到了破坏。
这个例子里,我们看到buff2的内容是形如"ab烫烫烫烫烫烫烫烫1111111111111111"这样的乱码
诚然,这样的操作也是极不安全的。
我们继续执行,看看StringCbCopy表现如何。
呼呼,我们终于看到一个表现堪称“不错”的字符串拷贝函数了。
她总算是“优美”的截断了过长的字符串,保证了缓冲区的安全性。
然而,这样的截断后的字符串是你需要的吗?比如一个路径名字符串被截断了,你还能用他来打开文件吗?
strcpy_s的第2个参数接收缓冲区大小。我们看看他执行结果如何:
饿...出现了assert错误。
不过这样对程序员来说应该是最好的,你很快能知道是哪里出了问题。
一般而言,凡是函数后面带有_s的字符串系列函数,都是微软整的“安全”字符串函数。
他会进行传入指针不为NULL、缓冲区足够大等等安全检查。
因此,我们在整字符串的时候,应该使用这些安全字符串。
最后请注意:上面所有函数都不进行内存重叠检查!请自己进行必要的内存重叠检查。
原文地址:http://blog.youkuaiyun.com/lsldd/article/details/4669888