Prosta aplikacja testowa:
cout << new int[0] << endl;
wyjścia:
0x876c0b8
Wygląda na to, że działa. Co standard mówi o tym? Czy zawsze „przydzielanie” pustego bloku pamięci jest legalne?
Prosta aplikacja testowa:
cout << new int[0] << endl;
wyjścia:
0x876c0b8
Wygląda na to, że działa. Co standard mówi o tym? Czy zawsze „przydzielanie” pustego bloku pamięci jest legalne?
Odpowiedzi:
Od 5.3.4 / 7
Gdy wartość wyrażenia w bezpośrednim nowym deklaratorze wynosi zero, funkcja alokacji jest wywoływana w celu alokacji tablicy bez elementów.
Od 3.7.3.1/2
Efekt dereferencji wskaźnika zwróconego jako żądanie zerowego rozmiaru jest niezdefiniowany.
Również
Nawet jeśli wielkość żądanego miejsca [przez new] wynosi zero, żądanie może się nie powieść.
Oznacza to, że możesz to zrobić, ale nie możesz legalnie (w dobrze zdefiniowany sposób na wszystkich platformach) wyłuskać dostępnej pamięci - możesz tylko przekazać ją do skasowania tablicy - i powinieneś ją usunąć.
Oto interesująca notatka (tj. Nie normatywna część normy, ale dołączona do celów ekspozycyjnych) dołączona do zdania z 3.7.3.1/2
[32. Chodzi o to, aby operator new () mógł zostać wdrożony przez wywołanie malloc () lub calloc (), więc reguły są zasadniczo takie same. C ++ różni się od C wymaganiem zerowego żądania do zwrócenia wskaźnika niepustego.]
new[]
z delete[]
- niezależnie od wielkości. W szczególności, gdy dzwonisz new[i]
, potrzebujesz nieco więcej pamięci niż ta, którą próbujesz przydzielić, aby przechowywać rozmiar tablicy (która jest później używana delete[]
podczas
Tak, legalne jest przydzielanie tablicy o zerowej wielkości w ten sposób. Ale musisz go również usunąć.
int ar[0];
jest to nielegalne, dlaczego nowy jest OK?
sizeof (type)
oczekuje się, że nigdy nie zwróci zera. Patrz na przykład: stackoverflow.com/questions/2632021/can-sizeof-return-0-zero
Co standard mówi o tym? Czy zawsze „przydzielanie” pustego bloku pamięci jest legalne?
Każdy obiekt ma unikalną tożsamość, tj. Unikalny adres, co oznacza niezerową długość (rzeczywista ilość pamięci zostanie cicho zwiększona, jeśli poprosisz o zero bajtów).
Jeśli przydzielono więcej niż jeden z tych obiektów, okazałoby się, że mają one różne adresy.
operator []
przechowuje również gdzieś rozmiar tablicy (patrz isocpp.org/wiki/faq/freestore-mgmt#num-elems-in-new-array ). Więc jeśli implementacja przydzieli liczbę bajtów z danymi, może po prostu przydzielić dla liczby bajtów i 0 bajtów dla danych, zwracając wskaźnik 1-ostatni-ostatni.
operator new[]
powinny zwracać różne wskaźniki. BTW, new expression
ma kilka dodatkowych zasad (5.3.4). Nie mogłem znaleźć żadnej wskazówki, która new
przy rozmiarze 0 jest faktycznie wymagana do przydzielenia czegokolwiek. Przepraszam, przegłosowałem, ponieważ uważam, że twoja odpowiedź nie odpowiada na pytania, ale zawiera pewne kontrowersyjne stwierdzenia.
new[]
implementacja zwracała adresy w zakresie, w którym nie ma dynamicznej pamięci mapowanej, a więc tak naprawdę nie używa żadnej pamięci (podczas korzystania z przestrzeni adresowej)
while(!exitRequested) { char *p = new char[0]; delete [] p; }
pętlę bez wskaźników ponownego przetwarzania zapadłby się w pył, zanim mógł zabraknąć przestrzeni adresowej, ale na platformie z 32-bitowymi wskaźnikami byłby to znacznie mniej uzasadnione założenie.
Tak, jest całkowicie legalne, aby przydzielić 0
rozmiar bloku new
. Po prostu nie możesz zrobić nic przydatnego, ponieważ nie ma ważnych danych, do których miałbyś dostęp. int[0] = 5;
jest nielegalne.
Uważam jednak, że standard pozwala na rzeczy takie jak malloc(0)
zwrot NULL
.
Nadal będziesz potrzebował delete []
wskaźnika, który otrzymasz z przydziału.
Co ciekawe, C ++ wymaga, aby nowy operator zwrócił prawidłowy wskaźnik, nawet gdy wymagane jest zero bajtów. (Wymaganie tego dziwnie brzmiącego zachowania upraszcza sprawy w innym języku).
Znalazłem Effective C ++ Third Edition w następujący sposób: „Punkt 51: Przestrzegaj konwencji podczas pisania nowego i usuwania”.
Gwarantuję ci, że nowy int [0] kosztuje dodatkowe miejsce, odkąd go przetestowałem.
Na przykład użycie pamięci przez
int **arr = new int*[1000000000];
jest znacznie mniejszy niż
int **arr = new int*[1000000000];
for(int i =0; i < 1000000000; i++) {
arr[i]=new int[0];
}
Wykorzystanie pamięci drugiego fragmentu kodu minus zużycie pierwszego fragmentu kodu to pamięć używana przez liczne nowe int [0].