Определить размер динамически выделяемой памяти в C

есть ли способ В C узнать размер динамически выделенной памяти?

например, после

char* p = malloc (100);

есть ли способ узнать размер памяти, связанной с p?

15 ответов


comp.ленг.C список FAQ · вопрос 7.27 -

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

A. К сожалению, нет стандартного или портативного способа. (Некоторые компиляторы предоставляют нестандартные расширения. Если вам нужно знать, вы должны будете следить за этим сами. (См. также вопрос 7.28.)


нет стандартного способа найти эту информацию. Однако некоторые реализации предоставляют такие функции, как msize для этого. Например:

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


некоторые вещи, которые вы могли бы сделать с областью памяти, требуют только место старта регионе. Такие вещи включают работу с нулевыми строками, манипулируя первым n байт области (если известно, что область по крайней мере такая большая) и так далее.

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

многие библиотечные функции (например,fread()) требует указатель на начало области, а также размер этой области. Если вам нужен размер региона, вы должны отслеживать его.

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

Если вам нужна структура данных, которая знает, насколько велика каждая область, C может сделать это за вас. Просто используйте структуру, которая отслеживает, насколько велика область, а также указатель на регион.


нет, библиотека времени выполнения C не предоставляет такой функции.

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


Как все уже говорили: Нет, нет.

кроме того, я бы всегда избегал всех функций, специфичных для поставщика, потому что, когда вы обнаружите, что вам действительно нужно их использовать, это обычно признак того, что вы делаете это неправильно. Вы должны либо хранить размер отдельно, либо не знать его вообще. Использование функций поставщика-Это самый быстрый способ потерять одно из основных преимуществ написания на C, переносимость.


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

украдено из:https://stackoverflow.com/a/35326444/638848

вы также можете реализовать оболочку для 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 и бесплатные звонки. Все другие операции указателя не требуют рефакторинга.


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


нет, нет.


Если вы используете 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, чтобы точно сказать вам размер выделенного блока. Чтобы убедиться в этом, представьте, что вы пишете--21-->большой приложение, с несколькими различными распределителями памяти-возможно, вы используете raw libc malloc в одной части, но c++ operator new в другой части, а затем некоторые конкретные окна API в еще одной части. Итак, у вас есть все виды void* летают. Написание функции, которая может работать на любой из этих void*s невозможно, если вы не можете каким-то образом определить по значению указателя, из какой из ваших куч он пришел.

таким образом, вы можете обернуть каждый указатель в своей программе некоторым соглашением, которое указывает, откуда пришел указатель (и куда его нужно вернуть). Например, в C++ мы называем это std::unique_ptr<void> (для указателей, что нужно быть operator delete ' d) или std::unique_ptr<void, D> (для указателей, которые должны быть возвращены через некоторый другой механизм D). Вы могли бы сделать то же самое в C, если вы хотели. И как только вы заворачиваете указатели в более безопасные объекты в любом случае, это всего лишь маленький шаг к 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 - не из пользовательской кучи и не указывая на середину object - вы можете использовать следующие специфические для ОС 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);
}

проверено на:

  • Visual Studio, Win64 -_msize
  • GCC / Clang, glibc, Linux -malloc_usable_size
  • Clang, libc, Mac OS X - malloc_size
  • Clang, jemalloc, Mac OS X-работает на практике, но я бы не доверял ему (молча смешивает jemalloc malloc и родной libc malloc_size)
  • должно отлично работать с jemalloc на Linux
  • должно отлично работать с dlmalloc на Linux если составлен без USE_DL_PREFIX
  • должно отлично работать с tcmalloc везде

int *a; a=malloc(n*sizeof(int)); если malloc возвращает NULL, это означает, что вы не получили память, иначе вы получите базовый адрес назначенного блока, что означает (n*sizeof(int)) размер блока.


Я не уверен, но попробуйте:

char **q = &p;
int size = q[1] - q[0];