Określ rozmiar dynamicznie przydzielanej pamięci w C

Czy jest sposób w C, aby dowiedzieć się, rozmiar dynamicznie przydzielanej pamięci?

Na przykład po

char* p = malloc (100);

Czy istnieje sposób, aby dowiedzieć się o wielkości pamięci związanej z p?

Author: Community, 2009-08-15

15 answers

Komp.lang.c lista FAQ * pytanie 7.27 -

Q. Czy mogę więc odpytywać pakiet malloc, aby dowiedzieć się, jak duży jest alokowany blok?

A. niestety, nie ma standardowego lub przenośnego sposobu. (Niektóre Kompilatory zapewniają niestandardowe rozszerzenia.) Jeśli musisz wiedzieć, będziesz musiał śledzić to sam. (Zobacz też pytanie 7.28.)

 39
Author: Alex Reynolds,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-12 21:05:22

Nie ma standardowego sposobu, aby znaleźć te informacje. Jednak niektóre implementacje dostarczają do tego celu funkcje takie jak msize. Na przykład:

Pamiętaj jednak, że malloc przydzieli minimum żądanego rozmiaru, więc powinieneś sprawdzić, czy wariant msize dla Twojej implementacji rzeczywiście zwraca rozmiar obiektu lub pamięć faktycznie przydzielona na stercie.

 39
Author: ars,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-16 07:51:12

Mentalność C polega na dostarczaniu programistom narzędzi, które pomogą mu w jego pracy, a nie na dostarczaniu abstrakcji, które zmieniają charakter jego pracy. C stara się również uniknąć ułatwiania/bezpieczniejszego działania, jeśli dzieje się to kosztem limitu wydajności.

Niektóre rzeczy, które chcesz zrobić z regionem pamięci, wymagają tylko Lokalizacji początku regionu. Takie rzeczy obejmują pracę z łańcuchami zakończonymi znakiem null, manipulowanie pierwszymi N bajtami regionu (jeśli region jest znany, że jest co najmniej tak duży), i tak dalej.

Zasadniczo śledzenie długości regionu jest dodatkową pracą, a gdyby C zrobił to automatycznie, czasami robi to niepotrzebnie.

Wiele funkcji bibliotecznych (na przykład fread()) wymaga wskaźnika do początku regionu, a także rozmiaru tego regionu. Jeśli potrzebujesz rozmiaru regionu, musisz go śledzić.

Tak, implementacje malloc () Zwykle śledzą rozmiar regionu, ale mogą to robić pośrednio lub zaokrąglać do pewnej wartości, lub w ogóle jej nie utrzymywać. Nawet jeśli go wspierają, znalezienie rozmiaru w ten sposób może być powolne w porównaniu z samodzielnym śledzeniem go.

Jeśli potrzebujesz struktury danych, która wie, jak duży jest Każdy region, C może to zrobić za Ciebie. Wystarczy użyć struktury, która śledzi, jak duży jest region, a także wskaźnika do regionu.

 8
Author: Artelius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-15 12:46:02

Nie, biblioteka uruchomieniowa C nie udostępnia takiej funkcji.

Niektóre biblioteki mogą dostarczać specyficzne dla platformy lub kompilatora funkcje, które mogą pobierać te informacje, ale generalnie sposób śledzenia tych informacji jest w innej zmiennej całkowitej.

 6
Author: Greg Hewgill,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-15 11:25:52

Jak wszyscy już mówili: nie ma.

Ponadto, zawsze unikałbym wszystkich funkcji specyficznych dla dostawcy, ponieważ kiedy okaże się, że naprawdę musisz z nich korzystać, jest to zazwyczaj znak, że robisz to źle. Należy albo przechowywać rozmiar oddzielnie, lub nie trzeba go znać w ogóle. Korzystanie z funkcji Dostawcy jest najszybszym sposobem na utratę jednej z głównych zalet pisania w języku C, przenośności.

 3
Author: Enno,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-27 21:58:08

Oto najlepszy sposób, jaki widziałem, aby utworzyć oznaczony wskaźnik do przechowywania rozmiaru z adresem. Wszystkie funkcje wskaźnika nadal będą działać zgodnie z oczekiwaniami:

Skradzione z: https://stackoverflow.com/a/35326444/638848

Możesz również zaimplementować wrapper dla malloc i za darmo dodawać tagi (jak przypisany rozmiar i inne meta informacje) przed wskaźnikiem zwrócony przez malloca. Jest to w rzeczywistości metoda, którą kompilator c++ tagi obiekty z odniesieniami do wirtualnych klasy. Oto jeden działający przykład:

#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;
}

Przewaga tej metody nad strukturą o rozmiarze i wskaźniku

 struct pointer
 {
   size_t size;
   void *p;
 };

Jest to, że wystarczy wymienić malloc i darmowe rozmowy. Wszystkie inne operacje pointera nie wymagają refaktoryzacji.

 3
Author: Leslie Godwin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:17

Spodziewałbym się, że będzie to zależne od implementacji.
Jeśli masz strukturę danych nagłówka, możesz rzucić ją z powrotem na wskaźnik i uzyskać rozmiar.

 2
Author: nik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-15 11:43:08

Nie, Nie Ma.

 1
Author: Electro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-15 11:26:22

Jeśli używasz malloc, nie możesz uzyskać rozmiaru.

Z drugiej strony, jeśli używasz OS API do dynamicznego przydzielania pamięci, tak jak Windows heap functions , to jest to możliwe.

 1
Author: Nick Dandoulakis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-15 11:36:53

Ten kod prawdopodobnie będzie działał na większości instalacji 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);
}
 1
