Serbest bırakıldıktan sonra değişkeni NULL olarak ayarlama


156

Şirketimde, herhangi bir belleği boşalttıktan sonra değişkeni sıfırlayan bir kodlama kuralı var NULL. Örneğin ...

void some_func () 
{
    int *nPtr;

    nPtr = malloc (100);

    free (nPtr);
    nPtr = NULL;

    return;
}

Yukarıda gösterilen kod gibi durumlarda, ayarının NULLherhangi bir anlamı olmadığını hissediyorum . Yoksa bir şey mi kaçırıyorum?

Böyle durumlarda bir anlamı yoksa, ben bu kodlama kuralını kaldırmak için "kalite ekibi" ile ele alacağım. Lütfen tavsiye.


3
onunla ptr == NULLherhangi bir şey yapmadan önce kontrol etmek her zaman yararlıdır . Eğer ücretsiz işaretçilerinizi geçersiz kılmazsanız ptr != NULLyine de kullanılamaz bir işaretçi elde edersiniz .
Ki Jéy

Sarkan işaretçiler , Ücretsiz Kullanımdan Sonra gibi sömürülebilir güvenlik açıklarına yol açabilir.
Константин Ван

Yanıtlar:


285

Kullanılmayan işaretçileri NULL olarak ayarlamak, sarkan işaretçi hatalarına karşı koruma sağlayan savunma amaçlı bir stildir. Sarkan bir işaretçiye serbest bırakıldıktan sonra erişilirse, rastgele belleği okuyabilir veya üzerine yazabilirsiniz. Boş bir işaretçiye erişilirse, çoğu sistemde anında bir kilitlenme oluşur ve hatanın ne olduğunu hemen söylersiniz.

Yerel değişkenler için, imlecin serbest bırakıldıktan sonra artık erişilmediği "açık" ise, biraz anlamsız olabilir, bu nedenle bu stil üye verileri ve global değişkenler için daha uygundur. Yerel değişkenler için bile, işlev, bellek serbest bırakıldıktan sonra devam ederse iyi bir yaklaşım olabilir.

Stili tamamlamak için, işaretçileri gerçek bir işaretçi değeri atanmadan önce NULL olarak başlatmalısınız.


3
Neden "gerçek bir işaretçi değeri atanmadan önce NULL için işaretçiler başlatmak" anlamıyorum?
Paul Biggar

26
@ Paul: Belirli bir durumda, beyan okuyabilir int *nPtr=NULL;. Şimdi, bir sonraki satırda bir malloc ile bunun gereksiz olacağını kabul ediyorum. Ancak, bildirim ile ilk başlatma arasında bir kod varsa, birisi henüz değeri olmasa da değişkeni kullanmaya başlayabilir. Sıfırla başlatırsanız segfault elde edersiniz; olmadan, rastgele belleği tekrar okuyabilir veya yazabilirsiniz. Benzer şekilde, değişken daha sonra yalnızca koşullu olarak başlatılırsa, null-başlatmayı hatırlarsanız, daha sonra hatalı erişimler size anında çökmeler vermelidir.
Martin / Löwis

1
Ben şahsen önemsiz herhangi bir kod temasında nere dereferencing için bir hata almak boş değil sahibi olmayan bir adresi dereferencing bir hata almak gibi belirsiz olduğunu düşünüyorum. Şahsen hiç rahatsız etmem.
wilhelmtell

9
Wilhelm, mesele şudur ki, boş gösterici dereference ile belirli bir çökme ve sorunun gerçek konumu elde edersiniz. Kötü erişim çökebilir veya çökmeyebilir ve beklenmedik yerlerde verileri veya davranışı beklenmedik şekillerde bozabilir.
Amit Naidu

4
Aslında, işaretçiyi NULL olarak başlatmanın en az bir önemli dezavantajı vardır: derleyicinin başlatılmamış değişkenler hakkında sizi uyarmasını önleyebilir. Kodunuzun mantığı gerçekten işaretçi için bu değeri açıkça işlemezse (yani (nPtr == NULL) dosomething ...) olduğu gibi bırakmak daha iyidir.
Eric

37

İşaretçiyi NULLsonraya ayarlamak free, genellikle yanlış bir öncülte "iyi programlama" kuralı olarak popüler olan şüpheli bir uygulamadır. "Kulağa doğru geliyor" kategorisine ait sahte gerçeklerden biridir, ancak gerçekte kesinlikle hiçbir işe yaramaz (ve bazen olumsuz sonuçlara yol açar).

İddiaya göre, bir işaretçinin NULLsonraya ayarlanması free, aynı işaretçi değeri freebirden çok kez iletildiğinde korkunç "çift serbest" sorununu önlemelidir . Gerçekte, 10 üzerinden 9 durumda, gerçek "çift serbest" problemi , aynı işaretçi değerini tutan farklı işaretçi nesneleri için bağımsız değişken olarak kullanıldığında ortaya çıkar free. Söylemeye gerek yok, bir işaretçiyi NULLsonraya ayarlamak free, bu gibi durumlarda sorunu önlemek için kesinlikle hiçbir şey başaramaz.

