内存重叠之strcpy/strncpy/strcat/strncat和memcpy---我又踩到地雷了

       前面, 我们说过, 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/)的替代方法, 就相对迂回了点, 需要自己多分配不重叠的内存去解决。


 

 

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
1. atof(): 将字符串转换为double类型的值。 例如: ```c++ char str[] = "3.14"; double num = atof(str); printf("%f", num); ``` 结果为:3.140000 2. atoi(): 将字符串转换为int类型的值。 例如: ```c++ char str[] = "1234"; int num = atoi(str); printf("%d", num); ``` 结果为:1234 3. atol(): 将字符串转换为long类型的值。 例如: ```c++ char str[] = "1234567"; long num = atol(str); printf("%ld", num); ``` 结果为:1234567 4. strtod(): 类似于atof(),将字符串转换为double类型的值。 例如: ```c++ char str[] = "3.14"; double num = strtod(str, NULL); printf("%f", num); ``` 结果为:3.140000 5. strtol(): 将字符串转换为long类型的值,同时支持指定转换的基数(例如10进制、16进制等)和错误检查。 例如: ```c++ char str[] = "0110"; long num = strtol(str, NULL, 2); printf("%ld", num); ``` 结果为:6 6. strtoul(): 类似于strtol(),不过返回的是无符号的long类型。 例如: ```c++ char str[] = "0xA"; unsigned long num = strtoul(str, NULL, 16); printf("%lu", num); ``` 结果为:10 7. memset(): 将一段内存区域设置为指定的值。 例如: ```c++ char str[10]; memset(str, 'a', sizeof(str)); printf("%s", str); ``` 结果为:aaaaaaa 8. memcpy(): 将一段内存区域的内容复制到另一段内存区域。 例如: ```c++ char src[] = "hello"; char dst[10]; memcpy(dst, src, sizeof(src)); printf("%s", dst); ``` 结果为:hello 9. memmove(): 和memcpy()类似,但是保证在有重叠的情况下会正确工作。 例如: ```c++ char str[] = "hello"; memmove(str + 2, str, 3); printf("%s", str); ``` 结果为:hehlo 10. memcmp(): 比较两段内存区域的内容是否相等。 例如: ```c++ char str1[] = "hello"; char str2[] = "Hello"; int result = memcmp(str1, str2, 5); printf("%d", result); ``` 结果为:32(h和H的ASCII码差值) 11. memchr(): 在一段内存区域中搜索指定的字符,并返回指向该字符的指针。 例如: ```c++ char str[] = "hello"; char* ptr = (char*)memchr(str, 'l', 5); printf("%s", ptr); ``` 结果为:ll 12. strcpy(): 将一个字符串复制到另一个字符串。 例如: ```c++ char src[] = "hello"; char dst[10]; strcpy(dst, src); printf("%s", dst); ``` 结果为:hello 13. strncpy(): 类似于strcpy(),不过只会复制指定长度的字符。 例如: ```c++ char src[] = "hello"; char dst[10]; strncpy(dst, src, 3); dst[3] = '\0'; printf("%s", dst); ``` 结果为:hel 14. strcat(): 将一个字符串附加到另一个字符串的末尾。 例如: ```c++ char str1[] = "hello"; char str2[] = "world"; strcat(str1, str2); printf("%s", str1); ``` 结果为:helloworld 15. strncat(): 类似于strcat(),不过只会附加指定长度的字符。 例如: ```c++ char str1[] = "hello"; char str2[] = "world"; strncat(str1, str2, 3); printf("%s", str1); ``` 结果为:helloworld 16. strcmp(): 比较两个字符串是否相等。 例如: ```c++ char str1[] = "hello"; char str2[] = "world"; int result = strcmp(str1, str2); printf("%d", result); ``` 结果为:-15 17. strncmp(): 类似于strcmp(),不过只会比较指定长度的字符。 例如: ```c++ char str1[] = "hello"; char str2[] = "world"; int result = strncmp(str1, str2, 3); printf("%d", result); ``` 结果为:0 18. strchr(): 在一个字符串中搜索指定的字符,并返回指向该字符的指针。 例如: ```c++ char str[] = "hello"; char* ptr = strchr(str, 'l'); printf("%s", ptr); ``` 结果为:llo 19. strrchr(): 类似于strchr(),不过会从字符串的末尾开始搜索。 例如: ```c++ char str[] = "hello"; char* ptr = strrchr(str, 'l'); printf("%s", ptr); ``` 结果为:lo 20. strstr(): 在一个字符串中搜索指定的子字符串,并返回指向该子字符串的指针。 例如: ```c++ char str[] = "hello world"; char* ptr = strstr(str, "world"); printf("%s", ptr); ``` 结果为:world
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值