C ++ 'da bir işaretçiyi silme


92

Açıklama: İşaretçilerin etrafına sarılmaya çalışıyorum, onları birkaç hafta önce okulda gördük ve bugün pratik yaparken bir aptalla karşılaştım? sorun, sizin için çok basit olabilir, ancak hiç programlama deneyimim yok.

SO'da işaretçileri silmeyle ilgili epeyce soru gördüm, ancak hepsi bir sınıfı silmekle ilgili görünüyor ve 'basit' bir işaretçi değil (veya uygun terim ne olursa olsun), işte yapmaya çalıştığım kod Çalıştırmak:

#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.

}

Yani sorularım:

  1. İlk vaka neden işe yaramıyor? Bir işaretçiyi kullanmak ve silmek için en basit kullanım mı görünüyor? Hata, belleğin ayrılmadığını, ancak 'cout'un bir adres döndürdüğünü söylüyor.
  2. İkinci örnekte hata tetiklenmiyor, ancak myPointer değerinin bir cout'u yapmak hala bir bellek adresi döndürüyor mu?
  3. 3 numara gerçekten işe yarıyor mu? Bana çalışıyor gibi görünüyor, işaretçi artık bir adres depolamıyor, bir işaretçiyi silmenin doğru yolu bu mu?

Uzun soru için özür dilerim, bunu olabildiğince açıklığa kavuşturmak istedim, ayrıca yinelemek için, çok az programlama deneyimim var, bu yüzden birisi buna meslekten olmayanların terimlerini kullanarak cevap verebilirse, çok takdir edilecektir!


16
İlk örneği görmemenizin nedeni yanlış olmasıdır. Sadece deletesen new. Ayrıca işaretçinin siz sildikten sonra kendisini NULL olarak ayarlaması gerekmez. Orada güvenlik istiyorsanız, hafızayı sizin için serbest bırakan ve bir şey tutmadıklarında onlara erişmeye çalıştığınızda hata veren akıllı işaretçiler kullanın.
chris

Hmm tamam, akıllı işaretlerin ne olduğundan emin değilim, ama onu inceleyeceğim, teşekkürler!
leopik

1
Özetle, benim tarif ettiğim şeyi yapıyorlar. Yeni bir şeyi tutmak resetiçin ararsınız ve bu eskisini serbest bırakır. Değiştirmeden serbest bırakmak için arayın release. Kapsam dışına çıktığında yok edilir ve hafızayı ne tür olduğuna bağlı olarak serbest bırakabilir. std::unique_ptryalnızca bir sahip içindir. std::shared_ptrson sahip kaynağa sahip olmayı bıraktığında onu serbest bırakır. Ayrıca istisnai güvenlidirler. Bir kaynak ile bir kaynak ayırırsanız ve ardından bir istisna ile karşılaşırsanız, kaynak uygun şekilde serbest bırakılır.
chris

Yanıtlar:


168

1 ve 2

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

İlk değişken yığın üzerinde tahsis edildi. Silme işlemini yalnızca newoperatörü kullanarak dinamik olarak (yığın üzerinde) ayırdığınız bellekte çağırabilirsiniz .

3.

  myPointer = NULL;
  delete myPointer;

Yukarıdakiler hiçbir şey yapmadı . İşaretçi NULL'u gösterdiğinden hiçbir şeyi serbest bırakmadınız.


Aşağıdakiler yapılmamalıdır:

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

NULL'a işaret ettiniz ve geride sızdırılmış bellek (ayırdığınız yeni int) bıraktınız. Gösterdiğiniz hafızayı boşaltmalısınız. Ayrılana new intartık erişmenin bir yolu yok, dolayısıyla bellek sızıntısı.


Doğru yol:

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

Daha iyi yol:

Eğer C kullanıyorsanız ++, yok çiğ işaretçileri kullanın. Kullanım akıllı işaretçileri , az bir çabayla sizin için bunları işleyebilir hangi yerine. C ++ 11 birkaç tane ile birlikte gelir .


13
<pedantry> "Yığın üzerinde" bir uygulama ayrıntısıdır - C ++ 'ın açıkça bahsetmekten kaçındığı bir ayrıntı. Daha doğru terim "otomatik saklama süresi ile" dir. (C ++ 11, 3.7.3) </
pedantry

4
Teşekkürler, a) neyin yanlış olduğunu açıklamak ve b) en iyi uygulamayı yapmak için cevabınızı seçtim, çok teşekkürler!
leopik

6
@Tqn Bu doğru değil. delete myPointerayırır *myPointer. Bu doğru. Ancak myPointer, serbest bırakılan ve UB olduğu için kullanılmaması gereken bir bellek konumunu göstermeye devam ediyor. Kapsamın sona ermesinden sonra yalnızca ilk etapta yerel bir değişken ise erişilemez olacaktır.
Anirudh Ramanathan'ın