Tabii ki, bir argümanla aynı işaretçi nesnesini kullanırken "çift serbest" problemi ile karşılaşmak mümkündür free. Bununla birlikte, gerçekte böyle durumlarda normalde kodun genel mantıksal yapısıyla ilgili bir sorun olduğunu gösterir, sadece tesadüfi bir "çift serbest" değil. Bu gibi durumlarda sorunla başa çıkmanın uygun bir yolu, aynı işaretçinin bir freekereden fazla geçtiği durumdan kaçınmak için kodun yapısını gözden geçirmek ve yeniden düşünmektir . Bu gibi durumlarda göstergenin ayarlanması ve NULL"sabit" sorununun ele alınması, halının altına süpürme girişiminden başka bir şey değildir. Genel olarak çalışmaz, çünkü kod yapısıyla ilgili sorun her zaman kendini göstermenin başka bir yolunu bulur.

Kodunuzu özellikle işaretçi değeri olma güvenmek için tasarlanmıştır Son olarak, NULLya da değil NULL, mükemmel için işaretçi değerini ayarlamak için gayet NULLsonra free. Ancak genel bir "iyi uygulama" kuralı olarak ("her zaman işaretçinizi NULLsonraya getirin free" de olduğu gibi), bir kez daha, sadece dini, vudu benzeri nedenlerden ötürü, bazıları tarafından bilinen ve oldukça işe yaramaz bir sahte.


1
Kesinlikle. Serbest bıraktıktan sonra işaretçiyi NULL olarak ayarlayarak sabitlenecek bir çift serbestliğe neden olduğunu hiç hatırlamıyorum, ancak yapamayacağım bolluğa neden oldum.
LnxPrgr3

4
@AnT "şüpheli" biraz fazla. Her şey kullanım durumuna bağlıdır. İşaretçinin değeri gerçek / yanlış anlamda kullanılıyorsa, bu sadece geçerli bir uygulama değil, en iyi uygulamadır.
Coder

1
@Coder Tamamen yanlış. İşaretçinin değeri, ücretsiz çağrıdan önce bir nesneye işaret edip etmediğini bilmek için yanlış bir anlamda kullanılırsa, bu sadece en iyi uygulama değil, yanlıştır . Örneğin: foo* bar=getFoo(); /*more_code*/ free(bar); /*more_code*/ return bar != NULL;. Burada ayarlama bariçin NULLçağrısından sonra için freeo düşünmek işlevini neden olacaktır asla bir bar vardı ve yanlış değerini döndürür!
David Schwartz

Asıl yararın çift serbestliğe karşı korumak olduğunu düşünmüyorum, daha önce ve daha güvenilir bir şekilde sarkan işaretçileri yakalamak. Örneğin, içerdiği bellek işaretçileri serbest ve içerilen dosyaları kapatmak gibi ben kaynakları, tahsis bellek için işaretçiler, dosya tanıtıcıları, vb tutan bir yapı serbest zaman, ben NULL ilgili üyeler. Daha sonra kaynaklardan birine yanlışlıkla sarkan bir işaretçi aracılığıyla erişilirse, program her seferinde orada hata yapma eğilimindedir. Aksi takdirde, NULL olmadan, serbest bırakılan verilerin henüz üzerine yazılmamış olabilir ve hata kolayca tekrar üretilemeyebilir.
jimhark

1
İyi yapılandırılmış kodun, bir işaretçinin serbest bırakıldıktan sonra erişildiği duruma veya iki kez serbest bırakıldığı duruma izin vermemesi gerektiğini kabul ediyorum. Ama gerçek dünyada benim kodum muhtemelen beni tanımayan ve işleri düzgün yapmak için zamana ve / veya becerilere sahip olmayan biri tarafından değiştirilecek ve / veya korunacaktır (çünkü son tarih her zaman dündür). Bu nedenle, yanlış kullanılsa bile sistemi çökmeyen kurşun geçirmez fonksiyonlar yazma eğilimindeyim.
mfloris

35

Yanıtların çoğu çift serbest kalmayı önlemeye odaklanmıştır, ancak işaretçiyi NULL değerine ayarlamak başka bir avantaja sahiptir. Bir işaretçiyi serbest bıraktığınızda, bu bellek başka bir malloc çağrısı tarafından yeniden tahsis edilebilir. Etrafta hala orijinal işaretçiniz varsa, işaretçiyi serbest bıraktıktan ve başka bir değişkenden sonra bozmaya çalıştığınız bir hatayla sonuçlanabilir ve programınız bilinmeyen bir duruma girer ve her türlü kötü şey olabilir ( Şanslıysanız, şanssızsanız veri bozulması). İşaretçiyi serbest bıraktıktan sonra NULL olarak ayarladıysanız, daha sonra bu işaretçiyi okuma / yazma girişimi genellikle rasgele bellek bozulmasına tercih edilen bir segfault ile sonuçlanır.

