Domanda

C'è un modo in C per scoprire la dimensione di memoria allocata in modo dinamico?

Per esempio, dopo

char* p = malloc (100);

C'è un modo per scoprire la dimensione della memoria associata con p?

È stato utile?

Soluzione

comp.lang.c FAQ lista · Domanda 7,27 -

Q. Così posso interrogare il pacchetto malloc per scoprire quanto è grande un blocco allocato è?

A. Purtroppo, non esiste un modo standard o portatile. (Alcuni compilatori fornire estensioni non standard). Se avete bisogno di sapere, si dovrà tenere traccia di voi stessi. (Vedi anche la domanda 7.28 .)

Altri suggerimenti

Non c'è un modo standard per trovare queste informazioni. Tuttavia, alcune implementazioni forniscono funzioni come msize per fare questo. Ad esempio:

Tenete a mente, però, che malloc assegnerà un minimo di dimensioni richieste, quindi si dovrebbe verificare se msize variante per l'implementazione in realtà restituisce la dimensione dell'oggetto o la memoria allocata in realtà sul mucchio.

La mentalità C è quello di fornire al programmatore strumenti per aiutare lui con il suo lavoro, non per fornire astrazioni che cambiano la natura del suo lavoro. C cerca anche di evitare di fare le cose più semplici / più sicuro se questo avviene a spese del limite di prestazioni.

Certe cose come si potrebbe fare con una regione di memoria, richiedono solo la posizione di inizio della regione. Tali soluzioni sono di lavorare con le stringhe null-terminate, manipolando i primi n byte della regione (se la regione si caratterizza per essere almeno questo grande), e così via.

In sostanza, tenendo traccia della lunghezza di una regione è lavoro extra, e se C ha fatto automaticamente, sarebbe a volte essere farlo inutilmente.

Molte funzioni di libreria (ad esempio fread()) richiedono un puntatore all'inizio di una regione, e anche la dimensione di questa regione. Se avete bisogno della dimensione di una regione, è necessario tenere traccia di esso.

Sì, malloc () implementazioni di solito tenere traccia delle dimensioni di una regione, ma possono farlo indirettamente, o intorno ad esso fino a un certo valore, o non tenere affatto. Anche se sostengono che, trovando la dimensione in questo modo potrebbe essere lenta rispetto al tenere traccia di voi stessi.

Se avete bisogno di una struttura di dati che sa quanto è grande ogni regione è, C può farlo per voi. Basta usare una struttura che tiene traccia di quanto sia grande la regione è così come un puntatore alla regione.

No, la libreria di runtime C non fornisce una tale funzione.

Alcune biblioteche possono fornire funzioni dalla piattaforma o specifiche del compilatore che possono ottenere queste informazioni, ma in generale il modo per tenere traccia di queste informazioni è in un'altra variabile intera.

Qui è il modo migliore che ho visto a creare un tag puntatore per memorizzare le dimensioni con l'indirizzo.Tutte le funzioni di puntatore sarebbe ancora funzionare come previsto:

Rubato da: https://stackoverflow.com/a/35326444/638848

Si potrebbe anche implementare un wrapper per la malloc e free per aggiungere tag (come dimensione allocata e altri meta-informazioni) prima che il puntatore restituito da malloc.In realtà questo è il metodo che un compilatore c++ tags oggetti con riferimenti a classi virtuali.Qui è un posto di lavoro esempio:

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

Il vantaggio di questo metodo su una struttura con dimensioni e puntatore

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

è che avete solo bisogno di sostituire la malloc e chiamate gratuite.Tutti altre operazioni sui puntatori che non richiedono per il refactoring.

Come tutti gli altri già detto: No, non c'è

.

Inoltre, vorrei sempre evitare tutte le funzioni specifiche del fornitore qui, perché quando si scopre che si ha realmente bisogno di usarli, che è generalmente un segno che si sta facendo male. Si dovrebbe o memorizzare le dimensioni a parte, o non è necessario conoscere a tutti. Utilizzando le funzioni vendor è il modo più rapido per perdere uno dei principali vantaggi di scrivere in C, la portabilità.

Mi aspetto che questo sia carico di attuazione.
Se hai la struttura dei dati di intestazione, potresti lanciare di nuovo sul puntatore e ottenere la dimensione.

Se si utilizza malloc, allora non si può ottenere la dimensione.
In altra parte, se si utilizza OS API per allocare dinamicamente la memoria, come Windows funzioni di heap , allora è possibile farlo.

