2015-11-14 15 views
24

Da quanto scritto here, new assegna in negozio libera mentre malloc usa mucchio ei due termini spesso significano la stessa cosa.È sicuro allocare la memoria allocata con nuova?

Da quanto scritto here, realloc è possibile spostare il blocco di memoria in una nuova posizione. Se la memoria e l'heap liberi sono due spazi di memoria diversi, vuol dire quindi qualche problema?

In particolare mi piacerebbe sapere se è sicuro da usare

int* data = new int[3]; 
// ... 
int* mydata = (int*)realloc(data,6*sizeof(int)); 

In caso contrario, c'è qualche altro modo per realloc memoria allocata con new in modo sicuro? Potrei assegnare nuova area e memcpy il contenuto, ma da quello che capisco realloc potrebbe utilizzare la stessa area, se possibile.

+13

Basta usare un 'vector'. –

+8

@KarolyHorvath Come fai a sapere che è un approccio praticabile in ogni caso? Che dire della distribuzione di un sistema embedded senza il supporto della libreria standard? Che dire dell'integrazione con un'interfaccia C che può eseguire un realloc? –

+6

@KarolyHorvath siete invitati a valutare quanto tempo è necessario per allocare 200 MB di memoria con 'malloc' (pochi microsecondi) rispetto a' std :: vector' (~ 200 millisecondi!). 'std :: vector' non è una soluzione magica per ogni problema di memoria –

risposta

39

È possibile solo realloc quello che è stato assegnato tramite malloc (o famiglia, come calloc).

Questo perché le strutture di dati sottostanti che tengono traccia delle aree di memoria libere e utilizzate possono essere molto diverse.

È probabile ma in nessun modo garantito che C++ new e C malloc utilizzano lo stesso allocatore sottostante, nel qual caso realloc potrebbe funzionare per entrambi. Ma formalmente è in terra UB. E in pratica è solo inutilmente rischioso.


C++ non offre funzionalità corrispondenti a realloc.

Il più vicino è la riassegnazione automatica di (i buffer interni di) contenitori come std::vector.

I contenitori C++ sono progettati in modo da escludere l'uso di realloc.


Invece del codice presentato

int* data = new int[3]; 
//... 
int* mydata = (int*)realloc(data,6*sizeof(int)); 

& hellip; fare questo:

vector<int> data(3); 
//... 
data.resize(6); 

Tuttavia, se è assolutamente necessario l'efficienza generale del realloc, e se si deve accettare new per l'assegnazione originale, quindi l'unica soluzione per l'efficienza è quello di utilizzare mezzi specifici del compilatore, sapendo che realloc è sicuro con questo compilatore.

In caso contrario, se è assolutamente necessario l'efficienza generale del realloc ma non è costretto ad accettare new, quindi è possibile utilizzare malloc e realloc. L'utilizzo di puntatori intelligenti consente di ottenere gran parte della stessa sicurezza dei contenitori C++.

+1

il snippet che hai scritto è il modo più idiomatico per riallocare la memoria in C++, ma è un modo sicuro per uccidere la tua performance, se sei in questo campo. –

+2

@KyleStrand: se si deve accettare 'new' per l'allocazione originale, allora l'unica risorsa per l'efficienza è usare mezzi specifici del compilatore. Per esempio. conoscenza che 'realloc' è sicuro con questo compilatore. Altrimenti, puoi usare i puntatori intelligenti con 'malloc' e' realloc'. Ad ogni modo, ricorda la prima (e la seconda) regola di ottimizzazione, ovvero ** MISURA **. –

+0

Grazie. Ho eliminato il mio commento perché la tua modifica ha risposto alla mia domanda. –

5

Non è sicuro e non è elegante.

Potrebbe essere possibile eseguire l'override di new/delete per supportare la riallocazione, ma si potrebbe anche considerare di utilizzare i contenitori.

+4

Non sono sicuro di cosa sia inelegante su realloc. –

+0

utilizzando new/delete con realloc, per override, o altri mezzi per farlo funzionare, non è elegante, si prega di leggere l'argomento. –

+0

Quindi intendi che * perché * non è sicuro, è inelegante provare a * renderlo sicuro? Questo non è chiaro dalla tua risposta. E non dare per scontato che in qualche modo sia riuscito a lasciare un commento sulla tua risposta senza "leggere l'argomento"; è inutile insultare. –

5

Sì - se new in realtà si chiama malloc in primo luogo (ad esempio, questo è il modo in cui VC++ new funziona).

Altrimenti. si noti che una volta deciso di riallocare la memoria (perché new chiamato malloc), il proprio codice è specifico del compilatore e non più portatile tra i compilatori.

(So che questa risposta potrebbe turbare molti sviluppatori, ma la risposta dipende da fatti reali, non solo da idiomi).

+0

È vero per 'operator new []()', che è ciò che viene usato qui, piuttosto che plain 'operator new()'? –

+0

su VC++ tutti gli operatori 'new' standard chiamano' malloc' alla fine. –

+0

Sì, ma sarei sorpreso se il risultato di 'operator new []' fosse uguale al valore restituito da una chiamata a 'malloc', a causa della memorizzazione del conteggio. E se non lo è, non puoi passarlo a 'realloc'. –

7

In generale, non farlo. Se si utilizzano tipi definiti dall'utente con l'inizializzazione non banale , in caso di riallocazione della copia di riassegnazione, il distruttore degli oggetti non verrà chiamato entro il realloc. La copia del costruttore non verrà chiamata anche durante la copia. Ciò può comportare un comportamento indefinito a causa di un uso non corretto della durata dell'oggetto (vedere Standard C++ §3.8 Durata dell'oggetto, [vita base]).

