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 expressionma kilka dodatkowych zasad (5.3.4). Nie mogłem znaleźć żadnej wskazówki, która newprzy 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ć 0rozmiar 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].