C ++ new int [0] - allocerà la memoria?


241

Una semplice app di prova:

cout << new int[0] << endl;

uscite:

0x876c0b8

Quindi sembra che funzioni. Cosa dice lo standard al riguardo? È sempre legale "allocare" un blocco di memoria vuoto?


4
+1 Domanda molto interessante - anche se non sono sicuro di quanto sia importante nel codice reale.
Zifre,

37
@Zifre: sto chiedendo curiosità, ma potrebbe interessare nel mondo reale, ad esempio quando la dimensione dei blocchi di memoria allocati viene calcolata in qualche modo e il risultato del calcolo potrebbe essere zero, quindi non è necessario aggiungere eccezioni per non allocare blocchi di dimensioni zero. Perché dovrebbero essere allocati ed eliminati senza errori (se non viene fatto solo il dereferenziamento del blocco di dimensioni zero). Quindi, in genere, ciò fornisce un'astrazione più ampia di ciò che è un blocco di memoria.

2
@ emg-2: nella tua situazione di esempio, in realtà non importa, perché delete [] è perfettamente legale su un puntatore NULL :-).
Evan Teran,

2
È solo tangenzialmente correlato - quindi sto commentando qui - ma C ++ in molti modi garantisce che oggetti distinti abbiano indirizzi univoci ... anche se non richiedono esplicitamente spazio di archiviazione. Un esperimento correlato sarebbe quello di verificare la dimensione di una struttura vuota. O una matrice di quella struttura.
Ha disegnato Dormann il

2
Per approfondire il commento di Shmoopty: Soprattutto durante la programmazione con modelli (ad esempio modelli di classi di politiche come std :: allocatore), è comune in C ++ avere oggetti di dimensioni zero. Potrebbe essere necessario che il codice generico assegni dinamicamente tali oggetti e utilizzi i puntatori per confrontare l'identità dell'oggetto. Questo è il motivo per cui l'operatore new () restituisce puntatori univoci per richieste di dimensioni zero. Sebbene probabilmente meno importante / comune, lo stesso ragionamento si applica all'allocazione di array e all'operatore new [] ().
Trevor Robinson,

Risposte:


234

Dal 5.3.4 / 7

Quando il valore dell'espressione in un nuovo dichiaratore diretto è zero, la funzione di allocazione viene chiamata per allocare un array senza elementi.

Dal 3.7.3.1/2

L'effetto del dereferenziamento di un puntatore restituito come richiesta di dimensione zero non è definito.

Anche

Anche se la dimensione dello spazio richiesto [da nuovo] è zero, la richiesta può fallire.

Ciò significa che puoi farlo, ma non puoi legalmente (in modo ben definito su tutte le piattaforme) dereferenziare la memoria che ottieni - puoi solo passarla all'array array - e dovresti eliminarla.

Ecco un'interessante nota (cioè non una parte normativa dello standard, ma inclusa a fini espositivi) allegata alla frase del 3.7.3.1/2

[32. L'intento è di avere un operatore new () implementabile chiamando malloc () o calloc (), quindi le regole sono sostanzialmente le stesse. C ++ differisce da C nel richiedere una richiesta zero per restituire un puntatore non nullo.]


Ottengo una perdita di memoria se non cancello. È previsto? Almeno non me lo aspettavo.
EralpB,

12
@EralpB: al contrario, anche se la tua richiesta è zero, questa allocazione avviene sull'heap, dove una richiesta implica diverse operazioni di tenuta dei libri, come l'assegnazione e l'inizializzazione delle protezioni heap prima e dopo la zona assegnata dall'allocatore, l'inserimento in freelist o altre strutture orribili complesse. Liberarlo significa fare la contabilità all'indietro.
v.oddou,

3
@EralpB sì, immagino che ci si possa aspettare una perdita di memoria ogni volta che non si bilancia a new[]con a delete[]- qualunque sia la dimensione. In particolare, quando chiami new[i]hai bisogno di un po 'più di memoria di quella che stai tentando di allocare per memorizzare le dimensioni dell'array (che verrà successivamente utilizzato delete[]durante la deallocazione)
pqnet

24

Sì, è legale allocare un array di dimensioni zero come questo. Ma devi anche cancellarlo.


1
Hai una citazione per questo? Sappiamo tutti che int ar[0];è illegale perché è nuovo OK?
Motti,

4
È interessante notare che C ++ non è così efficace nel proibire oggetti di dimensioni zero. Pensa all'ottimizzazione della classe base vuota: anche qui un oggetto secondario della classe base vuota può avere dimensione zero. Al contrario, lo standard C fa un grande sforzo per garantire che non vengano mai creati oggetti di dimensioni zero: nel definire malloc (0) si dice che l'effetto è di eseguire malloc con qualche argomento diverso da zero, per esempio. E in struct {...; T n []; }; quando non c'è spazio allocato per l'array (FAM), dice che si comporta come se "n" abbia un elemento. (In entrambi i casi, l'utilizzo dell'oggetto in qualsiasi modo è UB, come xn [0])
Johannes Schaub - litb

1
Penso che non sizeof (type)dovrebbe mai restituire zero. Si veda ad esempio: stackoverflow.com/questions/2632021/can-sizeof-return-0-zero
pqnet

Anche la cosiddetta "ottimizzazione della classe di base vuota" è rilevante solo a causa dell'insistenza sul fatto che tutti gli oggetti (al contrario di tutti i byte all'interno degli oggetti) devono avere indirizzi univoci. Il C ++ avrebbe potuto essere semplificato se fosse stato richiesto il codice che si occupava effettivamente di oggetti con indirizzi univoci per assicurarsi che avessero dimensioni diverse da zero.
supercat,