Her iki nedenden dolayı, işaretçiyi serbest bırakıldıktan sonra NULL olarak ayarlamak iyi bir fikir olabilir (). Yine de her zaman gerekli değildir. Örneğin, pointer değişkeni free () işlevinden hemen sonra kapsam dışına çıkarsa, bunu NULL olarak ayarlamak için fazla bir neden yoktur.


1
+1 Bu aslında çok iyi bir nokta. "Tamamen sahte olan" çift ücretsiz "hakkında muhakeme değil, ama bu . Ben işaretçiler mekanik NULL-ing hayranı değilim free, ama bu gerçekten mantıklı.
AnT

Bir işaretçiyi aynı işaretçiden serbest bıraktıktan sonra erişebiliyorsanız, işaret ettiği nesneyi serbest bıraktıktan sonra bir işaretçiye başka bir işaretçi aracılığıyla erişmeniz daha da olasıdır. Yani bu size hiç yardımcı olmuyor - bir nesneye diğerinden kurtulduktan sonra bir işaretçiden erişmediğinizden emin olmak için hala başka bir mekanizma kullanmanız gerekiyor. Aynı yöntemi aynı işaretçi durumunda korumak için de kullanabilirsiniz.
David Schwartz

1
@DavidSchwartz: Yorumunuza katılmıyorum. Birkaç hafta önce bir üniversite egzersizi için bir yığın yazmam gerektiğinde, bir sorunum vardı, birkaç saat araştırdım. Bazı noktalarda zaten boş hafızaya eriştim (ücretsiz bazı satırlar çok erken). Ve bazen çok garip davranışlara yol açar. İşaretçiyi serbest bıraktıktan sonra NULL olarak ayarlasaydım, "basit" bir segfault olurdu ve birkaç saat çalışmayı kurtarırdım. Yani bu cevap için +1!
mozzbozz

2
@katze_sonne Durdurulmuş bir saat bile günde iki kez doğrudur. İşaretçileri NULL olarak ayarlamak , zaten serbest bırakılmış nesnelere hatalı erişimlerin NULL olup olmadığını denetleyen kodda segfaulting yapmasını engelleyerek ve daha sonra kontrol etmesi gereken bir nesneyi sessiz bir şekilde kontrol edememesi nedeniyle hataları gizleyecektir . (Belki de belirli hata ayıklama yapılarında ücretsiz olarak işaretçileri NULL olarak ayarlamak yararlı olabilir veya bunları segfault için garanti edilen NULL dışında bir değere ayarlamak mantıklı olabilir. Ancak bu sersemlik size yardımcı olmak için bir kez lehine bir tartışma değildir. .)
David Schwartz

@DavidSchwartz Kulağa mantıklı geliyor ... Yorumunuz için teşekkürler, bunu gelecekte ele alacağım! :) +1
mozzbozz

20

Belleğin üzerine yazmaktan kaçınmak için bu iyi bir uygulama olarak kabul edilir. Yukarıdaki işlevde gereksizdir, ancak yapıldığında çoğu zaman uygulama hataları bulabilir.

Bunun yerine böyle bir şey deneyin:

#if DEBUG_VERSION
void myfree(void **ptr)
{
    free(*ptr);
    *ptr = NULL;
}
#else
#define myfree(p) do { void ** p_tmp = (p); free(*(p_tmp)); *(p_tmp) = NULL; } while (0)
#endif

DEBUG_VERSION, hata ayıklama kodunda profil serbest bırakmanıza izin verir, ancak her ikisi de işlevsel olarak aynıdır.

Düzenleme : Aşağıda önerildiği gibi do ... eklendi, teşekkürler.


3
Köşeli ayraçlar olmadan bir if ifadesinden sonra kullanırsanız, makro sürümünde küçük bir hata vardır.
Mark Ransom

(Void) 0 nedir? Bu kod şunları yapar: if (x) myfree (& x); başka do_foo (); (x) {serbest (* (& x)) olursa; * (& x) = boş; } boşluk 0; başka do_foo (); Diğeri bir hatadır.
jmucchiello

Bu makro virgül operatörü için mükemmel bir yerdir: free ( (p)), * (p) = null. Tabii ki bir sonraki sorun * (p) 'yi iki kez değerlendirmesidir. {Void * _pp = (p) olmalıdır; Ücretsiz P & P); * _pp = null; } Önişlemci eğlenceli değil.
jmucchiello

5
Makro çıplak parantez içinde olmamalı, bir do { } while(0)blokta olmalıdır, böylece if(x) myfree(x); else dostuff();kırılmaz.
Chris Lutz

3
Lutz'un dediği gibi, makro gövde do {X} while (0)IMO'dur ve bir işlev "gibi hissettiren ve gibi çalışan" bir makro gövde oluşturmanın en iyi yoludur. Çoğu derleyici döngüyü yine de optimize eder.
Mike Clark

7