Questo codice probabilmente funzionerà sulla maggior parte delle installazioni di 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);
}

Bene, ora so che questo non risponde alla tua domanda specifica, tuttavia pensare fuori della scatola per così dire ... Mi viene in mente che probabilmente non c'è bisogno di sapere. Ok, ok, no non voglio dire tu abbia un'implementazione cattivo o non-ortodossa ... voglio dire è che probabilmente (senza guardare il codice sto solo indovinando) si prbably solo vuole sapere se i dati possono andare bene nella memoria allocata, se questo è il caso, allora questa soluzione potrebbe essere meglio. Non dovrebbe offrire troppa testa e risolverà il tuo problema "fitting" se questo è davvero quello che si sta gestendo:

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

o se è necessario mantenere i vecchi contenuti:

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

Naturalmente si potrebbe utilizzare:

p = realloc(p, required_size);

e da fare con esso.

Tutti ti dice che è impossibile è tecnicamente corretta (il miglior tipo di corretta).

Per ragioni di ingegneria, è una cattiva idea di fare affidamento sul sottosistema malloc a dire la dimensione di un blocco allocato in modo accurato. Per convincersi di questo, immaginate che si stesse scrivendo un di grandi dimensioni di applicazione, con diversi ripartitori differenti di memoria - forse si usa malloc libc prima in una parte, ma operator new C ++ in un'altra parte, e poi alcuni specifici di Windows API in un'altra parte. Così hai tutti i tipi di void* volano intorno. Scrivere una funzione che può lavorare su qualsiasi di questi void*s impossibili, a meno che non si può in qualche modo dire dal valore del puntatore quale dei tuoi cumuli è venuto.

Così si potrebbe desiderare di avvolgere ogni puntatore nel vostro programma con una certa convenzione che indica dove il puntatore è venuto da (e dove deve essere restituito a). Ad esempio, in C ++ che noi chiamiamo std::unique_ptr<void> (per i puntatori che devono essere operator delete'd) o std::unique_ptr<void, D> (per i puntatori che devono essere restituiti attraverso qualche altro meccanismo di D). Si potrebbe fare lo stesso tipo di cosa in C se si voleva. E una volta che si sta avvolgendo puntatori a oggetti più grandi più sicuri in ogni caso , è solo un piccolo passo per struct SizedPtr { void *ptr; size_t size; } e poi non hai mai bisogno di preoccuparsi per le dimensioni di uno stanziamento di nuovo.

Comunque.

Ci sono anche buone ragioni per cui si potrebbe legittimamente desiderare di conoscere la reale dimensione di fondo di uno stanziamento. Per esempio, forse si sta scrivendo uno strumento di profiling per la tua app che riporterà la quantità effettiva di memoria utilizzato di ciascun sottosistema, non solo la quantità di memoria che il programmatore pensava che stava usando. Se ognuno dei tuoi allocazioni 10-byte è segretamente utilizzando 16 byte sotto il cofano, che è bene sapere! (Naturalmente ci saranno altre spese generali pure, che non si sta misurando in questo modo. Ma ci sono ancora altri strumenti per che di lavoro.) O forse sei solo indagare il comportamento dei realloc su la vostra piattaforma. O forse si desidera "arrotondare" la capacità di una dotazione crescente di evitare prematuri riassegnazioni in futuro. Esempio:

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

Per ottenere la dimensione della dotazione dietro un puntatore non nullo , che è stato restituito direttamente da libc malloc - non da un mucchio personalizzato, e non rivolto verso la metà di un oggetto - si può utilizzare le seguenti API specifiche del sistema operativo, che ho impacchettati in una funzione wrapper "portable-ish" per convenienza. Se si trova un sistema comune in cui questo codice non funziona, si prega di lasciare un commento e cercherò di risolvere il problema!

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

Testato su:

Questo può funzionare, un piccolo aggiornamento nel codice:

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

Ma questo si tradurrà 1, cioè, memoria associata p se è char*. Se è int* poi risultato sarà 4.

Non c'è modo di scoprire dotazione totale.

Quuxplusone ha scritto: "Scrivere una funzione che può lavorare su uno di questi void * s impossibile, a meno che non si può in qualche modo dire dal valore del puntatore quale dei tuoi cumuli è venuto." Determinare dimensione di memoria assegnata dinamicamente in C "

In realtà in Windows _msize ti dà la dimensione della memoria allocata dal valore del puntatore. Se non v'è memoria allocata presso l'indirizzo viene generato un errore.

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

Grazie per la raccolta #define. Ecco la versione macro.

#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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top