memset()导致数据中止

时间:2020-03-05 18:42:02  来源:igfitidea点击:

调用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。在这里,实际上什么都不需要零初始化。

此代码可以安全地执行相同的操作,运行速度更快,并且使用的内存更少。

##代码##