Serbest () d olan işaretçiye ulaşırsanız kırılabilir veya kırılmaz. Bu bellek, programınızın başka bir bölümüne yeniden tahsis edilmiş olabilir ve daha sonra bellek bozulması elde edersiniz,

İşaretçiyi NULL olarak ayarlarsanız, ona erişirseniz, program her zaman bir segfault ile kilitlenir. Artık yok, bazen işe yarıyor '', artık yok, tahmin edilemeyen bir şekilde çöküyor ''. Hata ayıklamak çok daha kolay.


5
Program her zaman bir segfault ile çökmez. İşaretçiye erişme şekliniz, kayıt silme işleminden önce kendisine yeterince büyük bir ofset uygulandığı anlamına gelirse, adreslenebilir belleğe ulaşabilir: ((MyHugeStruct *) 0) -> fieldNearTheEnd. Ve bu, 0 erişimde hiç segfault olmayan donanımlarla uğraşmadan önce bile. Ancak, program bir segfault ile çökme olasılığı daha yüksektir.
Steve Jessop

7

İşaretçiyi free'd belleğine ayarlamak, bu belleğe işaretçi aracılığıyla erişme girişiminin tanımsız davranışa neden olmak yerine hemen çökeceği anlamına gelir. İşlerin nerede yanlış gittiğini belirlemeyi kolaylaştırır.

Argümanınızı görebiliyorum: nPtrhemen sonra kapsam dışına çıktığı nPtr = NULLiçin bunu ortaya koymak için bir neden yok gibi görünüyor NULL. Ancak, bir structüye veya işaretçinin hemen kapsam dışına çıkmadığı bir yerde, daha mantıklıdır. Bu işaretçinin kullanmaması gereken kod tarafından tekrar kullanıp kullanmayacağı hemen belli olmaz.

Kuralın bu iki durum arasında bir ayrım yapmadan ifade edilmesi muhtemeldir, çünkü kuralı geliştiricilerin takip etmesi için kuralı otomatik olarak uygulamak çok daha zordur. NULLHer özgürlükten sonra işaretçi koymak acı vermez , ancak büyük sorunlara işaret etme potansiyeline sahiptir.


7

c'deki en yaygın hata çift serbesttir. Temelde böyle bir şey yapıyorsun

free(foobar);
/* lot of code */
free(foobar);

ve oldukça kötü sonuçlanır, işletim sistemi zaten boşaltılmış bazı bellekleri boşaltmaya çalışır ve genellikle segfault. Bu yüzden iyi uygulama, NULLbu hafızayı gerçekten boşaltmanız gerekip gerekmediğini kontrol etmek ve test etmek olabilir.

if(foobar != null){
  free(foobar);
}

Ayrıca free(NULL)hiçbir şey yapmayacağına dikkat etmek için if deyimini yazmak zorunda değilsiniz. Gerçekten bir işletim sistemi gurusu değilim ama şimdi bile çoğu işletim sistemi çift ücretsiz çökecekti.

Bu aynı zamanda çöp toplama özelliğine sahip tüm dillerin (Java, dotnet) bu sorunu yaşamamaktan ve ayrıca bir bütün olarak bellek yönetimini geliştiricilere bırakmak zorunda kalmamaktan gurur duymasının ana nedenidir.


11
Aslında free () öğesini kontrol etmeden arayabilirsiniz - free (NULL) hiçbir şey yapmamak olarak tanımlanır.
Amber

5
Bu böcekleri gizlemiyor mu? (Çok fazla serbest bırakmak gibi.)
Georg Schölly

1
teşekkürler, anladım. denedim:p = (char *)malloc(.....); free(p); if(p!=null) //p!=null is true, p is not null although freed { free(p); //Note: checking doesnot prevent error here }
Shaobo Wang

5
Dediğim gibi free(void *ptr) olamaz o geçirilir pointer değerini değiştirin. İşaretçinin içeriğini , bu adreste depolanan verileri değiştirebilir , ancak adresin kendisini veya işaretçinin değerini değiştiremez . Bu free(void **ptr)(görünüşte standart tarafından izin verilmez) veya bir makro (izin verilen ve mükemmel taşınabilir ancak insanlar makroları sevmez) gerektirir. Ayrıca, C kolaylık değil, programcılara istedikleri kadar kontrol vermekle ilgilidir. Eğer ayar işaretçileri ek yükü istemiyorlarsa, NULLonlara zorlanmamalıdır.
Chris Lutz

2
Dünyada C kodu yazarının bir kısmında profesyonellik eksikliğini veren az şey var. Ancak "aramadan önce işaretçiyi NULL için denetleme free" ("bellek ayırma işlevlerinin sonucunu yayınlama" veya "ile tür adlarını düşüncesiz kullanma sizeof" gibi) içerir.
AnT

6

Bunun arkasındaki fikir, serbest bırakılan işaretçinin yanlışlıkla tekrar kullanılmasını durdurmaktır.


4