15

Cosa dice lo standard al riguardo? È sempre legale "allocare" un blocco di memoria vuoto?

Ogni oggetto ha un'identità univoca, ovvero un indirizzo univoco, che implica una lunghezza diversa da zero (la quantità effettiva di memoria verrà aumentata in silenzio, se si richiedono zero byte).

Se assegnassi più di uno di questi oggetti, scopriresti che hanno indirizzi diversi.


"Ogni oggetto ha un'identità unica, vale a dire un indirizzo univoco, che implica una lunghezza diversa da zero" - vero, ma sbagliato. L'oggetto ha un indirizzo, ma il puntatore all'oggetto può puntare alla memoria casuale. "La quantità effettiva di memoria verrà aumentata silenziosamente, se si richiedono zero byte" - non sono sicuro. operator []memorizza anche la dimensione dell'array da qualche parte (vedi isocpp.org/wiki/faq/freestore-mgmt#num-elems-in-new-array ). Pertanto, se l'implementazione alloca il conteggio dei byte con i dati, potrebbe semplicemente allocare per il conteggio dei byte e 0 byte per i dati, restituendo il puntatore 1-last-last.
Steed

Una lunghezza diversa da zero della memoria allocata, non una lunghezza diversa da zero della memoria utilizzabile. Il mio punto era che due puntatori a due oggetti distinti non dovevano puntare allo stesso indirizzo.
ChrisW,

1
Lo standard ( open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf ) in effetti dice (3.7.4.1/2) che chiamate diverse operator new[]dovrebbero restituire puntatori diversi. A proposito, il new expressionha alcune regole aggiuntive (5.3.4). Non sono riuscito a trovare alcun indizio che newcon la dimensione 0 sia effettivamente necessario per allocare qualcosa. Siamo spiacenti, ho annullato il voto perché trovo che la tua risposta non risponda alle domande, ma fornisca alcune dichiarazioni controverse.
Steed

@Steed la memoria per memorizzare la lunghezza del blocco può essere implicitamente calcolata utilizzando lo spazio degli indirizzi. Ad esempio, per array di dimensioni zero potrebbe essere possibile che l' new[]implementazione restituisca gli indirizzi in un intervallo in cui non è mappata la memoria dinamica, quindi non utilizza realmente alcuna memoria (mentre si utilizza lo spazio degli indirizzi)
pqnet,

@pqnet: un'implementazione di buona qualità dovrebbe consentire un numero illimitato di cicli new / delete, no? Su una piattaforma con puntatori a 64 bit potrebbe essere ragionevole affermare che un computer che esegue un while(!exitRequested) { char *p = new char[0]; delete [] p; }ciclo senza riciclare i puntatori collasserebbe in polvere prima che potesse esaurire lo spazio degli indirizzi, ma su una piattaforma con puntatori a 32 bit che sarebbe un presupposto molto meno ragionevole.
supercat,

14

Sì, è completamente legale allocare un 0blocco di dimensioni con new. Semplicemente non puoi farci nulla di utile poiché non ci sono dati validi per l'accesso. int[0] = 5;è illegale.

Tuttavia, credo che lo standard consenta il malloc(0)ritorno di cose come NULL.

Avrai comunque bisogno di delete []qualunque puntatore ti venga restituito dall'allocazione.


5
Per quanto riguarda malloc, hai ragione: è l'implementazione definita. Questo è spesso visto come un malfunzionamento.

Suppongo che una domanda interessante sia: la versione nothrow del nuovo restituisce NULL se le dimensioni sono 0?
Evan Teran,

1
La semantica di new e malloc non è collegata in alcun modo, almeno dallo standard.

non dire che lo sono ... stava chiedendo in particolare la nuova versione di nothrow.
Evan Teran,

@Evan - la versione nothrow di nuovi ritorni null solo se la richiesta fallisce - non solo se la dimensione della richiesta è 0 @Neil - non collegata normalmente - ma collegata per intento (cioè l'operatore new può essere implementato in termini di malloc ma non l'altro in giro) - vedi la nota a piè di pagina che ho inserito nella mia risposta
Faisal Vali,

1

Curiosamente, C ++ richiede che l'operatore new restituisca un puntatore legittimo anche quando sono richiesti zero byte. (Richiedere questo comportamento dal suono strano semplifica le cose altrove nella lingua.)

Ho trovato che la terza edizione effettiva di C ++ dicesse così in "Articolo 51: aderire alla convenzione quando si scrive nuovo ed elimina".


0

Ti garantisco che il nuovo int [0] ti costa più spazio da quando l'ho provato.

Ad esempio, l'utilizzo della memoria di

int **arr = new int*[1000000000];

è significativamente più piccolo di

int **arr = new int*[1000000000];
for(int i =0; i < 1000000000; i++) {
    arr[i]=new int[0];
}

L'utilizzo della memoria del secondo frammento di codice meno quello del primo frammento di codice è la memoria utilizzata per i numerosi nuovi int [0].

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.