Вопрос

Я получаю странные, периодические прерывания передачи данных (< 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++ объединяют объявления строковых литералов в одну строку.

Вы пробовали использовать Валгринд?Обычно это самый быстрый и простой способ устранения подобных ошибок.Если вы читаете или пишете за пределами выделенной памяти, это будет отмечено для вас.

Вы используете Sprintf, который по своей природе небезопасен;Если вы не на 100% позитивно, что вы не будете превышать размер буфера, вы почти всегда предпочитаете SNPRINTF.То же самое относится и к strcat;Предпочитаю более безопасную альтернативную strncat.

Ага.....В последнее время я в основном использую .NET, и старые привычки умирают с трудом.Вероятно, я вытащил этот код из чего-то еще, написанного до меня...

Но впредь постараюсь ими не пользоваться ;)

Знаешь, это может быть даже не твой код...Есть ли какие-либо другие запущенные программы, которые могут иметь утечку памяти?

Это может быть ваш процессор.Некоторые процессоры не могут обращаться к отдельным байтам и требуют, чтобы вы работали со словами или размерами фрагментов, или имели инструкции, которые можно использовать только с данными, выровненными по словам или фрагментам.

Обычно компилятор узнает об этом и работает с ними, но иногда вы можете выделить область как байты, а затем попытаться обратиться к ней как к структуре или полю размером больше байта, и компилятор не уловит это. , но позже процессор выдаст исключение данных.

Этого не произойдет, если вы не используете необычный процессор.Например, ARM9 это сделает, а i686 — нет.Я вижу, что он помечен как Windows Mobile, так что, возможно, у вас действительно проблема с процессором.

Вместо того, чтобы делать 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