Bu gerçekten önemli olabilir. Belleği boşaltmanıza rağmen, programın daha sonraki bir kısmı uzaya inmek için yeni bir şey tahsis edebilir. Eski işaretçiniz şimdi geçerli bir bellek yığınını gösterecektir. Bu durumda birisinin işaretçiyi kullanması, geçersiz program durumuna neden olabilir.

İşaretçiyi BOŞ yaparsanız, o zaman herhangi bir deneme girişimi 0x0 dereference ve çökme olacak, bu da hata ayıklamak kolaydır. Rastgele belleğe işaret eden rastgele işaretçilerin hata ayıklaması zordur. Tabii ki gerekli değil ama bu yüzden en iyi uygulamalar belgesinde.


Windows'ta, en azından hata ayıklama derlemeleri belleği 0xdddddddd olarak ayarlayacaktır, böylece hafızayı silmek için bir işaretçi kullandığınızda hemen bileceksiniz. Tüm platformlarda benzer mekanizmalar olmalıdır.
i_am_jorf

2
jeffamaphone, silinmiş bellek bloğu , işaretçiyi tekrar kullandığınızda yeniden tahsis edilmiş ve başka bir nesneye atanmış olabilir .
Constantin

4

ANSI C standardından:

void free(void *ptr);

Serbest fonksiyon, ptr ile gösterilen alanın serbest bırakılmasına, yani daha fazla tahsis için müsait olmasına neden olur. Ptr bir boş gösterici ise, hiçbir eylem gerçekleşmez. Aksi takdirde, argüman daha önce calloc, malloc veya realloc işlevi tarafından döndürülen bir işaretçiyle eşleşmezse veya boşluk bir serbest bırakma veya yeniden tahsis çağrısı tarafından yeniden yerleştirilmişse, davranış tanımsızdır.

"tanımsız davranış" neredeyse her zaman bir program çökmesidir. Bundan kaçınmak için işaretçiyi NULL olarak sıfırlamak güvenlidir. free () 'in kendisi bunu yapamaz çünkü bir işaretçiye değil, yalnızca imleç geçirilir. İşaretçiyi NULL alan daha güvenli bir free () sürümü de yazabilirsiniz:

void safe_free(void** ptr)
{
  free(*ptr);
  *ptr = NULL;
}

@DrPizza - Bir hata (bence), programınızın olması gerektiği gibi çalışmamasına neden olan bir şeydir. Eğer gizli bir çift serbest programınızı kırarsa, bu bir hatadır. Tam olarak tasarlandığı şekilde çalışıyorsa, bu bir hata değildir.
Chris Lutz

@DrPizza: Birisinin neden NULLmaskeleme hatalarından kaçınmak için ayarlanması gerektiğine dair bir argüman buldum . stackoverflow.com/questions/1025589/… Her iki durumda da bazı hatalar gizleniyor gibi görünüyor.
Georg Schölly

1
Geçersiz bir işaretçi-işaretçi arasında sorunların olduğunu unutmayın: c-faq.com/ptrs/genericpp.html
Güvenli

3
@Chris, hayır, en iyi yaklaşım kod yapısıdır. Kod tabanınızın her yerine rastgele mallocs atmayın ve bunları serbest bırakmayın, ilgili şeyleri bir arada tutun. Bir kaynağı (bellek, dosya, ...) tahsis eden "modül" onu serbest bırakmaktan sorumludur ve bunu yapmak için bir işlev sağlamak zorundadır. Herhangi bir belirli kaynak için, her ikisi de birbirine yakın olarak tahsis edildiği bir yere ve serbest bırakıldığı bir yere sahip olursunuz.
Güvenli

4
@Chris Lutz: Hogwash. Aynı işaretçiyi iki kez serbest bırakan bir kod yazarsanız, programınızda mantıksal bir hata vardır. Bu mantıksal hatanın çökmemesini maskelemek, programın doğru olduğu anlamına gelmez: hala saçma bir şey yapıyor. Çift serbest yazmanın haklı olduğu bir senaryo yoktur.
DrPizza

4

Ben insanlar özgür bir bellek tahsisi eriştiğimde, benim deneyimimde olduğu gibi çok az yardımcı olduğunu düşünüyorum, çünkü neredeyse her yerde başka bir işaretçi var. Ve sonra "işe yaramaz dağınıklığı önlemek" olan başka bir kişisel kodlama standardı ile çakışıyor, bu yüzden nadiren yardımcı olduğunu ve kodu biraz daha az okunabilir hale getirdiğini düşünüyorum gibi yapmıyorum.

Ancak - eğer işaretçi tekrar kullanılmayacaksa, değişkeni null değerine ayarlamayacağım, ancak genellikle daha üst düzey tasarım bana yine de null değerine ayarlamak için bir neden verir. Örneğin, işaretçi bir sınıfın üyesiyse ve işaret ettiği şeyi sildim, sonra sınıftan hoşlanıyorsanız "sözleşme", o üyenin herhangi bir zamanda geçerli bir şeye işaret edeceği için null olarak ayarlanması gerekir bu sebepten dolayı. Küçük bir ayrım ama bence önemli bir ayrım.

