Есть ли способ в C узнать размер динамически выделяемой памяти?
Например, после
char* p = malloc (100);
Есть ли способ узнать размер памяти, связанной с p
?
Есть ли способ в C узнать размер динамически выделяемой памяти?
Например, после
char* p = malloc (100);
Есть ли способ узнать размер памяти, связанной с p
?
Список часто задаваемых вопросов comp.lang.c · Вопрос 7.27 -
В. Итак, могу ли я запросить пакет malloc
, чтобы узнать, насколько велик выделенный блок?
A. К сожалению, стандартного или портативного способа не существует. (Некоторые компиляторы предоставляют нестандартные расширения.) Если вам нужно знать, вам придется следить за этим самостоятельно. (См. Также вопрос 7.28.)
Стандартного способа найти эту информацию не существует. Однако в некоторых реализациях для этого есть такие функции, как msize
. Например:
Однако имейте в виду, что этот malloc будет выделять минимум запрошенного размера, поэтому вам следует проверить, действительно ли вариант msize для вашей реализации возвращает размер объекта или память, фактически выделенную в куче.
Менталитет C заключается в том, чтобы предоставить программисту инструменты, помогающие ему в работе, а не предоставлять абстракции, которые меняют характер его работы. C также пытается избежать упрощения / безопасности, если это происходит за счет ограничения производительности.
Некоторые вещи, которые вы могли бы сделать с областью памяти, требуют только местоположения начала области. Такие вещи включают в себя работу со строками с завершающим нулем, манипулирование первыми n байтами области (если известно, что область по крайней мере такая большая) и т. Д.
По сути, отслеживание длины региона - это дополнительная работа, и если бы C делал это автоматически, он иногда делал бы это без надобности.
Для многих библиотечных функций (например, fread()
) требуется указатель на начало области, а также размер этой области. Если вам нужен размер региона, вы должны отслеживать его.
Да, реализации malloc () обычно отслеживают размер области, но они могут делать это косвенно, или округлять до некоторого значения, или не сохранять его вообще. Даже если они это поддерживают, определение размера таким способом может быть медленным по сравнению с отслеживанием его самостоятельно.
Если вам нужна структура данных, которая знает, насколько велика каждая область, C может сделать это за вас. Просто используйте структуру, которая отслеживает размер региона, а также указатель на него.
Вот лучший способ, который я видел, для создания помеченного указателя для хранения размера с адресом. Все функции указателя по-прежнему будут работать должным образом:
Украдено с: https://stackoverflow.com/a/35326444/638848
Вы также можете реализовать оболочку для malloc и бесплатно добавлять теги (например, выделенный размер и другую метаинформацию) перед указателем, возвращаемым malloc. Фактически, это метод, которым компилятор C ++ помечает объекты ссылками на виртуальные классы. Вот один рабочий пример:
#include <stdlib.h> #include <stdio.h> void * my_malloc(size_t s) { size_t * ret = malloc(sizeof(size_t) + s); *ret = s; return &ret[1]; } void my_free(void * ptr) { free( (size_t*)ptr - 1); } size_t allocated_size(void * ptr) { return ((size_t*)ptr)[-1]; } int main(int argc, const char ** argv) { int * array = my_malloc(sizeof(int) * 3); printf("%u\n", allocated_size(array)); my_free(array); return 0; }
Преимущество этого метода перед структурой с размером и указателем
struct pointer { size_t size; void *p; };
в том, что вам нужно только заменить malloc и бесплатные звонки. Все остальные операции с указателями не требуют рефакторинга.
&ret[1]
соответствующим образом выровнен, чтобы его можно было назначить указатель на любой тип объекта с фундаментальным требованием выравнивания, который затем используется для доступа к такому объекту или массиву таких объектов в выделенном пространстве. А может и не быть.
- person Andrew Henle; 31.10.2019
&ret[1]
будет соответствующим образом выровнен, но это неверно - нет никаких гарантий, что это так.
- person Andrew Henle; 04.06.2021
malloc()
возвращает правильно выровненную память, my_malloc()
в этом ответе не, потому что он изменяет значение, возвращаемое malloc()
- вызывающий код не гарантируется правильно выровненная память.
- person Andrew Henle; 04.06.2021
Нет, библиотека времени выполнения C не предоставляет такой функции.
Некоторые библиотеки могут предоставлять специфичные для платформы или компилятора функции, которые могут получать эту информацию, но обычно способ отслеживать эту информацию находится в другой целочисленной переменной.
Все, кто говорит вам, что это невозможно, технически правильны (лучший вариант правильного).
По техническим причинам полагаться на подсистему malloc для точного определения размера выделенного блока - плохая идея. Чтобы убедиться в этом, представьте, что вы пишете большое приложение с несколькими разными распределителями памяти - возможно, вы используете необработанную libc malloc
в одной части, но C ++ operator new
в другой части, а затем какую-то конкретную Windows API в еще одной части. Итак, у вас есть все виды void*
летающих. Написать функцию, которая может работать с любыми из этих void*
, невозможно, если вы не сможете каким-то образом определить по значению указателя, из какой из ваших куч он пришел.
Таким образом, вы можете заключить каждый указатель в своей программе с некоторым соглашением, которое указывает, откуда он появился (и куда его нужно вернуть). Например, в C ++ мы называем это std::unique_ptr<void>
(для указателей, которые должны быть operator delete
'd) или std::unique_ptr<void, D>
(для указателей, которые необходимо вернуть через какой-либо другой механизм D
). Вы могли бы сделать то же самое в C, если бы захотели. И как только вы заключите указатели в более крупные и безопасные объекты в любом случае, это всего лишь небольшой шаг до struct SizedPtr { void *ptr; size_t size; }
, и тогда вам больше не придется беспокоиться о размере выделения.
Однако.
Есть также веские причины, по которым вы можете законно захотеть узнать фактический базовый размер выделения. Например, возможно, вы пишете инструмент профилирования для своего приложения, который будет сообщать фактический объем памяти, используемый каждой подсистемой, а не только объем памяти, который подумал программист em > Он пользовался. Если каждое из ваших 10-байтовых распределений тайно использует 16 байтов под капотом, это полезно знать! (Конечно, будут и другие накладные расходы, которые вы не измеряете таким образом. Но есть и другие инструменты для этой работы.) Или, может быть, вы просто исследуете поведение realloc
на ваша платформа. Или, может быть, вы хотите «округлить» емкость растущего выделения, чтобы избежать преждевременного перераспределения в будущем. Пример:
SizedPtr round_up(void *p) {
size_t sz = portable_ish_malloced_size(p);
void *q = realloc(p, sz); // for sanitizer-cleanliness
assert(q != NULL && portable_ish_malloced_size(q) == sz);
return (SizedPtr){q, sz};
}
bool reserve(VectorOfChar *v, size_t newcap) {
if (v->sizedptr.size >= newcap) return true;
char *newdata = realloc(v->sizedptr.ptr, newcap);
if (newdata == NULL) return false;
v->sizedptr = round_up(newdata);
return true;
}
Чтобы получить размер выделения за ненулевым указателем , который был возвращен непосредственно из libc malloc - не из настраиваемой кучи и не указывающий на середину объекта - вы можете использовать следующее API-интерфейсы, специфичные для ОС, которые я для удобства объединил в функцию-оболочку "Portable-ish". Если вы найдете общую систему, в которой этот код не работает, оставьте комментарий, и я постараюсь это исправить!
#if defined(__linux__)
// https://linux.die.net/man/3/malloc_usable_size
#include <malloc.h>
size_t portable_ish_malloced_size(const void *p) {
return malloc_usable_size((void*)p);
}
#elif defined(__APPLE__)
// https://www.unix.com/man-page/osx/3/malloc_size/
#include <malloc/malloc.h>
size_t portable_ish_malloced_size(const void *p) {
return malloc_size(p);
}
#elif defined(_WIN32)
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/msize
#include <malloc.h>
size_t portable_ish_malloced_size(const void *p) {
return _msize((void *)p);
}
#else
#error "oops, I don't know this system"
#endif
#include <stdio.h>
#include <stdlib.h> // for malloc itself
int main() {
void *p = malloc(42);
size_t true_length = portable_ish_malloced_size(p);
printf("%zu\n", true_length);
}
Проверено на:
_msize
malloc_usable_size
malloc_size
malloc
jemalloc и malloc_size
родной libc)USE_DL_PREFIX
Как все уже сказали: нет.
Кроме того, я бы всегда избегал здесь всех функций, зависящих от поставщика, потому что, когда вы обнаруживаете, что вам действительно нужно их использовать, это, как правило, признак того, что вы делаете это неправильно. Размер следует либо хранить отдельно, либо вовсе не знать его. Использование функций поставщика - это самый быстрый способ потерять одно из главных преимуществ написания на C - переносимость.
Я ожидал, что это будет зависеть от реализации.
Если у вас есть структура данных заголовка, вы можете вернуть ее на указатель и получить размер.
free
аналогично для минимального обслуживания (и высокого риска).
- person nik; 15.08.2009
free
, получить размер из указателя может - а может быть - невозможно (однако необходимо знать реализацию). Однако в некоторых реализациях этот примитив может быть указан в API (в этом случае внутренние знания не требуются).
- person nik; 15.08.2009
Если вы используете malloc, вы не можете получить размер.
С другой стороны, если вы используете API ОС для динамического выделения памяти, например Windows heap functions, тогда это возможно.
Что ж, теперь я знаю, что это не ответ на ваш конкретный вопрос, однако мыслить нестандартно ... Мне приходит в голову, что вам, вероятно, не нужно знать. Хорошо, хорошо, нет, я не имею в виду, что у вас плохая или неортодоксальная реализация ... Я имею в виду, что вы, вероятно (не глядя на свой код, я только предполагаю), вы, вероятно, только хотите знать, подходят ли ваши данные в выделенной памяти, если это так, то это решение может быть лучше. Он не должен предлагать слишком много накладных расходов и решит вашу проблему "подгонки", если это действительно то, с чем вы справляетесь:
if ( p != (tmp = realloc(p, required_size)) ) p = tmp;
или если вам нужно сохранить старое содержимое:
if ( p != (tmp = realloc(p, required_size)) ) memcpy(tmp, p = tmp, required_size);
конечно, вы можете просто использовать:
p = realloc(p, required_size);
и покончить с этим.
Этот код, вероятно, будет работать в большинстве установок Windows:
template <class T>
int get_allocated_bytes(T* ptr)
{
return *((int*)ptr-4);
}
template <class T>
int get_allocated_elements(T* ptr)
{
return get_allocated_bytes(ptr)/sizeof(T);
}
Quuxplusone писал: «Написать функцию, которая может работать с любым из этих void *, невозможно, если вы не сможете каким-либо образом определить по значению указателя, из какой из ваших куч он пришел». Определить размер динамически выделяемой памяти в C "
Фактически в Windows _msize дает вам размер выделенной памяти из значения указателя. Если по адресу нет выделенной памяти, выдается ошибка.
int main()
{
char* ptr1 = NULL, * ptr2 = NULL;
size_t bsz;
ptr1 = (char*)malloc(10);
ptr2 = ptr1;
bsz = _msize(ptr2);
ptr1++;
//bsz = _msize(ptr1); /* error */
free(ptr2);
return 0;
}
Спасибо за коллекцию #define. Вот версия макроса.
#define MALLOC(bsz) malloc(bsz)
#define FREE(ptr) do { free(ptr); ptr = NULL; } while(0)
#ifdef __linux__
#include <malloc.h>
#define MSIZE(ptr) malloc_usable_size((void*)ptr)
#elif defined __APPLE__
#include <malloc/malloc.h>
#define MSIZE(ptr) malloc_size(const void *ptr)
#elif defined _WIN32
#include <malloc.h>
#define MSIZE(ptr) _msize(ptr)
#else
#error "unknown system"
#endif
Недавно я боролся с визуализацией памяти, которая была доступна для записи (т.е. с использованием функций типа strcat
или strcpy
сразу после malloc).
Это не должно быть слишком техническим ответом, но он может помочь вам при отладке, так же как и мне.
Вы можете использовать размер, который вы malloc
d, в memset
, установить произвольное значение для второго параметра (чтобы вы могли его распознать) и использовать указатель, который вы получили от malloc
.
Вот так:
char* my_string = (char*) malloc(custom_size * sizeof(char));
if(my_string) { memset(my_string, 1, custom_size); }
Затем вы можете визуализировать в отладчике, как выглядит ваша выделенная память:
char *
. Любой другой тип сопряжен с проблемами согласования и неопределенным поведением.
- person Andrew Henle; 31.10.2019
Примечание: использование _msize
работает только для памяти, выделенной с помощью calloc
, malloc
и т. Д. Как указано в документации Microsoft
Функция _msize возвращает размер в байтах блока памяти, выделенного вызовом
calloc
,malloc
илиrealloc
.
И в противном случае вызовет исключение.
https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/msize?view=vs-2019
Это может сработать, небольшое обновление в вашем коде:
void* inc = (void*) (++p)
size=p-inc;
Но в результате будет 1, то есть память, связанная с p
, если это char*
. Если это int*
, то результат будет 4.
Нет никакого способа узнать общее распределение.
sizeof(char) * …
является избыточным, так какchar
гарантированно будет иметь размер1
. - person mk12   schedule 19.08.2012malloc(100*sizeof(char))
, что соответствует обычному соглашению о размещении единиц справа от количества. - person DepressedDaniel   schedule 29.12.2016TYPE *ptr = malloc(100 * sizeof *ptr)
, где TYPE пишется только один раз. Это гарантирует, что вы получите массив из 100 элементов, даже если вы измените TYPE. - person mk12   schedule 30.12.2016