Menghapus penunjuk di C ++

92

Konteks: Saya mencoba memahami petunjuk, kami baru saja melihatnya beberapa minggu yang lalu di sekolah dan saat berlatih hari ini saya bertemu dengan orang bodoh? masalah, ini bisa sangat mudah bagi Anda tetapi saya memiliki sedikit atau tidak ada pengalaman pemrograman.

Saya telah melihat beberapa pertanyaan di SO tentang menghapus pointer tetapi semuanya tampaknya terkait dengan menghapus kelas dan bukan pointer 'sederhana' (atau apa pun istilah yang tepat), inilah kode yang saya coba Lari:

#include <iostream>;

using namespace std;

int main() {
  int myVar,
      *myPointer;

  myVar = 8;
  myPointer = &myVar;

  cout << "delete-ing pointers " << endl;
  cout << "Memory address: " << myPointer << endl;

  // Seems I can't *just* delete it, as it triggers an error 
  delete myPointer;
  cout << "myPointer: " << myPointer << endl;
  // Error: a.out(14399) malloc: *** error for object 0x7fff61e537f4:
  // pointer being freed was not allocated
  // *** set a breakpoint in malloc_error_break to debug
  // Abort trap: 6

  // Using the new keyword befor deleting it works, but
  // does it really frees up the space? 
  myPointer = new int;
  delete myPointer;
  cout << "myPointer: " << myPointer << endl;
  // myPointer continues to store a memory address.

  // Using NULL before deleting it, seems to work. 
  myPointer = NULL;
  delete myPointer;
  cout << "myPointer: " << myPointer << endl;
  // myPointer returns 0.

}

Jadi pertanyaan saya adalah:

  1. Mengapa kasus pertama tidak berhasil? Tampaknya penggunaan paling mudah untuk menggunakan dan menghapus penunjuk? Kesalahan mengatakan memori tidak dialokasikan tetapi 'cout' mengembalikan alamat.
  2. Pada contoh kedua kesalahan tidak dipicu tetapi melakukan cout nilai myPointer masih mengembalikan alamat memori?
  3. Apakah # 3 benar-benar berfungsi? Sepertinya berhasil bagi saya, penunjuk tidak lagi menyimpan alamat, apakah ini cara yang tepat untuk menghapus penunjuk?

Maaf untuk pertanyaan yang panjang, ingin membuatnya sejelas mungkin, juga untuk menegaskan kembali, saya memiliki sedikit pengalaman pemrograman, jadi jika seseorang dapat menjawab ini menggunakan istilah awam, itu akan sangat dihargai!

leopic
sumber
16
Alasan Anda tidak melihat contoh pertama adalah karena itu salah. Hanya deleteapa kamu new. Itu juga tidak diperlukan untuk pointer untuk mengatur dirinya sendiri ke NULL setelah Anda menghapusnya. Jika Anda menginginkan keamanan di sana, gunakan penunjuk cerdas, yang membebaskan memori untuk Anda dan memberikan kesalahan saat Anda mencoba mengaksesnya ketika tidak ada sesuatu.
chris
Hmm oke, saya tidak yakin apa itu smart pointer, tapi saya akan memeriksanya, terima kasih!
leopic
1
Singkatnya, mereka melakukan apa yang saya jelaskan. Untuk menyimpan sesuatu yang baru, Anda menelepon resetdan membebaskan yang lama. Untuk membebaskannya tanpa penggantian, Anda menelepon release. Ketika keluar dari ruang lingkup, itu dihancurkan, dan dapat membebaskan memori berdasarkan jenisnya. std::unique_ptrdimaksudkan hanya untuk satu pemilik. std::shared_ptrmembebaskannya saat pemilik terakhir berhenti memiliki sumber daya. Mereka juga aman untuk pengecualian. Jika Anda mengalokasikan sumber daya dengan satu sumber daya, lalu menemukan pengecualian, sumber daya tersebut akan dibebaskan dengan benar.
chris

Jawaban:

168

1 & 2

