Derleme zamanında silme [] ve silme kötüye kullanımını algılama


19

deleteDerleme zamanında aşağıda açıklanan hatayı tespit etmenin mümkün olup olmadığını bilmek ister misiniz ? Özellikle, g ++ derleyicisini duymak istiyorum.

ClassTypeA *abc_ptr = new ClassTypeA[100];  
abc_ptr[10].data_ = 1;  
delete abc_ptr; // error, should be delete []  

7
Yine de elle silme işlemini çağırmamalısınız.
Martin York

9
@LokiAstari Bu yorumun yararlı olduğunu düşündünüz mü?
James

5
@James: Evet. Anahtar "manüel" dir.
Martin York

Bazen buna uymak için bir sürü eski kodun yeniden yazılması gerekebilir
Nick Keighley

Kullanın std::unique_ptr<ClassTypeA[]>ve sonra gerek yok.
user253751

Yanıtlar:


6

Genel olarak, derleyici bu tür hataları algılayamaz. Örnek: Bazı sınıflar için yapıcıyı kullanarak bazı veri üyelerini ayırdığını new TypeName[], ancak yıkıcı deleteyerine yanlış kullandığını varsayalım delete[]. Yapıcı ve yıkıcı ayrı derleme birimlerinde tanımlanmışsa, derleyici, kullanımın yapıcıyı tanımlayan ayrı olarak derlenmiş dosyada tutarsız olduğunu belirleyiciyi tanımlayan dosyayı derlerken nasıl bilebilir?

GNU derleyicileri ile ilgili olarak, öyle değil. Yukarıda belirtildiği gibi, genel durumda bunu yapamaz. Derleyici, tanımsız bir davranış olduğundan, bu tür uyumsuz yeni / silme hatalarını algılamak zorunda değildir. UB derleyici satıcısının "hapisten kurtulun" kartıdır.

Valgrind gibi araçlar bu tür yeni / silme uyumsuzluklarını algılayabilir ve algılayabilir, ancak çalışma zamanında bunu yapabilir. Sonunda bir yürütülebilir dosya oluşturmak için derlenecek tüm kaynak dosyalarına bakar statik bir analiz aracı olabilir, ama ben bu tür bir hata tespit böyle bir statik analiz aracı yok.


Bu senaryo için kesinlikle bir kuralı olan Parasoft adlı statik bir analiz aracı kullandım . Belirli bir projedeki tüm dosyalarda (doğru yapılandırılmışsa) çalışır. Bununla birlikte, Pete Kirkham'ın Kilian Foth'un cevabı hakkındaki yorumu gibi senaryoları ne kadar iyi işlediğinden emin değilim.
Velociraptors

28

İçin uygun RAII sınıflarını kullanabilirsiniz delete. Bunu yapmanın tek güvenli yolu budur ve bu hata, deletekendinizi çağırırken karşılaşacağınız çok, çok fazla şeyden sadece biridir .

Dinamik ömür boyu kaynakları yönetmek için her zaman sınıfları kullanın ve tür sistemi doğru kaynak tahribatını uygulayacaktır.

Edit: "Ya kodu denetliyorsanız ve değiştiremiyorsanız?" Kahretsin.


18
-1 çünkü bu aslında soruyu cevaplamıyor.
Mason Wheeler

2
Uyuşmazlığı tespit etmenin tek yolu RAII sınıflarının kullanılmasını içeren tür sistemini kullanmaktır.
DeadMG

9
... bu daha da az mantıklı. RAII sınıflarının - bir çalışma zamanı mekanizması - derleyicinin derleme zamanında bildiği statik tip sistem bilgileriyle ne ilgisi var?
Mason Wheeler

6
@MasonWheeler örnek olarak boost :: shared_ptr ve boost :: shared_array öğelerine bakın. Shared_ptr öğesini yok etmek nesneyi siler, paylaşılan_dizisini silmek [] s dizisini siler. Bir paylaşılan_diziye paylaşılan_dizisi atayamazsınız, bu nedenle - ilk etapta bir dizi ile paylaşılan bir_ptr oluşturmazsanız - tür sistemi yanlış silinmenin kullanılmasını engeller.
Pete Kirkham

4
Normalde, böyle bir cevap yardımcı olmaktan daha iğrençtir. Ancak, bu durumda, aslında doğrudur. Derleyici ortak bir hatanın uygulanmasını arıyor ve RAII kullanmak bu hata stilini düzgün bir şekilde önlüyor ve böylece ona tam olarak ne istediğini veriyor. +1
riwalk

10

Bu özel hata - evet. Bu tür bir hata genellikle: ne yazık ki hayır! Bu, gerçekte yürütmeden yürütme akışını tahmin etmeyi içerir ve bu keyfi programlar için mümkün değildir. (Bu yüzden çoğu derleyici örneğiniz gibi basit vakaları tespit etmeye bile çalışmaz.)

Bu nedenle DeadMG'nin cevabı uygun cevaptır: dikkat ederek doğru bulmaya çalışmayın - insan dikkati yanılgandır. Sağlanan dili kullanın ve bilgisayarın dikkat etmesine izin verin.


Bu, yürütme akışını tahmin etmeyi nasıl gerektirir? Bu bana tamamen statik, derleme zamanı bilgisi gibi geliyor; derleyicinin tür sistemi bir dizi ve neyin olmadığını bilir.
Mason Wheeler

Dökümlerin varlığında bile? Üzgünüm, eğer yanlış anladıysam cevabı sileceğim.
Kilian Foth

