memset()导致数据中止

时间:2008-08-22 14:17:02

标签: c++ c memory windows-mobile

在调用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, '\0', 128);
wcstombs(tempstr, gdevID, wcslen(gdevID));
sprintf(temp, "%s: %s%s", "DeviceID", tempstr, EOL);
strcat(msg, temp);

正如你所看到的,我并没有尝试使用尺寸大于最初用malloc()分配的memset

有人看到这可能有什么问题吗?

10 个答案:

答案 0 :(得分:21)

如果没有可用内存,

malloc可以返回NULL。你没有检查它。

答案 1 :(得分:4)

有几件事。你正在使用本身不安全的sprintf;除非你100%肯定你不会超过缓冲区的大小,否则你应该总是更喜欢snprintf。这同样适用于strcat;更喜欢更安全的替代strncat

显然,这可能无法解决任何问题,但它采用的方式来帮助发现可能会发现可能非常烦人的错误。

答案 2 :(得分:3)

  如果没有内存,

malloc可以返回NULL   可用。你没有检查   这一点。

对,你是......我没有想到这一点,因为我正在监视记忆,而且有足够的免费。有没有办法在系统上有可用的内存,但是malloc失败了?

  

是的,如果内存碎片化。此外,当您说“监视内存”时,系统上可能会有某些内容偶尔消耗大量内存,然后在您发现之前将其释放。如果您对malloc的呼叫发生,则不会有任何可用内存。 - Joel

无论哪种方式......我都会加上检查:)

答案 3 :(得分:1)

wcstombs没有得到目的地的大小,所以理论上它可以缓冲溢出。

为什么你使用sprintf我认为是常量?只需使用:

EZMPPOST" " EZMPTAG "/" EZMPVER " " TYPETXT EOL

C和C ++将字符串文字声明组合成一个字符串。

答案 4 :(得分:0)

你尝试过使用Valgrind吗?这通常是调试这些错误的最快速,最简单的方法。如果您正在读取或写入超出已分配内存的范围,它将为您标记它。

答案 5 :(得分:0)

  

你正在使用sprintf   本质上不安全;除非你是100%   积极的,你不会   超过缓冲区的大小,你   应该几乎总是喜欢snprintf。   这同样适用于strcat;更喜欢   更安全的替代strncat。

是的.....我最近主要做.NET,老习惯很难。我可能会把这段代码从我之前写的其他东西中拉出来......

但是我将来会尝试不使用那些;)

答案 6 :(得分:0)

你知道它甚至可能不是你的代码......是否还有其他正在运行的程序会导致内存泄漏?

答案 7 :(得分:0)

它可能是你的处理器。某些CPU无法处理单个字节,并且要求您使用单词或块大小,或者具有只能用于字或块对齐数据的指令。

通常编译器会知道这些并解决它们,但有时您可以将一个区域作为字节malloc,然后尝试将其作为结构或宽于一个字节的字段来解决,编译器赢了“抓住它,但处理器稍后会抛出数据异常。

除非您使用的是异常CPU,否则不会发生这种情况。例如,ARM9会这样做,但i686不会。我看到它被标记为windows mobile,所以也许你有这个CPU问题。

答案 8 :(得分:0)

您应该使用malloc来清除新分配的内存,而不是memset后跟calloc。除此之外,做乔尔所说的。

答案 9 :(得分:0)

NB从其他答案中借鉴了一些评论并整合到一起。代码全是我的......

  • 检查错误代码。例如。如果没有可用内存,malloc可以返回NULL。这可能导致您的数据中止。
  • 根据定义,
  • sizeof(char)为1
  • 使用snprintf而不是sprintf来避免缓冲区溢出
    • 如果EZMPPOST等是常量,那么你不需要格式字符串,你可以将几个字符串文字组合为STRING1“”STRING2“”STRING3和strcat整个批次。
  • 你使用的内存比你需要的多得多。
  • 只需进行一次小改动,您就不需要首先调用memset。没有 这里真的需要零初始化。

此代码执行相同的操作,安全,运行速度更快,占用内存更少。

    // 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);