C ++ 'da, bazı bellek ayırdığınızda (akıllı işaretçiler kullanmıyorsanız, ancak o zaman bile bir düşünce gerekli değilse) her zaman bu verilere kimin sahip olduğunu düşünmek önemlidir . Ve bu süreç, işaretçilerin genellikle bir sınıfın üyesi olmasına yol açar ve genellikle bir sınıfın her zaman geçerli bir durumda olmasını istersiniz ve bunu yapmanın en kolay yolu, üye değişkenini puanlarını belirtmek için NULL olarak ayarlamaktır. şimdi hiçbir şeye.

Yaygın bir örüntü, tüm üye işaretçileri yapıcıda NULL olarak ayarlamak ve yıkıcı çağrısının tasarımınızın sınıfın sahibi olduğunu söylediği verilere herhangi bir işaretçi üzerinde silinmesini sağlamaktır . Açıkçası bu durumda, daha önce hiçbir veriye sahip olmadığınızı belirtmek için bir şeyi sildiğinizde işaretçiyi NULL olarak ayarlamanız gerekir.

Özetlemek gerekirse, evet genellikle bir şeyi sildikten sonra işaretçiyi NULL olarak ayarladım, ancak daha büyük bir tasarımın ve kodlama standart kuralını körü körüne takip etmekten ziyade verilere kimin sahip olduğu hakkındaki düşüncelerin bir parçası olarak. Ben bunu yapmak için hiçbir faydası olduğunu düşünüyorum ve benim deneyimimde böcek ve kötü kod bu tür bir şey gibi sorumlu olduğu "dağınıklığı" ekler gibi ben senin örnekte böyle olmaz.


4

Son zamanlarda aynı soruyu cevap aradıktan sonra karşımıza çıkıyor. Bu sonuca vardım:

En iyi yöntemdir ve tüm (gömülü) sistemlerde taşınabilir hale getirmek için bunu takip etmek gerekir.

free()platform değiştiğinde değişen bir kütüphane işlevidir, bu nedenle işaretçiyi bu işleve ilettikten ve belleği boşalttıktan sonra bu işaretçinin NULL olarak ayarlanmasını beklememelisiniz. Bu, platform için uygulanan bazı kütüphane için geçerli olmayabilir.

bu yüzden her zaman git

free(ptr);
ptr = NULL;

3

Bu kural, aşağıdaki senaryolardan kaçınmaya çalışırken kullanışlıdır:

1) Karmaşık mantık ve bellek yönetimi ile gerçekten uzun bir fonksiyonunuz var ve işaretçiyi yanlışlıkla fonksiyonun ilerleyen bölümlerinde silinmiş belleğe tekrar kullanmak istemiyorsunuz.

2) İşaretçi, oldukça karmaşık davranışlara sahip bir sınıfın üye değişkenidir ve işaretçiyi yanlışlıkla diğer işlevlerdeki silinmiş belleğe yeniden kullanmak istemezsiniz.

Senaryonuzda, pek bir anlam ifade etmiyor, ancak işlev daha uzun sürecekse, önemli olabilir.

NULL olarak ayarlamanızın daha sonra mantık hatalarını maskeleyebileceğini veya geçerli olduğunu varsaymanız durumunda yine de NULL üzerinde çöktüğünüzü iddia edebilirsiniz, bu yüzden önemli değil.

Genel olarak, iyi bir fikir olduğunu düşündüğünüzde NULL olarak ayarlamanızı ve buna değmediğini düşündüğünüzde rahatsız etmemenizi tavsiye ederim. Bunun yerine kısa fonksiyonlar ve iyi tasarlanmış sınıflar yazmaya odaklanın.


2

Başkalarının söylediklerine eklemek için, işaretçi kullanımının iyi bir yöntemi her zaman geçerli bir işaretçi olup olmadığını kontrol etmektir. Gibi bir şey:


if(ptr)
   ptr->CallSomeMethod();

Serbest bırakıldıktan sonra işaretçiyi açıkça NULL olarak işaretlemek, C / C ++ 'da bu tür kullanıma izin verir.


5
NULL işaretçisinin bir anlamı olmadığı birçok durumda, bunun yerine bir iddia yazmak tercih edilir.
Erich Kitzmueller

2

Bu, tüm göstergeleri NULL olarak başlatmak için daha fazla argüman olabilir, ancak böyle bir şey çok sinsi bir hata olabilir:

void other_func() {
  int *p; // forgot to initialize
  // some unrelated mallocs and stuff
  // ...
  if (p) {
    *p = 1; // hm...
  }
}

void caller() {
  some_func();
  other_func();
}

p, yığın üzerinde öncekiyle aynı yerde bulunur nPtr, bu yüzden hala geçerli bir işaretçi içerebilir. Olarak atamak *p, ilgisiz her tür şeyin üzerine yazabilir ve çirkin hatalara yol açabilir. Özellikle derleyici, hata ayıklama modunda sıfır ile yerel değişkenleri başlatır, ancak optimizasyonlar bir kez açılmazsa. Bu nedenle, hata ayıklama yapıları hatanın herhangi bir belirtisini göstermezken, sürümler rastgele patlar ...