1 La durata di un oggetto è una proprietà di runtime dell'oggetto. Si dice che un oggetto abbia un'inizializzazione non banale se è di classe o di tipo aggregato e questo o uno dei suoi membri è inizializzato da un costruttore diverso da un semplice costruttore di default. [Nota: l'inizializzazione da parte di un semplice costruttore di copia/spostamento è un'inizializzazione non banale. -end nota]

La durata di un oggetto di tipo T inizia quando:

- ottenuto stoccaggio con il corretto allineamento e la dimensione per tipo T, e

- se l'oggetto è non banale inizializzazione, la sua inizializzazione è completa.

La durata di un oggetto di tipo T termina quando:

- se T è un tipo di classe con un distruttore non banale (12.4), inizia la chiamata distruttore, o

- lo stoccaggio che l'oggetto occupa viene riutilizzato o rilasciato.

E più tardi (sottolineatura mia):

3 Le proprietà attribuite agli oggetti in tutta questa norma internazionale applicare per un dato oggetto solo durante la sua vita.

Quindi, davvero non si vuole usare un oggetto fuori della sua vita.

4

Questo non è sicuro. Innanzitutto il puntatore che si passa a realloc deve essere stato ottenuto da malloc o realloc: http://en.cppreference.com/w/cpp/memory/c/realloc.

In secondo luogo, il risultato di new int [3] non deve essere uguale al risultato della funzione di allocazione: è possibile allocare spazio aggiuntivo per memorizzare il conteggio degli elementi.

(e per più tipi complessi rispetto int, realloc non sarebbe sicuro dal momento che non chiama copiare o spostare i costruttori.)

3

si può essere in grado di (non in tutti i casi), ma non dovreste 't. Se hai bisogno di ridimensionare la tua tabella di dati, dovresti usare invece std::vector.

Dettagli su come usarlo sono elencati in un altro SO question.

12

L'unica eventualmente corrispondente restrizione C++ aggiunge realloc è che C++ s 'malloc/calloc/realloc non deve essere attuata in termini di ::operator new, e la sua free non deve essere realizzata in termini di ::operator delete (per C++ 14 [c. malloc] pagine 3-4).

Ciò significa che la garanzia che stai cercando non esiste in C++. Significa anche che è possibile implementare ::operator new in termini di malloc. E se lo fai, in teoria, il risultato di ::operator new può essere passato a realloc.

In pratica, si dovrebbe essere preoccupati per la possibilità che il risultato di new non corrisponda al risultato di ::operator new. I compilatori C++ possono ad es. combinare più espressioni new per utilizzare una singola chiamata ::operator new. Questo è qualcosa che i compilatori hanno già fatto quando lo standard non lo consentiva, IIRC, e lo standard ora lo consente (per C++ 14 [expr.new] p10). Ciò significa che anche se segui questa strada, non hai ancora la garanzia che passare i puntatori new a realloc non abbia alcun significato, anche se non è più un comportamento non definito.

+1

Si prega di aggiungere riferimenti per (1)" malloc/calloc/C++/realloc non deve essere implementato in termini di :: operator new ", e per (2), sulla pratica non ancora approvata dallo standard, che" i compilatori C++ possono ad esempio combinare più nuove espressioni per utilizzare una singola chiamata :: operator new ". –

+0

@ Cheersandhth.-Alf Aggiunto un riferimento per il primo. Non ho incluso il testo standard attuale perché questa non è una domanda [avvocato linguistico]. Non ho un esempio pronto per più chiamate 'new' che forniscono i risultati che sto descrivendo, e un esempio semplice e rapido che elimina la memoria allocata non combina le allocazioni in una sola, ma semplicemente ottimizza le allocazioni interamente. – hvd

0

Questi funzione è usato soprattutto in C.

memset imposta i byte in un blocco di memoria a un valore specifico.

malloc alloca un blocco di memoria.

calloc, come malloc. L'unica differenza è che inizializza i byte a zero.

In C++ il metodo preferito per allocare la memoria è quello di utilizzare nuovo.

C: int intray = (int *) malloc (10 * sizeof (int)); C++: int intray = new int [10];

C: int intray = (int *) calloc (10 * sizeof (int)); C++: int intArray = new int10;

+2

Non credo che questo risponda alla domanda, perché non affronta affatto la riallocazione. –

3

In generale, n.

ci sono una serie di cose che devono essere in possesso per renderlo sicuro:

  1. Bitwise copia il tipo e abbandonando la fonte deve essere sicuro.
  2. Il distruttore deve essere banale o è necessario localizzare gli elementi che si desidera deallocare.
  3. O il costruttore è banale, oppure è necessario costruire sul posto i nuovi elementi.

I tipi di trivial soddisfano i requisiti di cui sopra.

Inoltre:

  1. Il new[]-funzione deve passare la richiesta al malloc senza alcun cambiamento, né alcuna contabilità sul lato. Puoi forzarlo sostituendo global new [] e delete [], o quelli nelle rispettive classi.
  2. Il compilatore non deve chiedere più memoria per salvare il numero di elementi assegnati o qualsiasi altra cosa.
    Non esiste alcun modo per forzare ciò, sebbene un compilatore non debba salvare tali informazioni se il tipo ha un distruttore banale come una questione di Quality of Implementation.