Foo* set = new Foo[100];
// ...
delete [] set;
Nie przekraczasz granic tablicy delete[]. Ale gdzie są przechowywane te informacje? Czy to jest znormalizowane?
Foo* set = new Foo[100];
// ...
delete [] set;
Nie przekraczasz granic tablicy delete[]. Ale gdzie są przechowywane te informacje? Czy to jest znormalizowane?
Odpowiedzi:
Kiedy przydzielasz pamięć na stercie, twój alokator będzie śledził, ile pamięci przydzieliłeś. Zazwyczaj jest to przechowywane w segmencie „head” tuż przed przydzieloną pamięcią. W ten sposób, gdy nadejdzie czas na zwolnienie pamięci, de-alokator wie dokładnie, ile pamięci do zwolnienia.
freewiadomo, ile pamięci należy zwolnić”. Tak, rozmiar bloku pamięci jest przechowywany „gdzieś” przez malloc(zwykle w samym bloku), więc o tym freewie. Jednak new[]/ delete[]to inna historia. Te ostatnie działają w zasadzie na malloc/ free. new[]przechowuje również liczbę elementów, które utworzył w bloku pamięci (niezależnie od malloc), aby później delete[]móc pobrać ten numer i użyć go do wywołania odpowiedniej liczby destruktorów.
malloc) i liczba elementów (według new[]). Zauważ, że tego pierwszego nie można użyć do obliczenia drugiego, ponieważ w ogólnym przypadku wielkość bloku pamięci może być większa niż naprawdę konieczna dla tablicy żądanego rozmiaru. Zauważ też, że licznik elementów tablicy jest potrzebny tylko dla typów z nietrywialnym destruktorem. W przypadku typów z trywialnym destruktorem licznik nie jest przechowywany przez new[]i, oczywiście, nie jest pobierany przez delete[].
JEDNYM z podejść do kompilatorów jest przydzielenie nieco więcej pamięci i zapisanie liczby elementów w elemencie głównym.
Przykład, jak można to zrobić:
Tutaj
int* i = new int[4];
kompilator przydzieli sizeof(int)*5bajty.
int *temp = malloc(sizeof(int)*5)
Będzie przechowywać „4” w pierwszych sizeof(int)bajtach
*temp = 4;
i nastaw i
i = temp + 1;
Więc ipunkty do tablicy 4 elementów, a nie 5.
I usunięcie
delete[] i;
będą przetwarzane w następujący sposób:
int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements
... that are stored in temp + 1, temp + 2, ... temp + 4 if needed
free (temp)
Informacje nie są znormalizowane. Jednak na platformach, nad którymi pracowałem, ta informacja jest przechowywana w pamięci tuż przed pierwszym elementem. Dlatego możesz teoretycznie uzyskać do niego dostęp i sprawdzić go, jednak nie jest to tego warte.
Również dlatego musisz użyć delete [], kiedy przydzielisz pamięć nowemu [], ponieważ tablicowa wersja delete wie, że (i gdzie) musi szukać zwolnienia odpowiedniej ilości pamięci - i wywołać odpowiednią liczbę destruktorów dla obiektów.
Zasadniczo jest umieszczony w pamięci jako:
[informacje] [mem, o który prosiłeś ...]
Gdzie info to struktura używana przez kompilator do przechowywania ilości przydzielonej pamięci, a co nie.
Jest to jednak zależne od implementacji.
W standardzie C ++ jest zdefiniowany jako specyficzny dla kompilatora. Co oznacza magię kompilatora. Może zerwać z nietrywialnymi ograniczeniami wyrównania na co najmniej jednej głównej platformie.
Możesz pomyśleć o możliwych implementacjach, zdając sobie sprawę, że delete[]jest zdefiniowane tylko dla wskaźników zwracanych przez new[], które mogą nie być tym samym wskaźnikiem, co zwracane przez operator new[]. Jedną z implementacji na wolności jest przechowywanie liczby tablic w pierwszej int zwracanej przez operator new[]i new[]zwracanie wskaźnika przesuniętego poza to. (Dlatego nietrywialne dopasowania mogą się złamać new[].)
Pamiętaj, że operator new[]/operator delete[]! = new[]/delete[].
Ponadto jest to prostopadłe do tego, w jaki sposób C zna wielkość przydzielonej pamięci malloc.
Ponieważ tablica, która ma zostać „usunięta”, powinna zostać utworzona przy użyciu jednego operatora „nowego”. „Nowa” operacja powinna była umieścić tę informację na stercie. W przeciwnym razie, skąd dodatkowe zastosowania nowych wiedziałby, gdzie kończy się kupa?
Nie jest znormalizowany. W środowisku wykonawczym Microsoftu nowy operator używa malloc (), a operator usuwania używa free (). Tak więc, w tym ustawieniu twoje pytanie jest równoważne z następującym: Skąd free () zna rozmiar bloku?
Za kulisami jest trochę księgowości, tj. W środowisku wykonawczym C.
Jest to bardziej interesujący problem, niż mogłoby się początkowo wydawać. Ta odpowiedź dotyczy jednej możliwej implementacji.
Po pierwsze, chociaż na pewnym poziomie twój system musi wiedzieć, jak „zwolnić” blok pamięci, leżący u jego podstaw malloc / free (który zazwyczaj nazywają new / delete / new [] / delete []) nie zawsze pamięta dokładnie, ile pamięci jeśli poprosisz, może zostać zaokrąglony w górę (na przykład, gdy przekroczysz 4K, często jest zaokrąglany w górę do następnego bloku wielkości 4K).
Dlatego nawet jeśli można uzyskać rozmiar bloku pamięci, nie mówi nam to, ile wartości znajduje się w nowej [] edytowanej pamięci, ponieważ może być mniejsza. Dlatego musimy przechowywać dodatkową liczbę całkowitą, która mówi nam, ile jest wartości.
Z WYJĄTKIEM, jeśli budowany typ nie ma destruktora, to delete [] nie musi nic robić poza zwolnieniem bloku pamięci, a zatem nie musi niczego przechowywać!