2

Az önce boş bırakılan işaretçiyi NULL olarak ayarlamak zorunlu değildir, ancak iyi bir uygulamadır. Bu şekilde, 1) serbest sivri uçlu 2) serbest kuleyi kullanmaktan kaçınabilirsiniz


2

NULL için bir işaretçi ayarları, çift serbest olarak adlandırılan ağları korumaktır - bu adrese bloğu yeniden tahsis etmeden, aynı adres için bir kezden fazla () çağrıldığında bir durum.

Çift serbest tanımsız davranışa yol açar - genellikle yığın bozulması veya programın hemen çökmesi. NULL işaretçisi için free () öğesini çağırmak hiçbir şey yapmaz ve bu nedenle güvenli olduğu garanti edilir.

Şimdi, işaretçinin kapsamı hemen serbest bıraktıktan hemen sonra () hemen terk etmediği sürece en iyi uygulama, bu işaretçiyi NULL olarak ayarlamaktır, böylece free () tekrar çağrılsa bile şimdi NULL işaretçisi ve tanımsız davranış için çağrılır kaçınıyor.


2

Fikir şu ki, artık geçerli olmayan işaretçiyi serbest bıraktıktan sonra serbest bırakmaya çalışırsanız, sessiz ve gizemli olmaktan ziyade sert (segfault) başarısız olmak istersiniz.

Ama dikkat et. NULL dereference durumunda tüm sistemler segfault neden olmaz. Açık (en azından bazı sürümleri) AIX, * (int *) 0 == 0 ve Solaris bu AIX "özelliğiyle" isteğe bağlı uyumluluğa sahiptir.


2

Orijinal soruya: İçeriği serbest bıraktıktan hemen sonra işaretçiyi NULL olarak ayarlamak, kodun tüm gereksinimleri karşılaması, tamamen hata ayıklaması ve bir daha asla değiştirilmemesi koşuluyla tam bir zaman kaybıdır. Öte yandan, serbest bırakılmış bir işaretçiyi defalarca NULL yapmak, orijinal modülün tasarımı doğru olmadığında ve durumunda, düşüncesizce free () altına yeni bir kod bloğu eklediğinde oldukça yararlı olabilir. -compiles-but-does-does-ne-ben-istemiyorum böcek.

Herhangi bir sistemde, doğru olanı en kolay hale getirmenin ve yanlış ölçümlerin indirgenemez maliyetinin elde edilemez bir amacı vardır. C'de, vasıflı bir işçinin elinde birçok şey yaratabilen ve yanlış kullanıldığında her türlü metaforik yaralanmaya neden olabilecek çok keskin, çok güçlü araçlar sunuyoruz. Bazılarını anlamak veya doğru kullanmak zordur. Ve insanlar, doğal olarak riskten kaçınmak, onunla ücretsiz aramadan önce NULL değeri için bir işaretçi kontrol etmek gibi mantıksız şeyler yaparlar…

Ölçüm sorunu, iyi olanı daha az iyi olandan ayırmaya çalıştığınızda, durum ne kadar karmaşık olursa, belirsiz bir ölçüm alma olasılığınız o kadar yüksektir. Eğer hedef sadece iyi uygulamalar yapmaksa, o zaman bazı belirsiz olanlar aslında iyi olmayanlarla atılırlar. Amacınız iyi olmayanı ortadan kaldırmaksa, belirsizlikler iyilikle kalabilir. Sadece iyi olan ya da açıkça kötüleşen iki hedef, taban tabana zıt gibi görünmektedir, ancak genellikle ne biri ne de diğeri, ikisi de olmayan üçüncü bir grup vardır.

Kalite departmanıyla bir dava açmadan önce, geçersiz işaretçi değerlerinin ne sıklıkta yazılması gereken sorunlara neden olduğunu görmek için hata veri tabanına bakmayı deneyin. Gerçek bir fark yaratmak istiyorsanız, üretim kodunuzdaki en yaygın sorunu tanımlayın ve önlemek için üç yol önerin


İyi cevap. Bir şey eklemek istiyorum. Hata veritabanını gözden geçirmek, çeşitli nedenlerle yapmak iyidir. Ancak orijinal soru bağlamında, kaç geçersiz işaretçi sorununun önlendiğini veya en azından hata veritabanına girmeyecek kadar erken yakalandığını bilmek zor olacaktır. Hata geçmişi, kodlama kuralları eklemek için daha iyi kanıt sağlar.
jimhark

2

Bunun iki nedeni vardır:

Çift serbest bırakırken çökmeleri önleyin

Tarafından yazıldı RageZ bir de yinelenen soru .

C'deki en yaygın hata çift serbesttir. Temelde böyle bir şey yapıyorsun

free(foobar);
/* lot of code */
free(foobar);