Author: H. Acker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-31 14:50:12

To może zadziałać, mała aktualizacja w kodzie:

void* inc = (void*) (++p)
size=p-inc;

Ale spowoduje to 1, czyli pamięć skojarzoną z p, jeśli jest char*. Jeśli jest int*, to wynik będzie 4.

Nie ma sposobu, aby dowiedzieć się całkowitej alokacji.

 1
Author: sarin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-13 18:06:03

Teraz wiem, że to nie jest odpowiedź na twoje konkretne pytanie, ale myślenie nieszablonowe... Wydaje mi się, że prawdopodobnie nie musisz wiedzieć. Ok, ok, nie mam na myśli, że masz złą lub nie-ortodoksyjną implementację... Chodzi mi o to, że prawdopodobnie (bez patrzenia na kod, tylko zgaduję) chcesz tylko wiedzieć, czy Twoje dane mogą zmieścić się w przydzielonej pamięci, jeśli tak jest, to To rozwiązanie może być lepsze. Nie powinna oferować zbyt dużych kosztów i rozwiąże Twój" pasujący " problem, jeśli to jest rzeczywiście to, co masz do czynienia: {]}

if ( p != (tmp = realloc(p, required_size)) ) p = tmp;

Lub jeśli trzeba zachować starą zawartość:

if ( p != (tmp = realloc(p, required_size)) ) memcpy(tmp, p = tmp, required_size);

Oczywiście możesz po prostu użyć:

p = realloc(p, required_size);
I skończ z tym.
 1
Author: Erich Horn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-12 03:13:49

Każdy, kto mówi ci, że to niemożliwe, jest technicznie poprawny (najlepszy rodzaj poprawności).

Ze względów inżynierskich, nie jest dobrym pomysłem poleganie na podsystemie malloc, aby dokładnie określić rozmiar przydzielonego bloku. Aby się o tym przekonać, wyobraź sobie, że piszesz dużą aplikację, z kilkoma różnymi alokatorami pamięci - może używasz surowego libc malloc w jednej części, ale C++ operator new w innej, a następnie jakiegoś specyficznego Windows API w jeszcze innej część. Więc masz wiele rodzajów void* latających wokół. Napisanie funkcji, która może działać na dowolnych z tych void*S jest niemożliwe, chyba że możesz w jakiś sposób stwierdzić na podstawie wartości wskaźnika, z której z Twoich stosów pochodzi.

Więc możesz chcieć zawinąć każdy wskaźnik w swoim programie z pewną konwencją, która wskazuje, skąd pochodzi wskaźnik(i gdzie musi być zwrócony). Na przykład w C++ nazywamy to std::unique_ptr<void> (dla wskaźników, które muszą być operator delete'd) lub std::unique_ptr<void, D> (dla wskaźników które muszą zostać zwrócone przez inny mechanizm D). Możesz zrobić to samo w C, jeśli chcesz. A kiedy już pakujesz wskaźniki w większe, bezpieczniejsze obiekty , jest to tylko mały krok do struct SizedPtr { void *ptr; size_t size; } i już nigdy nie musisz martwić się o rozmiar przydziału.

Jednak.

Istnieją również dobre powody, dla których możesz legalnie chcieć znać rzeczywistą wielkość bazową przydziału. Na przykład, może piszesz narzędzie do profilowania dla swojej aplikacji, które będzie raportować rzeczywistą ilość pamięci użytej przez każdy podsystem, a nie tylko ilość pamięci, której programista myślał, której używał. Jeśli każdy z Twoich 10-bajtowych przydziałów potajemnie używa 16 bajtów pod maską, to dobrze wiedzieć! (Oczywiście będą też inne koszty, których nie mierzysz w ten sposób. Ale są jeszcze inne narzędzia do tego zadania.) A może po prostu badasz zachowanie realloc na twojej platformie. A może chciałbyś "zaokrąglić" potencjał rosnącej alokacji, aby uniknąć przedwczesnych realokacji w przyszłości. Przykład:

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;
}

Aby uzyskać rozmiar alokacji za innym niż null wskaźnikiem , który został zwrócony bezpośrednio z libc malloc - nie z niestandardowego stosu i nie wskazując na środek obiektu - możesz użyć następujących API specyficznych dla systemu operacyjnego, które połączyłem w "przenośny" pakiet funkcja dla wygody. Jeśli znajdziesz wspólny system, w którym ten kod nie działa, zostaw komentarz, a ja spróbuję go naprawić!

#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);
}

Testowane na:

  • Visual Studio, Win64 - _msize
  • GCC/ clang, glibc, Linux - malloc_usable_size
  • Clang, libc, Mac OS X - malloc_size
  • Clang, jemalloc, Mac OS X - działa w praktyce, ale nie ufałbym mu (po cichu miesza jemalloc malloc i natywne libc malloc_size)
  • W tym celu należy skontaktować się z Działem obsługi klienta pod adresem .]}
  • powinien działać dobrze z dlmalloc na Linuksie jeśli skompilowany bez USE_DL_PREFIX
  • powinno działać dobrze z tcmalloc wszędzie
 0
Author: Quuxplusone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-04 20:29:57

int *a; a=malloc(n*sizeof(int)); jeśli malloc zwraca NULL, oznacza to, że nie otrzymałeś pamięci, w przeciwnym razie otrzymujesz adres bazowy przypisanego bloku, co oznacza rozmiar (n*sizeof(int)) bloku.

 -1
Author: kAmol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-10 09:14:04

Nie jestem pewien, ale spróbuj:

char **q = &p;
int size = q[1] - q[0]; 
 -4
Author: manish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-13 18:06:27