Как я могу получить размер массива из указателя в C?


я выделил "массив" от mystruct в размере n такой:

if (NULL == (p = calloc(sizeof(struct mystruct) * n,1))) {
 /* handle error */
}

позже, у меня есть только доступ к p и n. Есть ли способ определить длину массива, заданного только указателем p?

Я понимаю должны возможно, так как free(p) делает именно это. Я знаю malloc() отслеживает, сколько памяти он выделил, и именно поэтому он знает длину; возможно, есть способ запросить эта информация? Что-то вроде этого...

int length = askMallocLibraryHowMuchMemoryWasAlloced(p) / sizeof(mystruct)

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

13 65

13 ответов:

нет, нет никакого способа получить эту информацию без сильно зависит от деталей реализации malloc. В частности, malloc может выделять больше байтов, чем вы просите (например, для эффективности в конкретной архитектуре памяти). Было бы намного лучше перепроектировать ваш код так, чтобы вы отслеживали n явно. Альтернативой является по крайней мере столько же редизайн и гораздо более опасный подход (учитывая, что это нестандартно, злоупотребляет семантикой указатели, и будет кошмар обслуживания для тех, кто придет после вас): хранить длинуn по адресу malloc'D, за которым следует массив. Распределение тогда будет:

void *p = calloc(sizeof(struct mystruct) * n + sizeof(unsigned long int),1));
*((unsigned long int*)p) = n;

n теперь хранятся в *((unsigned long int*)p) и начало вашего массива теперь

void *arr = p+sizeof(unsigned long int);

Edit: просто играть адвоката дьявола... Я знаю, что все эти "решения" требуют перепроектирования, но давайте разыграем его. Конечно, решение, представленное выше, просто хаки реализация (хорошо упакованной) структуры. Вы могли бы также определить:

typedef struct { 
  unsigned int n;
  void *arr;
} arrInfo;

и пройти вокруг arrInfoС, а не сырые указатели.

теперь мы готовим. Но пока вы перепроектируете, зачем останавливаться здесь? Что вы действительно хотите-это абстрактный тип данных (АТД). Любой вводный текст для алгоритмов и структур данных будет делать это. ADT определяет открытый интерфейс типа данных, но скрывает реализацию этого типа данных. Таким образом, публично ADT для an массив может выглядеть как

typedef void* arrayInfo;
(arrayInfo)newArrayInfo(unsignd int n, unsigned int itemSize);
(void)deleteArrayInfo(arrayInfo);
(unsigned int)arrayLength(arrayInfo);
(void*)arrayPtr(arrayInfo);
...
std::vector.

там мы взяли простой вопрос о C и оказались в C++. Да поможет нам всем Бог.

следите за размером массива самостоятельно; free использует цепочку malloc, чтобы освободить блок который был выделен, который не обязательно имеет тот же размер, что и массив, который вы запросили

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

что, если это сработает?

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

typedef struct MyStructTag
{ /* etc. */ } MyStruct ;

void doSomething(MyStruct * p)
{
   /* well... extract the memory allocated? */
   size_t i = get_size(p) ;
   initializeMyStructArray(p, i) ;
}

void doSomethingElse()
{
   MyStruct * s = malloc(sizeof(MyStruct) * 10) ; /* Allocate 10 items */
   doSomething(s) ;
}

Почему даже если это сработает, это все равно не сработает?

но проблема этого подхода заключается в том, что в C, вы можете играть с указатель арифметика. Давайте перепишем doSomethingElse():

void doSomethingElse()
{
   MyStruct * s = malloc(sizeof(MyStruct) * 10) ; /* Allocate 10 items */
   MyStruct * s2 = s + 5 ; /* s2 points to the 5th item */
   doSomething(s2) ; /* Oops */
}

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

вывод

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

некоторые компиляторы предоставляют msize () или аналогичные функции (_msize () и т. д.), которые позволяют вам делать именно это

могу ли я порекомендовать ужасный способ сделать это?

выделите все массивы следующим образом:

void *blockOfMem = malloc(sizeof(mystruct)*n + sizeof(int));

((int *)blockofMem)[0] = n;
mystruct *structs = (mystruct *)(((int *)blockOfMem) + 1);

тогда вы всегда можете привести свои массивы к int * и к -1-й элемент.

обязательно free это указатель, а не сам указатель массива!

кроме того, это, вероятно, вызовет ужасные ошибки, которые оставят вас рвать волосы. Может быть, вы можете обернуть функции alloc в вызовы API или что-то в этом роде.

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

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


int size;
struct mystruct *cur;

for (cur = myarray; cur->name != NULL; cur++)
    ;

size = cur - myarray;

кстати это должно быть calloc (n, sizeof(struct mystruct)) в вашем образец.

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

если вы должны есть такое поведение, вы можете использовать или написать специализированный распределитель памяти. Это самое простое, что нужно сделать, это реализовать оболочку вокруг stdlib.h функции. Что-то вроде:

void* my_malloc(size_t s);     /* Calls malloc(s), and if successful stores 
                                  (p,s) in a list of handled blocks */
void my_free(void* p);         /* Removes list entry and calls free(p) */
size_t my_block_size(void* p); /* Looks up p, and returns the stored size */
...

на самом деле ваш вопрос - "я могу узнать размер Танос бы (или calloc б) блок данных". И как говорили другие: нет, не стандартным способом.

однако есть пользовательские реализации malloc, которые делают это - например http://dmalloc.com/

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

Почему вы не можете сохранить размер выделенной памяти?

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

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

Это тест моего рода рутину. Он устанавливает 7 переменных для хранения значений с плавающей запятой, а затем присваивает их массиву, который используется для поиска максимального значения.

магия заключается в вызове myMax:

поплавок ммах = myMax((типа float *)&Арр(инт) оператор sizeof(Арр)/размер(модуль arr[0]));

на uClibc, есть MALLOC_SIZE макрос malloc.h:

/* The size of a malloc allocation is stored in a size_t word
   MALLOC_HEADER_SIZE bytes prior to the start address of the allocation:

     +--------+---------+-------------------+
     | SIZE   |(unused) | allocation  ...   |
     +--------+---------+-------------------+
     ^ BASE             ^ ADDR
     ^ ADDR - MALLOC_HEADER_SIZE
*/

/* The amount of extra space used by the malloc header.  */
#define MALLOC_HEADER_SIZE          \
  (MALLOC_ALIGNMENT < sizeof (size_t)       \
   ? sizeof (size_t)                \
   : MALLOC_ALIGNMENT)

/* Set up the malloc header, and return the user address of a malloc block. */
#define MALLOC_SETUP(base, size)  \
  (MALLOC_SET_SIZE (base, size), (void *)((char *)base + MALLOC_HEADER_SIZE))
/* Set the size of a malloc allocation, given the base address.  */
#define MALLOC_SET_SIZE(base, size) (*(size_t *)(base) = (size))

/* Return base-address of a malloc allocation, given the user address.  */
#define MALLOC_BASE(addr)   ((void *)((char *)addr - MALLOC_HEADER_SIZE))
/* Return the size of a malloc allocation, given the user address. */
#define MALLOC_SIZE(addr)   (*(size_t *)MALLOC_BASE(addr))