12
@MasonWheeler abc_ptr statik türü ClassTypeA*böylece yeni ve silme arasına bir çizgi ekleyebilirsiniz Statik yazım if ( rand() % 2 == 1 ) abc_ptr = new ClassTypeA;sistemindeki hiçbir şey, abc_ptrbir diziye mi, dinamik bir nesneyi mi yoksa başka bir nesneye mi yoksa parça yoluna mı işaret ettiğini göstermez .
Pete Kirkham

... oh, doğru. C-karada ne kadar berbat olduğunu unutmaya devam ediyorum gerçek dizi türleri ile dil ile çalışmaya alışkınım. :(
Mason Wheeler

1
@Pete Kirkham, @Mason Wheeler: Ama yine de, çalışma zamanı, işaret ettiği adreste kaç nesnenin saklandığını görmeli abc_ptr, aksi takdirde doğru miktarda belleği nasıl ayırabilir? Böylece çalışma zamanı kaç nesnenin yeniden yerleştirilmesi gerektiğini bilir.
Giorgio

4

Gösterdiğiniz önemsiz durum derleme zamanında tespit edilebilir, çünkü nesnenin örneklenmesi ve imhası aynı kapsamdadır. Genel olarak, silme, örnekleme ile aynı kapsamda, hatta kaynak dosyada değildir. Ve bir C ++ işaretçisinin türü, ayırma şemasını değil, türünün veya dizisinin tek bir nesnesine başvurup başvurmadığı hakkında bilgi taşımaz. Bu nedenle bunu genel olarak derleme zamanında teşhis etmek mümkün değildir.

Neden mümkün olan özel vakaları teşhis etmiyorsunuz?

C ++ 'da, akıllı işaretçiler ve daha üst düzey diziler ( std::vector) gibi kapsamlara bağlı dinamik kaynakların sızmasıyla ilgili araçlar zaten vardır .

Doğru deletelezzeti kullansanız bile , kodunuz yine de istisna güvenli değildir. Arasındaki kod ise new[]ve delete[]dinamik bir çıkış tarafından sona erdiği, silme yürütür olmadı.

Çalışma zamanı algılama gittikçe, Valgrindaraç bunu çalışma zamanında tespit etmek için iyi bir iş çıkarır. İzlemek:

==26781== Command: ./a.out
==26781==
==26781== Mismatched free() / delete / delete []
==26781==    at 0x402ACFC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==26781==    by 0x8048498: main (in /home/kaz/test/a.out)
==26781==  Address 0x4324028 is 0 bytes inside a block of size 80 alloc'd
==26781==    at 0x402B454: operator new[](unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==26781==    by 0x8048488: main (in /home/kaz/test/a.out)

Elbette, Valgrind tüm platformlarda çalışmaz ve araç altındaki tüm çalışma zamanı durumlarını yeniden oluşturmak her zaman pratik veya mümkün değildir.


bu önemsiz vakanın derleme zamanında tespit edilebileceğini söylüyorsunuz. Bunu başarmak için hangi derleme komutunu kullandığınızı söyler misiniz?
SebGR

"derleme zamanında algılanabilir" burada g ++ 'da değil, bir derleyicide uygulanmasının kolay olduğu anlamına gelir. Bir derleyici, söz konusu kapsamı işlerken tanımlayıcının tüm ömrünü kavrar ve tahsis bilgisini sözdizimine bağlı anlamsal özellik olarak yayabilir.
Kaz

-3

Derleme zamanı / statik analiz zamanı zamanında algılama için bazı önemsiz örnekler:

Aşağıdaki özelliklere sahip bir RHEL7 sunucusunda cppcheck 1.77 and 1.49

> cat test.cc
#include <memory>
int main(){char* buf = new char[10];delete buf;}

http://cppcheck.sourceforge.net/

> cppcheck -x c++ test.cc
Checking test.cc ...
[test.cc:2]: (error) Mismatching allocation and deallocation: buf

İle clang++ 3.7.1RHEL7 üzerinde

> clang++ --analyze -x c++ test.cc
test.cc:2:37: warning: Memory allocated by 'new[]' should be deallocated by
'delete[]', not 'delete'
int main(){char* buf = new char[10];delete buf;}
                                    ^~~~~~~~~~
1 warning generated.

Clang Statik Analizörü ayrıca std::unique_ptrgeçilmediğini de tespit edebilir<char[]>

> cat test2.cc
#include <memory>
int main(){std::unique_ptr<char> buf(new char[10]);}

https://clang-analyzer.llvm.org/

> clang++ --analyze -x c++ -std=c++11 test2.cc
In file included from test2.cc:1:
In file included from /opt/rh/devtoolset-4/root/usr/lib/gcc/x86_64-redhat-linux/5.3.1/
../../../../include/c++/5.3.1/memory:81:
/opt/rh/devtoolset-4/root/usr/lib/gcc/x86_64-redhat-linux/5.3.1/
../../../../include/c++/5.3.1/bits/unique_ptr.h:76:2: 
warning: Memory allocated by
      'new[]' should be deallocated by 'delete[]', not 'delete'
        delete __ptr;
        ^~~~~~~~~~~~
1 warning generated.

Aşağıda clang, testler ve bulduğum bir hata ekledi çalışma için bir bağlantı ile güncelleme.

Bu, clang'a reviews.llvm.org/D4661 - "'Yeni' ve 'sil' kullanımlarını yanlış eşleştirmeyi algıla" ile eklendi .

Testler test aşamasındadır / Analiz / EşleşmiyorDeallocator-checker-test.mm

Bu açık hatayı buldum - bugs.llvm.org/show_bug.cgi?id=24819


Hiç kimse belirli bir yanlış kullanımı tespit eden statik bir analizör bulabileceğinizden şüphe etmez , bunun yerine tüm yanlış kullanımları tespit eden (ve umarım doğru kullanımları yanlış yapar )
Caleth
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.