Я получаю странные, прерывистые прерывания данных (<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().
Кто-нибудь видит, что в этом может быть не так?





Вы пробовали использовать Valgrind? Обычно это самый быстрый и простой способ отладки подобных ошибок. Если вы читаете или пишете за пределами выделенной памяти, он отметит это за вас.
malloc может вернуть NULL, если память недоступна. Вы этого не проверяете.
Также обратите внимание, что из-за оптимистичного распределения памяти у вас достаточно памяти не потому, что maloc () возвращает ненулевое значение. Он потерпит неудачу при первом фактическом обращении к памяти, которая будет здесь ... memset ().
@Ben: Вы очень правы для таких ОС, как настольный Linux. Но я сомневаюсь, что мобильная ОС вроде Windows Mobile будет оптимистично распределять ресурсы. Это не имеет большого смысла в системе с ограниченным ОЗУ и без виртуальной ОЗУ.
Есть пара вещей. Вы используете sprintf, который по своей сути небезопасен; если вы на 100% не уверены, что не собираетесь превышать размер буфера, вы должны почти всегда предпочесть snprintf. То же самое и с strcat; предпочитаю более безопасную альтернативу strncat.
Очевидно, что это может ничего не исправить, но это способ длинный, помогающий обнаруживать ошибки, которые в противном случае могли бы очень раздражать.
Нет, не используйте strncat. Он заполняет весь буфер. Это убивает производительность, если вы используете большой буфер. Он попал в верхнюю часть моего графика профилировщика в одном проекте, который использовал буфер 8K для URL-адресов.
@Zan Lynx: какая альтернатива strncat?
@Cristian: strlcat - один из вариантов. Возможно, вам придется скопировать код в свой проект, но это не всегда в среде разработки. Отслеживание длины вашей строки (или использование указателей на начало буфера, текущую позицию и конец буфера) в источнике и месте назначения и использование memcpy - мой любимый метод.
@Zan Lynx: strlcat может быть лучше, чем strncat с семантической точки зрения, но я до сих пор не понимаю, как его производительность могла бы быть лучше. В документ, представляющий strlcpy и strlcat упоминается только strncpy: «Наконец, strncpy () заполняет нулями оставшуюся часть целевой строки, что снижает производительность». Между прочим, моя страница руководства Linux strncat ничего не говорит о снижении производительности.
@Cristian: Да ну. Сейчас, когда вы это пишете, я понимаю, что все время имел в виду strncpy. Этого вам следует избегать, и мой предыдущий комментарий теперь глупый.
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
mallococcurs 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 позаимствовал некоторые комментарии из других ответов и объединил их в единое целое. Код весь мой ...
Этот код делает то же самое, безопасно, работает быстрее и использует меньше памяти.
// 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);
Пожалуйста, отредактируйте этот вопрос, потому что я проголосовал против, и если подумать ...