4

在调用memset(). 问题是,除非代码运行几天,否则通常不会发生这种情况,因此很难在实际中捕捉到它。

我正在使用以下代码:

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

如您所见,我并没有尝试使用大小比最初分配的更大的 memsetmalloc()

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

4

10 回答 10

21

mallocNULL如果没有可用内存可以返回。你不是在检查那个。

于 2008-08-22T14:21:43.030 回答
4

有几件事。您使用sprintf的是本质上不安全的;除非你 100% 肯定你不会超过缓冲区的大小,否则你应该几乎总是更喜欢snprintf. 这同样适用于strcat; 更喜欢更安全的选择strncat

显然,这可能无法解决任何问题,但它在帮助发现可能非常烦人的错误方面大有帮助

于 2008-08-22T14:26:14.860 回答
3

如果没有可用内存,malloc 可以返回 NULL。你不是在检查那个。

是的,您是...我没有考虑到这一点,因为我正在监视内存并且有足够的空闲空间。有没有办法让系统上有可用内存但 malloc 失败?

是的,如果内存是零散的。此外,当您说“监控内存”时,系统上可能有一些东西偶尔会消耗大量内存,然后在您注意到之前释放它。如果您的呼叫malloc发生然后,将没有任何可用的内存。——乔尔

无论哪种方式...我将添加该检查:)

于 2008-08-22T14:26:28.930 回答
1

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

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

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

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

于 2008-08-22T16:31:49.317 回答
0

你试过使用 Valgrind 吗?这通常是调试此类错误的最快和最简单的方法。如果您在分配的内存范围之外读取或写入,它会为您标记它。

于 2008-08-22T14:18:58.433 回答
0

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

是的.....我最近主要使用.NET,旧习惯很难改掉。我很可能从我之前写的其他东西中提取了那个代码......

但我以后会尽量不使用它们;)

于 2008-08-22T14:28:36.007 回答
0

您知道它甚至可能不是您的代码...是否有任何其他正在运行的程序可能存在内存泄漏?

于 2008-08-22T14:28:47.147 回答
0

它可能是你的处理器。一些 CPU 无法寻址单个字节,并且需要您以字或块大小工作,或者具有只能用于字或块对齐数据的指令。

通常编译器会意识到这些并围绕它们工作,但有时您可以将区域分配为字节,然后尝试将其作为结构或比字节宽的字段进行寻址,编译器不会捕获它,但处理器稍后会抛出数据异常。

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

于 2008-09-20T06:40:13.947 回答
0

而不是做mallocfollow by memset,你应该使用callocwhich 将为你清除新分配的内存。除此之外,按照乔尔所说的去做。

于 2008-09-20T06:46:06.337 回答
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);
于 2008-09-24T17:55:39.983 回答