前面, 我们说过, strcpy是一个非常不安全的函数, 如果大家在项目中还在用strcpy, 那是不是应该反思一下呢? 于是, 有了更好的strncpy.
但是, 有一次, 我在用strncpy的时候,我觉得很自然啊, 却犯了一个非常隐蔽的错误, 只是在当时的情况下, 该错误没有暴露出来。 在其他情况下, 肯定是有错误的。 原来实际是出现了内存重叠, 被某高人发现, 我心服口服啊。
看看有内存重叠的strcpy(为了简便起见, 我就不说strncpy的内存重叠了)
#include <iostream>
using namespace std;
int main()
{
char str[100] = {0};
strncpy(str, "abcdefghi", 100 - 1);
strcpy(str + 2, str); // 内存重叠了, bug!!!
cout << str << endl;
return 0;
}
我们期待的结果是:ababcdefghi, 但实际的结果是:ababcdcdghgh (在vc++6.0环境下),原来, strcpy/strncpy是没有考虑内存重叠的。 我不小心踩到了, 也算一种经历吧, 做软件开发就是这样, 犯错可以, 但不要犯相同的错误, 善于从错误中学习。
那怎么办呢?
#include <iostream>
using namespace std;
int main()
{
char str[100] = {0};
strncpy(str, "abcdefghi", 100 - 1);
cout << str << endl;
char szTemp[100] = {0};
strncpy(szTemp, str, sizeof(szTemp) - 1);
strcpy(str + 2, szTemp); // 这里是示意, 防止越界啊
cout << str << endl;
return 0;
}
我们期待的结果是:ababcdefghi, 实际结果也是这样的。
微软啊微软, 你咋就没有提前解决这个bug呢? 下面, 我来尝试写一个, 不一定100%稳健, 希望大家提出其中可能存在的bug.
#include <iostream>
using namespace std;
char *myStrCpy(char *dst, const char *src)
{
if(NULL == dst || NULL == src)
{
return NULL;
}
int len = strlen(src);
char *p = (char *)malloc(len + 1);
if(NULL == p)
{
return NULL;
}
char *q = p;
p[len] = '\0'; // 有必要
while(*p++ = *src++)
{
;
}
p = q;
while(*dst++ = *p++)
{
;
}
free(q); // 不是free(p);
return dst;
}
int main()
{
char str[100] = {0};
strncpy(str, "abcdefghi", 100 - 1);
myStrCpy(str + 2, str);
cout << str << endl;
return 0;
}
结果:ababcdefghi, 和预期一致。 有bug的话, 欢迎大家拍砖, 共同进步。
另外, 在内存拷贝时候, strcat/strncat/memcpy也是不允许内存重叠的, 总之, 一定要注意内存重叠问题。 memcpy替代的解决方法是memmove. 原型是:
void *memmove( void* dest, const void* src, size_t count ); 至于其他函数(如 strcpy/strncpy/strcat/strncat/)的替代方法, 就相对迂回了点, 需要自己多分配不重叠的内存去解决。