memset()导致数据中止
调用memset()
时,我的一些代码中出现了一些奇怪的,断断续续的数据中止(<5%的时间)的情况。问题是,除非代码运行了几天,通常是不会发生的,因此很难将其付诸实践。
我正在使用以下代码:
char *msg = (char*)malloc(sizeof(char)*2048); char *temp = (char*)malloc(sizeof(char)*1024); memset(msg, 0, 2048); memset(temp, 0, 1024); char *tempstr = (char*)malloc(sizeof(char)*128); sprintf(temp, "%s %s/%s %s%s", EZMPPOST, EZMPTAG, EZMPVER, TYPETXT, EOL); strcat(msg, temp); //Add Data memset(tempstr, 'malloc can return NULL if no memory is available. You're not checking for that.', 128); wcstombs(tempstr, gdevID, wcslen(gdevID)); sprintf(temp, "%s: %s%s", "DeviceID", tempstr, EOL); strcat(msg, temp);
如我们所见,我不打算使用大小大于malloc()最初分配的大小的memset。
有人看到这可能是什么问题吗?
解决方案
回答
我们是否尝试过使用Valgrind?通常,这是调试此类错误的最快,最简单的方法。如果我们正在分配的内存范围之外进行读取或者写入,它将为我们标记。
回答
如果没有可用的内存,则malloc可以返回NULL。我们不需要检查。
回答
有几件事。我们正在使用本质上不安全的sprintf
。除非我们100%肯定不会超出缓冲区的大小,否则应该几乎总是喜欢snprintf
。这同样适用于strcat
;更喜欢更安全的替代品" strncat"。
显然,这可能无法解决任何问题,但是在帮助发现可能会很烦人的bug方面走了很长的路要走。
回答
Yes, if memory is fragmented. Also, when you say "monitoring memory," there may be something on the system which occasionally consumes a lot of memory and then releases it before you notice. If your call to malloc occurs then, there won't be any memory available. -- Joel
是的,我们是...我在监视内存时没有考虑这一点,它有足够的可用空间。有什么办法可以使系统上有可用内存,但malloc失败?
You're using sprintf which is inherently unsafe; unless you're 100% positive that you're not going to exceed the size of the buffer, you should almost always prefer snprintf. The same applies to strcat; prefer the safer alternative strncat.
无论哪种方式...我都会添加该检查:)
回答
// sizeof(char) is 1 by definition. This memory does not require zero // initialisation. If it did, I'd use calloc. const int max_msg = 2048; char *msg = (char*)malloc(max_msg); if(!msg) { // Allocaton failure return; } // Use snprintf instead of sprintf to avoid buffer overruns // we write directly to msg, instead of using a temporary buffer and then calling // strcat. This saves CPU time, saves the temporary buffer, and removes the need // to zero initialise msg. snprintf(msg, max_msg, "%s %s/%s %s%s", EZMPPOST, EZMPTAG, EZMPVER, TYPETXT, EOL); //Add Data size_t len = wcslen(gdevID); // No need to zero init this char* temp = (char*)malloc(len); if(!temp) { free(msg); return; } wcstombs(temp, gdevID, len); // No need to use a temporary buffer - just append directly to the msg, protecting // against buffer overruns. snprintf(msg + strlen(msg), max_msg - strlen(msg), "%s: %s%s", "DeviceID", temp, EOL); free(temp);
是的.....我最近主要使用.NET,老习惯很难解决。我可能会把这段代码从我之前写过的其他东西中取出来...
但我会尽量不要在将来使用它们;)
回答
我们知道它甚至可能不是代码...是否正在运行其他程序,这些程序可能会导致内存泄漏?
回答
wcstombs不会得到目的地的大小,因此从理论上讲它可以缓冲缓冲区溢出。
以及为什么在我假定为常量的情况下使用sprintf
?只需使用:
EZMPPOST"" EZMPTAG" /" EZMPVER"" TYPETXT EOL
C和C ++将字符串文字声明合并为单个字符串。
回答
可能是处理器。某些CPU无法寻址单个字节,要求我们以字或者块大小工作,或者具有只能用于字或者块对齐数据的指令。
通常,编译器会意识到这些问题并加以解决,但是有时我们可以将区域分配为字节,然后尝试将其作为结构或者大于字节的字段来寻址,而编译器将无法捕获该字段。 ,但处理器稍后将引发数据异常。
除非我们使用不寻常的CPU,否则不会发生这种情况。例如,ARM9会这样做,但i686不会。我看到它被标记为Windows Mobile,所以也许我们确实有此CPU问题。
回答
应该使用calloc
代替为malloc
紧随其后的是memset
,这将为我们清除新分配的内存。除此之外,请按照乔尔所说的做。
回答
NB从其他答案中借鉴了一些意见,并整合为一个整体。代码都是我的...
- 检查错误代码。例如。如果没有可用的内存,则malloc可以返回NULL。这可能会导致数据中止。
- sizeof(char)根据定义为1
- 我们正在使用的内存远远超出了需要。
- 进行一次较小的更改,我们无需首先调用memset。在这里,实际上什么都不需要零初始化。
此代码可以安全地执行相同的操作,运行速度更快,并且使用的内存更少。
##代码##