Удалить [], равное удалению?
IP_ADAPTER_INFO *ptr=new IP_ADAPTER_INFO[100];
если я освобожу, используя
delete ptr;
это приведет к утечке памяти, если не тогда, почему?
Это код разборки, сгенерированный VS2005
; delete ptr;
0041351D mov eax,dword ptr [ptr]
00413520 mov dword ptr [ebp-0ECh],eax
00413526 mov ecx,dword ptr [ebp-0ECh]
0041352C push ecx
0041352D call operator delete (4111DBh)
00413532 add esp,4
; delete []ptr;
00413535 mov eax,dword ptr [ptr]
00413538 mov dword ptr [ebp-0E0h],eax
0041353E mov ecx,dword ptr [ebp-0E0h]
00413544 push ecx
00413545 call operator delete[] (4111E5h)
0041354A add esp,4
Ответы
Ответ 1
Если это приведет к утечке памяти, вытирает жесткий диск, забеременеет, делает неприятных носовых демонов, преследующих вас вокруг вашей квартиры, или позволяет всем работать нормально, без видимых проблем, undefined. Возможно, так и с одним компилятором, а с другой - с новой версией компилятора, с каждой новой компиляцией, с фазами луны, с вашим настроением или в зависимости от количества нейтрино, прошедшего через процессор на последнем солнечном после полудня. Или это не так.
Все это и бесконечное количество других возможностей помещаются в один термин: Undefined поведение:
Просто держитесь подальше от него.
Ответ 2
Просто иллюстрация некоторых действий "undefined" для некоторых ОС и компиляторов. Надеюсь, что это может быть полезно для людей, чтобы отлаживать их код.
Тест 1
#include <iostream>
using namespace std;
int main()
{
int *p = new int[5];
cout << "pass" << endl;
delete p;
return 0;
}
Тест 2
#include <iostream>
using namespace std;
int main()
{
int *p = new int;
cout << "pass" << endl;
delete[] p;
return 0;
}
Тест 3
#include <iostream>
using namespace std;
struct C {
C() { cout << "construct" << endl; }
~C() { cout << "destroy" << endl; }
};
int main()
{
C *p = new C[5];
cout << "pass" << endl;
delete p;
return 0;
}
Тест 4
#include <iostream>
using namespace std;
struct C {
C() { cout << "construct" << endl; }
~C() { cout << "destroy" << endl; }
};
int main()
{
C *p = new C;
cout << "pass" << endl;
delete[] p;
return 0;
}
- Windows 7 x86, msvc 2010. Компиляция с настройками по умолчанию, то есть обработчик исключений.
Тест 1
pass
Тест 2
pass
Тест 3
construct
construct
construct
construct
construct
pass
destroy
# Then, pop up crash msg
Тест 4
construct
pass
destroy
destroy
destroy
destroy
destroy
destroy
destroy
... # It never stop until CTRL+C
- Mac OS X 10.8.5, llvm-gcc 4.2 или gcc-4.8 генерируют один и тот же вывод
Тест 1
pass
Тест 2
pass
Тест 3
construct
construct
construct
construct
construct
pass
destroy
a.out(71111) malloc: *** error for object 0x7f99c94000e8: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
zsh: abort ./a.out
Тест 4
construct
pass
a.out(71035) malloc: *** error for object 0x7f83c14000d8: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
zsh: abort ./a.out
- Ubuntu 12.04, AMD64, gcc 4.7
Тест 1
pass
Тест 2
pass
Тест 3
construct
construct
construct
construct
construct
*** glibc detected *** ./a.out: munmap_chunk(): invalid pointer: 0x0000000001f10018 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7fe81d878b96]
./a.out[0x400a5b]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7fe81d81b76d]
./a.out[0x4008d9]
======= Memory map: ========
....
zsh: abort (core dumped) ./a.out
Тест 4
construct
destroy
destroy
destroy
destroy
destroy
destroy
destroy
destroy
...
destroy
destroy
*** glibc detected *** ./a.out: free(): invalid pointer: 0x00000000016f6008 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7fa9001fab96]
./a.out[0x400a18]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7fa90019d76d]
./a.out[0x4008d9]
======= Memory map: ========
...
zsh: abort (core dumped) ./a.out
Ответ 3
Он обычно не течет, потому что в случае деструкторов POD тривиальны, и нет необходимости их вызывать, поэтому delete
просто освобождает память, занятую массивом. Для освобождения памяти требуется только значение указателя, поэтому оно будет возвращено в кучу. Массив принимает смежный блок памяти, и поэтому освобождение может быть успешным, как если бы это было освобождение одного элемента.
Но не полагайтесь на это, так как это поведение undefined. Возможно, это работает нормально, может быть, что-то ужасное случается, работает над этим компилятором, не работает на другом, и многие люди благодарны вам за посадку ошибки.
Подробнее см. этот ответ.
Ответ 4
Если A указывает на массив, который был назначен через новый T [n], вы должны удалить его с помощью delete [] A.
Почему?
Разница между delete и delete [] проста: первый уничтожает скалярный объект, а последний уничтожает массив.
Подробнее здесь и здесь.
ОБНОВЛЕНИЕ 1 (неверно):
Если вы используете delete (а не delete []) при распределении с помощью нового T [n], только первый элемент освобождается, а другие не деструктурированы, что приведет к утечке памяти. Зачем? Так Бьярне Страуструп и другие разработали язык. И это не зависит от компилятора. Если один компилятор освобождает другой способ, он просто не соответствует стандарту. Язык программирования С++, главы 6.2.6.2 и 19.4.5.
ОБНОВЛЕНИЕ 2 (правильно):
Я допускаю свою ошибку в отношении поведения использования оператора delete при распределении с новым T [n]. Читая упомянутый документ, я не нашел точное описание, поэтому я предполагаю, что в этой ситуации поведение будет undefined и будет отличаться от компилятора к компилятору. AFAIK, MSVC-компилятор, например, будет генерировать другой код, чем GCC. Пожалуйста, игнорируйте UPDATE 1.
Ответ 5
удалить: вызывает соответствующий деструктор только для указанного элемента (если необходимо), затем освобождает фрагмент памяти
удалить []: вызывает в своем массиве соответствующие деструкторы для каждого элемента (если необходимо), затем освобождает фрагмент памяти
Ответ 6
Для массива POD он будет не протекать (с большинством компиляторов). Например, MSVC генерирует идентичный код удалить и удалить [] для массива POD strong > .
Лично я считаю, что C/С++ может быть без оператора delete []. Компилятор знает размер объекта, а выделенный размер памяти известен во время выполнения, поэтому очень просто знать массив указателей или нет и правильно распоряжаться памятью.
EDIT:
ОК, ребята. Можете ли вы протестировать свой компилятор и сказать, что он протекает?
Подумайте, как разработчик компилятора. У нас есть новый, новый [], удалить, удалить []. Каждый новый имеет свой собственный delete. Кажется совершенным и полным. Посмотрите, что происходит, когда вы вызываете delete []?
1. call vector destructor for an object
2. actual free memory
Что такое деструктор для POD? Ничего!
Таким образом, вызов удалить для массива POD не будет протекать! Даже если он нарушает стандарт. Даже если это не рекомендуется.
EDIT2:
Это код разборки, сгенерированный VS2008:
operator delete[]:
78583BC3 mov edi,edi
78583BC5 push ebp
78583BC6 mov ebp,esp
78583BC8 pop ebp
78583BC9 jmp operator delete (78583BA3h)