myVar = 8; //not dynamically allocated. Can't call delete on it.
myPointer = new int; //dynamically allocated, can call delete on it.

Variabel pertama dialokasikan di tumpukan. Anda hanya dapat memanggil delete pada memori yang Anda alokasikan secara dinamis (di heap) menggunakan newoperator.

3.

  myPointer = NULL;
  delete myPointer;

Di atas tidak melakukan apa pun . Anda tidak membebaskan apa pun, karena penunjuk menunjuk ke NULL.


Hal berikut tidak boleh dilakukan:

myPointer = new int;
myPointer = NULL; //leaked memory, no pointer to above int
delete myPointer; //no point at all

Anda mengarahkannya ke NULL, meninggalkan memori yang bocor (int baru yang Anda alokasikan). Anda harus membebaskan memori yang Anda tunjuk. Tidak ada cara untuk mengakses yang dialokasikan new intlagi, karena itu kebocoran memori.


Cara yang benar:

myPointer = new int;
delete myPointer; //freed memory
myPointer = NULL; //pointed dangling ptr to NULL

Cara yang lebih baik:

Jika Anda menggunakan C ++, jangan gunakan pointer mentah. Gunakan petunjuk cerdas sebagai gantinya yang dapat menangani hal-hal ini untuk Anda dengan sedikit biaya tambahan. C ++ 11 hadir dengan beberapa .

Anirudh Ramanathan
sumber
13
<pedantry> "On the stack" adalah detail implementasi - detail yang secara mencolok tidak disebutkan oleh C ++. Istilah yang lebih tepat adalah "dengan durasi penyimpanan otomatis". (C ++ 11, 3.7.3) </
pedantry
4
Terima kasih, saya memilih jawaban Anda untuk a) menjelaskan apa yang salah dan b) memberikan praktik terbaik, terima kasih banyak!
leopic
6
@Tidak Benar. delete myPointerdeallocates *myPointer. Itu betul. Namun myPointertetap menunjuk pada lokasi memori yang sudah dibebaskan dan tidak boleh digunakan karena di UB. Ini tidak akan dapat diakses setelah akhir lingkup hanya JIKA itu adalah variabel lokal di tempat pertama.
Anirudh Ramanathan
2
@DarkCulhu Terima kasih! (Saya benar-benar) belajar sesuatu newsetiap hari. (Saya murahan!)
Tqn
1
@AmelSalibasic Memori yang terkait dengan variabel pada tumpukan akan dibebaskan hanya setelah keluar dari ruang lingkup. Menetapkannya untuk NULLmencegah kami menyalahgunakannya nanti.
Anirudh Ramanathan
24

Saya yakin Anda tidak sepenuhnya memahami cara kerja pointer.
Ketika Anda memiliki penunjuk yang menunjuk ke beberapa memori, ada tiga hal berbeda yang harus Anda pahami:
- ada "apa yang ditunjukkan" oleh penunjuk (memori)
- alamat memori ini
- tidak semua penunjuk harus memiliki memori yang dihapus: Anda hanya perlu menghapus memori yang dialokasikan secara dinamis ( newoperator bekas ).

Membayangkan:

int *ptr = new int; 
// ptr has the address of the memory.
// at this point, the actual memory doesn't have anything.
*ptr = 8;
// you're assigning the integer 8 into that memory.
delete ptr;
// you are only deleting the memory.
// at this point the pointer still has the same memory address (as you could
//   notice from your 2nd test) but what inside that memory is gone!

Saat Anda melakukannya

ptr = NULL;
// you didn't delete the memory
// you're only saying that this pointer is now pointing to "nowhere".
// the memory that was pointed by this pointer is now lost.

C ++ memungkinkan Anda mencoba deletepenunjuk yang menunjuk nulltetapi tidak benar-benar melakukan apa pun, hanya tidak memberikan kesalahan apa pun.

salgadokk
sumber
2
Terima kasih, INI sangat membantu, saya pikir saya HARUS menghapus semua petunjuk, tidak tahu itu hanya untuk yang baru, terima kasih.
leopic
13

