Есть ли способ в C, чтобы узнать размер динамически распределенной памяти?
Например, после
char* p = malloc (100);
Есть ли способ узнать размер памяти, связанный с p
?
Есть ли способ в C, чтобы узнать размер динамически распределенной памяти?
Например, после
char* p = malloc (100);
Есть ли способ узнать размер памяти, связанный с p
?
comp.lang.c Часто задаваемые вопросы · Вопрос 7.27 -
Q. Итак, могу ли я запросить пакет malloc
, чтобы узнать, насколько большой выделенный блок является?
а. К сожалению, нет стандартного или переносного способа. (Некоторые компиляторы предоставляют нестандартные расширения.) Если вам нужно знать, вам придется самому отслеживать это. (См. Также вопрос 7.28.)
Не существует стандартного способа найти эту информацию. Однако некоторые реализации предоставляют такие функции, как msize
чтобы сделать это. Например:
Имейте в виду, однако, что malloc выделит минимум запрошенного размера, поэтому вы должны проверить, действительно ли msize-вариант для вашей реализации возвращает размер объекта или памяти, фактически выделенной в куче.
C менталитет состоит в том, чтобы предоставить программисту инструменты, помогающие ему выполнять его работу, а не предоставлять абстракции, которые изменяют характер его работы. C также пытается избежать упрощения/повышения безопасности, если это происходит за счет предела производительности.
Некоторым вещам, которые вы могли бы сделать с областью памяти, требуется только расположение начала региона. К таким вещам относятся работа с нулевыми строками, управление первыми n байтами области (если область, как известно, по крайней мере такая большая) и т.д.
В основном, отслеживание длины области - это дополнительная работа, и если C сделал это автоматически, это иногда делалось бы без необходимости.
Для многих функций библиотеки (например, fread()
) требуется указатель на начало области, а также размер этой области. Если вам нужен размер региона, вы должны отслеживать его.
Да, реализации malloc() обычно отслеживают размер региона, но они могут делать это косвенно или округлять его до некоторой ценности или вообще не сохранять. Даже если они поддерживают его, поиск размера таким образом может быть медленным по сравнению с тем, чтобы отслеживать его самостоятельно.
Если вам нужна структура данных, которая знает, насколько велик каждый регион, C может сделать это за вас. Просто используйте структуру, которая отслеживает, насколько велика область, а также указатель на область.
Нет, библиотека времени выполнения C не предоставляет такую функцию.
Некоторые библиотеки могут предоставлять функции, специфичные для платформы или компилятора, которые могут получить эту информацию, но в целом способ отслеживания этой информации находится в другой целочисленной переменной.
Вот лучший способ, по которому я видел создание тега с указателем для хранения размера с адресом. Все функции указателя будут работать, как ожидалось:
Украдены из: fooobar.com/questions/125847/...
Вы также можете реализовать оболочку для malloc и добавить теги (например, выделенный размер и другая метаинформация) перед указателем возвращен malloc. Это фактически метод, который компилятор С++ теги со ссылками на виртуальные классы. Вот один рабочий Пример:
#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 и бесплатные вызовы. Все другие операции указателя не требуют рефакторинга.
Как и все остальные уже сказали: "Нет".
Кроме того, я всегда буду избегать всех функций, связанных с конкретными производителями, потому что, когда вы обнаружите, что вам действительно нужно их использовать, это обычно признак того, что вы делаете это неправильно. Вы должны либо хранить размер отдельно, либо не знать об этом вообще. Использование функций поставщика - это самый быстрый способ потерять одно из основных преимуществ написания на C, переносимости.
Я ожидаю, что это будет зависимым от реализации.
Если у вас есть структура данных заголовка, вы можете вернуть его на указатель и получить размер.
Нет, нет.
Если вы используете malloc, вы не можете получить размер.
С другой стороны, если вы используете OS API для динамического выделения памяти, например Windows функции кучи, тогда это можно сделать.
Этот код, вероятно, будет работать на большинстве установок 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);
}
Это может сработать, небольшое обновление в вашем коде:
void* inc = (void*) (++p)
size=p-inc;
Но это приведет к 1, то есть памяти, связанной с p
, если она char*
. Если это int*
, тогда результат будет равен 4.
Нет способа узнать общее распределение.
Хорошо, теперь я знаю, что это не отвечает на ваш конкретный вопрос, но, думая, вне коробки, как бы... Это происходит со мной, вам, вероятно, не нужно знать. Ок, ок, нет, я не имею в виду, что у вас плохая или не ортодоксальная реализация... Я имею в виду, что вы, вероятно, (не глядя на свой код, я только догадываюсь), вы только хотите знать, могут ли ваши данные в выделенной памяти, если это так, то это решение может быть лучше. Он не должен предлагать слишком много накладных расходов и решит вашу "подходящую" проблему, если это действительно то, что вы обрабатываете:
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);
и выполняться с ним.
Все, кто говорит вам, что это невозможно, технически правильно (лучший вид правильно).
По техническим причинам плохая идея полагаться на подсистему malloc для точного определения размера выделенного блока. Чтобы убедиться в этом, представьте, что вы писали большое приложение с несколькими различными распределителями памяти - возможно, вы используете raw libc malloc
в одной части, но operator new
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; }
struct SizedPtr { void *ptr; size_t size; }
struct SizedPtr { void *ptr; size_t size; }
и тогда вам больше не нужно беспокоиться о размере выделения.
Тем не мение.
Существуют также веские причины, по которым вы можете на законных основаниях знать фактический базовый размер выделения. Например, возможно, вы пишете инструмент профилирования для своего приложения, который будет сообщать о фактическом объеме памяти, используемой каждой подсистемой, а не только об объеме памяти, который, по мнению программиста, он использовал. Если каждое из ваших 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
и нативный libc malloc_size
)USE_DL_PREFIX
int *a; a=malloc(n*sizeof(int));
если malloc возвращает NULL, это означает, что вы не получили память, иначе вы получите базовый адрес назначенного блока, то есть размер блока (n*sizeof(int))
.
Quuxplusone писал: "Написание функции, которая может работать с любым из этих void * s, невозможно, если вы не можете каким-то образом определить из значения указателя, из какой кучи это произошло". Определить размер динамически выделяемой памяти в 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
Я не уверен, но попробую:
char **q = &p;
int size = q[1] - q[0];