ve oldukça kötü sonuçlanır, işletim sistemi zaten boşaltılmış bazı bellekleri boşaltmaya çalışır ve genellikle segfault. Bu yüzden iyi uygulama, NULLbu hafızayı gerçekten boşaltmanız gerekip gerekmediğini kontrol etmek ve test etmek olabilir.

if(foobar != NULL){
  free(foobar);
}

Ayrıca free(NULL) hiçbir şey yapmayacağına dikkat etmek için if deyimini yazmak zorunda değilsiniz. Gerçekten bir işletim sistemi gurusu değilim ama şimdi bile çoğu işletim sistemi çift ücretsiz çökecekti.

Bu aynı zamanda çöp toplama (Java, dotnet) içeren tüm dillerin bu sorunu yaşamamaktan ve ayrıca bir bütün olarak bellek yönetimini geliştiriciye bırakmak zorunda kalmamaktan gurur duymasının ana nedenidir.

Zaten serbest bırakılmış işaretçiler kullanmaktan kaçının

Tarafından yazıldı Martin v. Lowis bir de başka bir yanıt .

Kullanılmayan işaretçileri NULL olarak ayarlamak, sarkan işaretçi hatalarına karşı koruma sağlayan savunma amaçlı bir stildir. Sarkan bir işaretçiye serbest bırakıldıktan sonra erişilirse, rastgele belleği okuyabilir veya üzerine yazabilirsiniz. Boş bir işaretçiye erişilirse, çoğu sistemde anında bir kilitlenme oluşur ve hatanın ne olduğunu hemen söylersiniz.

Yerel değişkenler için, imlecin serbest bırakıldıktan sonra artık erişilmediği "açık" ise, biraz anlamsız olabilir, bu nedenle bu stil üye verileri ve global değişkenler için daha uygundur. Yerel değişkenler için bile, işlev, bellek serbest bırakıldıktan sonra devam ederse iyi bir yaklaşım olabilir.

Stili tamamlamak için, işaretçileri gerçek bir işaretçi değeri atanmadan önce NULL olarak başlatmalısınız.


1

Bir kalite güvence ekibiniz olduğu için KG hakkında küçük bir nokta eklememe izin verin. C için bazı otomatik KG araçları, serbest bırakılmış göstergelere atamaları "işe yaramaz atama ptr" olarak işaretler. Örneğin Gimpel Software'den PC-lint / FlexeLint diyor ki tst.c 8 Warning 438: Last value assigned to variable 'nPtr' (defined at line 5) not used

Mesajları seçici olarak bastırmanın yolları vardır, böylece ekibiniz karar verirse her iki KG gereksinimini de karşılayabilirsiniz.


1

NULL ile bir işaretçi değişkeni bildirmek her zaman tavsiye edilir, örneğin,

int *ptr = NULL;

Diyelim diyelim ki, ptr işaret ediyor 0x1000 hafıza adresi. Kullandıktan sonra free(ptr), bu tekrar bildirerek işaretçi değişkeni geçersiz kılmak her zaman tavsiye NULL . Örneğin:

free(ptr);
ptr = NULL;

NULL olarak yeniden bildirilmezse , işaretçi değişkeni hala aynı adrese ( 0x1000 ) işaret etmeye devam eder , bu işaretçi değişkenine sarkan işaretçi denir . Başka bir işaretçi değişkeni tanımlarsanız (diyelim ki q ) ve adresi yeni işaretçiye dinamik olarak tahsis ederseniz , aynı adresi ( 0x1000 ) yeni işaretçi değişkeni ile alma şansınız vardır . Durumda, aynı işaretçi (kullanırsanız ptr ) ve adres aynı pointer (tarafından işaret de değerini güncellemek ptr ), sonra programın yere bir değer yazmaya sona erecek q beri (işaret p ve q vardır aynı adresi gösteriyor (0x1000 )).

Örneğin

*ptr = 20; //Points to 0x1000
free(ptr);
int *q = (int *)malloc(sizeof(int) * 2); //Points to 0x1000
*ptr = 30; //Since ptr and q are pointing to the same address, so the value of the address to which q is pointing would also change.

1

Uzun lafın kısası: Yanlışlıkla (yanlışlıkla) serbest bıraktığınız adrese erişmek istemezsiniz. Çünkü adresi serbest bıraktığınızda, öbekteki adresin başka bir uygulamaya tahsis edilmesine izin vermiş olursunuz.

Ancak, işaretçiyi NULL olarak ayarlamazsanız ve yanlışlıkla işaretçiyi referans almayı veya bu adresin değerini değiştirmeyi deneyin; HALA YAPABİLİRSİNİZ. AMA MANTIK OLARAK YAPMAK İSTEDİĞİNİZ BİR ŞEY.

Neden hala serbest bıraktığım bellek konumuna erişebiliyorum? Çünkü: Belleği boşaltmış olabilirsiniz, ancak işaretçi değişkeni yığın bellek adresi hakkında hala bilgiye sahipti. Bu yüzden, savunma stratejisi olarak lütfen NULL olarak ayarlayın.

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.