2
@DarkCthulhu Teşekkürler! (Kelimenin tam anlamıyla) newher gün bir şeyler öğreniyorum . (Sevimsizim!)
Tqn

1
@AmelSalibasic Yığın üzerindeki değişkenle ilişkili bellek, yalnızca kapsam dışına çıktığında serbest bırakılacaktır. NULLOnu daha sonra kötüye kullanmamızı önlemek için atamak .
Anirudh Ramanathan'ın

24

İşaretçilerin nasıl çalıştığını tam olarak anlamadığına inanıyorum.
Bir hafızayı işaret eden bir işaretçiniz olduğunda, anlamanız gereken üç farklı şey
vardır : - işaretçi tarafından "işaret edilen" vardır (hafıza)
- bu hafıza adresi
- tüm işaretçilerin hafızasını silmesi gerekmez: sadece siz dinamik olarak ayrılmış olan belleği silmeniz gerekir (kullanılan newoperatör).

Hayal etmek:

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!

Ne zaman yaptın

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 ++, denemenizi sağlar delete işaret eden bir işaretçiyinull ancak aslında hiçbir şey yapmaz, sadece herhangi bir hata vermez.


2
Teşekkürler, BU çok yardımcı oldu, tüm işaretçileri silmem gerektiğini düşündüm, bunun sadece yeni olanlar için olduğunu bilmiyordum, teşekkürler.
leopik

13

İşaretçiler, onları silmeniz gerekmediği için normal değişkenlere benzer. Bir işlevin yürütülmesinin ve / veya programın sonunda bellekten silinirler.

Bununla birlikte, bir bellek 'bloğunu' ayırmak için işaretçileri kullanabilirsiniz, örneğin:

int *some_integers = new int[20000]

Bu, 20000 tamsayı için bellek alanı ayıracaktır. Yararlıdır, çünkü Yığın sınırlı bir boyuta sahiptir ve yığın taşması hatası olmadan büyük bir 'ints' yükü ile uğraşmak isteyebilirsiniz.

Ne zaman yeni çağırırsanız, programınızın sonunda 'silmelisiniz', çünkü aksi takdirde bir bellek sızıntısı yaşarsınız ve tahsis edilen bellek alanlarından bazıları diğer programların kullanması için asla iade edilmez. Bunu yapmak için:

delete [] some_integers;

Umarım yardımcı olur.


1
Sadece ayrılan belleğin diğer programların kullanımı için iade EDİLECEĞİNİ eklemek istiyorum, ancak yalnızca programınızın yürütülmesi bittikten SONRA.
sk4l

7

C ++ 'da bir kural vardır, her yeni için bir silme vardır .

  1. İlk vaka neden işe yaramıyor? Bir işaretçiyi kullanmak ve silmek için en basit kullanım mı görünüyor? Hata, belleğin ayrılmadığını, ancak 'cout'un bir adres döndürdüğünü söylüyor.

yeni asla aranmaz. Dolayısıyla, cout'un yazdırdığı adres, myVar'ın bellek konumunun adresidir veya bu durumda myPointer'a atanan değerdir. Yazarak:

myPointer = &myVar;

diyorsun:

myPointer = myVar'daki verilerin depolandığı yerin adresi

  1. İkinci örnekte hata tetiklenmiyor, ancak myPointer değerinin bir cout'u yapmak hala bir bellek adresi döndürüyor mu?

Silinen bir hafıza konumuna işaret eden bir adres döndürür. Çünkü önce işaretçiyi yaratır ve değerini myPointer'a atarsınız, ikinci olarak onu silersiniz, üçüncü olarak yazdırırsınız. Dolayısıyla, myPointer'a başka bir değer atamazsanız, silinen adres kalacaktır.

  1. 3 numara gerçekten işe yarıyor mu? Bana çalışıyor gibi görünüyor, işaretçi artık bir adres depolamıyor, bir işaretçiyi silmenin doğru yolu bu mu?

NULL eşittir 0, siz 0'ı silersiniz, böylece hiçbir şey silmezsiniz. Ve bunu yaptığınız için 0 yazması mantıklı:

myPointer = NULL;

eşittir:

myPointer = 0;

4
  1. Yığın üzerinde ayrılmış bir değişkeni silmeye çalışıyorsunuz. Bunu yapamazsın
  2. Bir işaretçiyi silmek aslında bir işaretçiyi yok etmez, sadece kullanılan bellek işletim sistemine geri verilir. Bellek başka bir değişken için kullanılana veya başka bir şekilde değiştirilene kadar ona erişebilirsiniz. Bu nedenle, sildikten sonra bir göstericiyi NULL (0) olarak ayarlamak iyi bir uygulamadır.
  3. Bir NULL işaretçisinin silinmesi hiçbir şeyi silmez.

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.

1
Ayrıca, youtube.com/watch?v=bjObm0hxIYY yığın çerçeveler ve işaretçiler için youtube.com/watch?v=Rxvv9krECNw hakkındaki bu derse bakın .
Casper Beyer
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.