当调用时,我的某些代码中出现一些奇怪的、间歇性的数据中止(< 5% 的时间) 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);

正如您所看到的,我并没有尝试使用比最初分配的大小更大的 memset malloc()

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

有帮助吗?

解决方案

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

其他提示

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

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

如果没有内存,malloc 可以返回 NULL。 可用。您没有检查 那。

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

是的,如果记忆是碎片的话。另外,当您说“监视内存”时,系统上可能有某些东西偶尔会消耗大量内存,然后在您注意到之前将其释放。如果您致电 malloc 发生这种情况时,将没有任何可用内存。-- 乔尔

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

wcstombs 无法获取目标的大小,因此从理论上讲,它可能会导致缓冲区溢出。

你为什么使用 sprintf 我假设是常数?只需使用:

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

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

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

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

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

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

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

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

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

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

而不是做 malloc 其次是 memset, ,你应该使用 calloc 这将为您清除新分配的内存。除此之外,按乔尔说的去做。

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);
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top