Memset () вызывает прерывание данных

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

Кто-нибудь видит, что в этом может быть не так?

Пожалуйста, отредактируйте этот вопрос, потому что я проголосовал против, и если подумать ...

user12211554 20.05.2020 01:43
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
1
4 923
10
Перейти к ответу Данный вопрос помечен как решенный

Ответы 10

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

Ответ принят как подходящий

malloc может вернуть NULL, если память недоступна. Вы этого не проверяете.

Также обратите внимание, что из-за оптимистичного распределения памяти у вас достаточно памяти не потому, что maloc () возвращает ненулевое значение. Он потерпит неудачу при первом фактическом обращении к памяти, которая будет здесь ... memset ().

Ben 24.07.2009 11:44

@Ben: Вы очень правы для таких ОС, как настольный Linux. Но я сомневаюсь, что мобильная ОС вроде Windows Mobile будет оптимистично распределять ресурсы. Это не имеет большого смысла в системе с ограниченным ОЗУ и без виртуальной ОЗУ.

Zan Lynx 22.09.2010 03:37

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

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

Нет, не используйте strncat. Он заполняет весь буфер. Это убивает производительность, если вы используете большой буфер. Он попал в верхнюю часть моего графика профилировщика в одном проекте, который использовал буфер 8K для URL-адресов.

Zan Lynx 30.07.2009 07:40

@Zan Lynx: какая альтернатива strncat?

Cristian Ciupitu 22.09.2010 02:23

@Cristian: strlcat - один из вариантов. Возможно, вам придется скопировать код в свой проект, но это не всегда в среде разработки. Отслеживание длины вашей строки (или использование указателей на начало буфера, текущую позицию и конец буфера) в источнике и месте назначения и использование memcpy - мой любимый метод.

Zan Lynx 22.09.2010 03:35

@Zan Lynx: strlcat может быть лучше, чем strncat с семантической точки зрения, но я до сих пор не понимаю, как его производительность могла бы быть лучше. В документ, представляющий strlcpy и strlcat упоминается только strncpy: «Наконец, strncpy () заполняет нулями оставшуюся часть целевой строки, что снижает производительность». Между прочим, моя страница руководства Linux strncat ничего не говорит о снижении производительности.

Cristian Ciupitu 22.09.2010 05:36

@Cristian: Да ну. Сейчас, когда вы это пишете, я понимаю, что все время имел в виду strncpy. Этого вам следует избегать, и мой предыдущий комментарий теперь глупый.

Zan Lynx 22.09.2010 05:40

malloc can return NULL if no memory is available. You're not checking for that.

Правильно ... Я не думал об этом, так как следил за памятью, а там было достаточно свободного места. Есть ли способ, чтобы в системе была доступная память, но для сбоя malloc?

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

В любом случае ... Я добавлю этот чек :)

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.

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

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

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

wcstombs не получает размер места назначения, поэтому теоретически может возникнуть переполнение буфера.

И почему вы используете sprintf с, как я полагаю, константами? Просто используйте:

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

C и C++ объединяют объявления строковых литералов в одну строку.

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

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

Этого бы не произошло, если бы вы не использовали необычный процессор. Например, 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);

Другие вопросы по теме