Pointer mirip dengan variabel normal karena Anda tidak perlu menghapusnya. Mereka dihapus dari memori pada akhir pelaksanaan fungsi dan / atau akhir program.

Namun Anda dapat menggunakan pointer untuk mengalokasikan 'blok' memori, misalnya seperti ini:

int *some_integers = new int[20000]

Ini akan mengalokasikan ruang memori untuk 20000 integer. Berguna, karena Stack memiliki ukuran yang terbatas dan Anda mungkin ingin mengotak-atik muatan 'int' yang besar tanpa kesalahan stack overflow.

Setiap kali Anda memanggil baru, Anda harus 'menghapus' di akhir program Anda, karena jika tidak, Anda akan mendapatkan kebocoran memori, dan beberapa ruang memori yang dialokasikan tidak akan pernah dikembalikan untuk digunakan oleh program lain. Untuk melakukan ini:

delete [] some_integers;

Semoga membantu.

pengguna3728501
sumber
1
Saya ingin menambahkan bahwa memori yang dialokasikan AKAN dikembalikan untuk digunakan oleh program lain, tetapi hanya SETELAH program Anda selesai dijalankan.
sk4l
7

Ada aturan di C ++, untuk setiap baru ada yang dihapus .

  1. Mengapa kasus pertama tidak berhasil? Tampaknya penggunaan paling mudah untuk menggunakan dan menghapus penunjuk? Kesalahan mengatakan memori tidak dialokasikan tetapi 'cout' mengembalikan alamat.

baru tidak pernah dipanggil. Jadi alamat yang dicetak cout adalah alamat lokasi memori myVar, atau nilai yang diberikan ke myPointer dalam kasus ini. Dengan menulis:

myPointer = &myVar;

kamu bilang:

myPointer = Alamat penyimpanan data di myVar

  1. Pada contoh kedua kesalahan tidak dipicu tetapi melakukan cout nilai myPointer masih mengembalikan alamat memori?

Ini mengembalikan alamat yang menunjuk ke lokasi memori yang telah dihapus. Karena pertama Anda membuat pointer dan menetapkan nilainya ke myPointer, kedua Anda menghapusnya, ketiga Anda mencetaknya. Jadi, kecuali Anda memberikan nilai lain ke myPointer, alamat yang dihapus akan tetap ada.

  1. Apakah # 3 benar-benar berfungsi? Sepertinya berhasil bagi saya, penunjuk tidak lagi menyimpan alamat, apakah ini cara yang tepat untuk menghapus penunjuk?

NULL sama dengan 0, Anda menghapus 0, jadi Anda tidak menghapus apa pun. Dan logikanya mencetak 0 karena Anda melakukannya:

myPointer = NULL;

yang sama dengan:

myPointer = 0;
fonZ
sumber
4
  1. Anda mencoba menghapus variabel yang dialokasikan di stack. Anda tidak bisa melakukan ini
  2. Menghapus sebuah pointer sebenarnya tidak merusak sebuah pointer, hanya memori yang ditempati yang dikembalikan ke OS. Anda dapat mengaksesnya hingga memori digunakan untuk variabel lain, atau dimanipulasi. Jadi adalah praktik yang baik untuk mengatur pointer ke NULL (0) setelah menghapus.
  3. Menghapus penunjuk NULL tidak menghapus apa pun.
Hakan Serce
sumber
2
int value, *ptr;

value = 8;
ptr = &value;
// ptr points to value, which lives on a stack frame.
// you are not responsible for managing its lifetime.

ptr = new int;
delete ptr;
// yes this is the normal way to manage the lifetime of
// dynamically allocated memory, you new'ed it, you delete it.

ptr = nullptr;
delete ptr;
// this is illogical, essentially you are saying delete nothing.
Casper Beyer
sumber
1
Selain itu, lihat kuliah ini tentang tumpukan frame youtube.com/watch?v=bjObm0hxIYY , dan youtube.com/watch?v=Rxvv9krECNw tentang petunjuk.
Casper Beyer