Özellikle, malloc'un sonucunu oluşturmakla ilgili tehlikeli olan nedir?


87

Şimdi insanlar bunu bir kopya olarak işaretlemeye başlamadan önce, aradığım cevabı hiçbiri sağlamayan aşağıdakilerin hepsini okudum:

  1. C SSS: malloc'un dönüş değerini dökümde yanlış olan ne?
  2. SO: malloc () 'un dönüş değerini açıkça değiştirmeli miyim?
  3. SO: C'deki gereksiz işaretçiler
  4. SO: Malloc'un sonucunu yayınlıyor muyum?

Hem C SSS'si hem de yukarıdaki sorulara verilen birçok cevap, çevrimin mallocdönüş değerinin gizleyebileceği gizemli bir hatadan bahsediyor ; ancak bunların hiçbiri pratikte böyle bir hatanın belirli bir örneğini vermez. Şimdi uyarı değil hata dediğime dikkat edin .

Şimdi aşağıdaki kod verilmiştir:

Yukarıdaki kodu gcc 4.2 ile cast ile ve olmadan derlemek aynı uyarıları verir ve program düzgün bir şekilde çalışır ve her iki durumda da aynı sonuçları verir.

Öyleyse herhangi biri, dökümün mallocdönüş değeri nedeniyle oluşabilecek bir derleme veya çalışma zamanı hatası için belirli bir kod örneği verebilir mi, yoksa bu sadece bir şehir efsanesi mi?

Düzenleme Bu konuyla ilgili iyi yazılmış iki argümanla karşılaştım:

  1. Casting Lehine: CERT Advisory: Bir bellek ayırma fonksiyonu çağrısının sonucunu hemen tahsis edilen tipe bir göstericiye çevirin
  2. Yayına Karşı (2012-02-14 itibariyle 404 hatası: 2010-01-27 tarihli İnternet Arşivi Wayback Makinesi kopyasını kullanın . {2016-03-18: "robots.txt nedeniyle sayfa taranamıyor veya görüntülenemiyor."})

6
Döküm voidişaretçiler C ++ gibi kod derlemek sağlar; bazı insanlar bunun bir özellik olduğunu söylüyor, bunun bir hata olduğunu söyleyebilirim;)
Christoph

1
ayrıca, yayın yapmak yerine ne yapmanız gerektiğini açıkladığı için bağlantılarınızdan ilkine
Christoph

3
Oyuncu kadrosunu dahil etmek için CERT'lerin tavsiyesini alacağım. Ayrıca stdlib.h dosyasını eklemeyi asla unutmayacağım. :)
Abhinav

1
Döndürme işlemininmalloc dönüş değeri nedeniyle derleme çalışma zamanı hatasının bir SO-örneği : int*64-bit arch üzerine çevrim .
John_West

1
Bu soru etiketli Cdeğil C++(bunlar iki farklı dillerdir) Yani (bazı yanıtlar olduğu gibi) herhangi bir tartışma bu soruya alakalı değildir.
user3629249

Yanıtlar:


66

Derleyici hatası almazsınız , ancak bir derleyici uyarısı alırsınız . Alıntı yaptığınız kaynakların söylediği gibi (özellikle birincisi ), siz yapabilirsiniz tahmin edilemez bir olsun çalışma zamanı hatası döküm kullanırken dahil olmadanstdlib.h .

Yani sizin tarafınızdaki hata oyuncu kadrosu değil, eklemeyi unutmaktır stdlib.h. Derleyiciler varsayabiliriz mallocbir işlev döndürüyor int, bu nedenle dönüştürerek void*aslında tarafından döndürülen işaretçi mallociçin intve ardından işaretçi türü nedeniyle açık döküm için. Bazı platformlarda intve işaretçiler farklı sayıda bayt alabilir, bu nedenle tür dönüştürmeleri veri bozulmasına neden olabilir.

Neyse ki, modern derleyiciler gerçek hatanıza işaret eden uyarılar verir. Sağladığınız gccçıktıya bakın : Örtük bildirimin ( int malloc(int)) yerleşik ile uyumsuz olduğu konusunda sizi uyarır malloc. Öyleyse, onsuz bile gccbiliyor gibi görünüyormallocstdlib.h .

Bu hatayı önlemek için alçıdan çıkmak, çoğunlukla yazmakla aynı mantıktır.

onun yerine

İkincisi ciddi hata neden olabilir çünkü bir şaşırtmak olsaydı =ve ==ilki bir derleme hatasına yol açacak oysa. Kişisel olarak, niyetimi daha iyi yansıttığı için ikinci stili tercih ediyorum ve bu hatayı yapma eğiliminde değilim.

Aynı şey şu şekilde döndürülen değeri çevirmek için de geçerlidir malloc: Programlamada açık olmayı tercih ederim ve genel olarak kullandığım tüm işlevler için başlık dosyalarını dahil etmeyi iki kez kontrol ederim.


2
Görünüşe göre derleyici uyumsuz örtük bildirim hakkında uyarıda bulunduğundan, derleyici uyarılarınıza dikkat ettiğiniz sürece bu sorun olmayacaktır.
Robert S. Barnes

4
@Robert: Evet, derleyici hakkında belirli varsayımlar verildiğinde. İnsanlar genel olarak en iyi C'yi nasıl yazacaklarına dair tavsiye verirken, tavsiyeyi alan kişinin gcc'nin yeni bir sürümünü kullandığını varsayamazlar.
Steve Jessop

4
Oh, ve ikinci sorunun cevabı, arayanın dönüş değerini (int olduğunu düşündüğü) alıp T * 'ye dönüştüren kodu içermesidir. Aranan uç, dönüş değerini (boşluk * olarak) yazar ve geri döner. Bu yüzden, çağırma kuralına bağlı olarak: int dönüşleri ve void * dönüşleri "aynı yerde" olabilir veya olmayabilir (kayıt veya yığın yuvası); int ve void * aynı boyutta olabilir veya olmayabilir; İkisi arasında dönüşüm, işlemsiz olabilir veya olmayabilir. Yani "sadece çalışabilir" veya değer bozulmuş olabilir (belki bazı bitler kaybolabilir) veya arayan tamamen yanlış değeri alabilir.
Steve Jessop

1
@ RobertS.Barnes partiye geç kaldı, ancak: Dönüş değeri genellikle işlev imzasının bir parçası değil, C ++ 'da bile. Bağlayıcı sadece bir sembole bir sıçrama oluşturur, hepsi bu.
Peter - Monica'yı Yeniden

3
Stdlib.h'yi eklemeden çevirmeyi kullanırken tahmin edilemeyen bir çalışma zamanı hatası alabilirsiniz . Bu doğru, ancak dahil etmemek stdlib.hzaten kendi başına bir hatadır, sadece "dolaylı bildirim" uyarıları alsanız bile.
Jabberwocky

45

Sonucun ortaya atılmasına karşı iyi üst düzey argümanlardan biri malloc , bence, iyi bilinen alt düzey sorunlardan daha önemli olsa da (bildiri eksik olduğunda işaretçiyi kesmek gibi) çoğu zaman bırakılır.

İyi bir programlama uygulaması, mümkün olduğunca türden bağımsız olan kod yazmaktır. Bu, özellikle, tür adlarının kodda mümkün olduğunca az belirtilmesi veya en iyisi hiç belirtilmemesi gerektiği anlamına gelir. Bu, yayınlar (gereksiz dönüştürmelerden kaçının), argümanlar olarak türler sizeof(tür adlarını kullanmaktan kaçının sizeof) ve genellikle tür adlarına yönelik tüm diğer başvurular için geçerlidir.

Tip isimleri bildirimlere aittir. Tür adları mümkün olduğunca bildirimlerle ve yalnızca bildirimlerle sınırlandırılmalıdır.

Bu açıdan bakıldığında, bu kod parçası kötü

ve bu çok daha iyi

sadece "sonucunu mallocyayınlamadığı" için değil, daha ziyade tipten bağımsız olduğu için (veya isterseniz tip agnositik olduğu için), çünkü pherhangi bir müdahaleye gerek kalmadan kendini otomatik olarak bildirilen türe göre ayarlar . Kullanıcı.


Fwiw, bence bu aşağı yukarı bununla aynı sebep: stackoverflow.com/questions/953112/… ama DIY'den ziyade türden bağımsız olmaya odaklandı. Tabii ki ilki ikinciden sonra gelir (veya tam tersi), bu yüzden en azından bazen bahsedilir . :)
gevşeyin

5
@unwind you büyük olasılıkla DIY yerine KURU demek
kratenko

18

Prototiplenmemiş işlevlerin döndüğü varsayılır int .

Yani intbir işaretçiye atıyorsun . İşaretçiler daha genişseint platformunuzdakilerden , bu oldukça riskli bir davranıştır.

Artı, tabii ki, bazı insanlar uyarıları dikkate Buna olmak hata görürler, yani kodlar onlar olmadan derlenmelidir.

Kişisel olarak, void *başka bir işaretçi tipine çevirmenize gerek olmadığı gerçeğinin C'deki bir özellik olduğunu düşünüyorum ve kırılması gereken kodu göz önünde bulundurun.


14
Derleyicinin dil hakkında benden daha çok şey bildiğine inanıyorum, bu yüzden beni bir şey hakkında uyarırsa, dikkat ederim.
György Andrasek

3
Birçok projede, C kodu C ++ olarak derlenir ve burada void*.
laalto

nit: " varsayılan olarak , prototiplenmemiş işlevlerin döndüğü varsayılır int." - Prototiplenmemiş işlevlerin dönüş türünü değiştirmenin mümkün olduğunu mu söylüyorsunuz?
pmg

1
@laalto - Öyle, ama olmamalı. C, C ++ değil, C'dir ve C ++ derleyicisiyle değil, C derleyicisiyle derlenmelidir. Mazeret yok: GCC (piyasadaki en iyi C derleyicilerinden biri) akla gelebilecek hemen hemen her platformda çalışır (ve aynı zamanda yüksek düzeyde optimize edilmiş kod üretir). C'yi bir C ++ derleyicisiyle derlemek için tembellik ve gevşek standartlar dışında hangi nedenleriniz olabilir?
Chris Lutz

3
Hem C ve C ++ olarak derlemek isteyebilirsiniz kodu örneği: #ifdef __cplusplus \nextern "C" { \n#endif static inline uint16_t swb(uint16_t a) {return ((a << 8) | ((a >> 8) & 0xFF); } \n#ifdef __cplusplus\n } \n#endif. Şimdi, malloc'u neden durağan bir satır içi işlevde çağırmak istiyorsunuz, gerçekten bilmiyorum, ancak her ikisinde de çalışan başlıklar neredeyse hiç duyulmamış.
Steve Jessop

11

64-bit kipte derleme yaparken bunu yaparsanız, döndürülen göstericiniz 32-bit olarak kesilecektir.

DÜZENLEME: Çok kısa olduğum için üzgünüm. İşte tartışma amaçlı örnek bir kod parçası.

ana()
{
   karakter * c = (karakter *) malloc (2);
   printf ("% p", c);
}

Döndürülen yığın işaretçisinin, örneğin 0xAB00000000 gibi bir int içinde gösterilenden daha büyük bir şey olduğunu varsayalım.

Malloc bir gösterici döndürmek için prototiplenmemişse, döndürülen int değeri başlangıçta tüm önemli bit kümesiyle bazı kayıtlarda olacaktır. Şimdi derleyici "tamam, nasıl bir göstericiye dönüştürürüm ve int" der. Bu, malloc'un prototipi çıkararak "geri döndüğü" söylendiği düşük dereceli 32 bitlerin bir işaret uzantısı veya sıfır uzantısı olacaktır. İnt işaretli olduğundan, dönüşümün işaret uzantısı olacağını düşünüyorum, bu durumda değeri sıfıra dönüştürecek. 0xABF0000000 dönüş değeri ile sıfırdan farklı bir işaretçi elde edersiniz, bu da referansını kaldırmaya çalıştığınızda biraz eğlenceye neden olur.


1
Bunun nasıl olacağını ayrıntılı olarak açıklayabilir misiniz?
Robert S. Barnes

5
Sanırım Peeter Joot, "Varsayılan olarak, prototiplenmemiş işlevlerin stdlib.h dahil olmak üzere int döndürdüğü varsayılır" ve sizeof (ptr) 64 iken sizeof (int) 32 bittir.
Test

4

Yeniden Kullanılabilir Yazılım Kuralı:

Malloc () kullanılan bir satır içi işlev yazılması durumunda, C ++ kodu için de yeniden kullanılabilir hale getirmek için, lütfen açık bir tür çevirimi yapın (örn. (Char *)); aksi takdirde derleyici şikayet eder.


umarım, bağlantı zamanı optimizasyonlarının (son zamanlarda) gcc'ye dahil edilmesiyle (bkz. gcc.gnu.org/ml/gcc/2009-10/msg00060.html ), başlık dosyalarında satır içi işlevleri bildirmek artık gerekli olmayacaktır
Christoph

kötü fikirlerin var. farklı derleyiciler / sürümler / mimariler arasında taşınabilir ve çapraz platformun ne olduğunun farkında mısınız? tamam, yapmayabilirsiniz. o zaman yeniden kullanılabilir ne anlama geliyor?
test edin

2
C ++ yazarken malloc / free doğru metodoloji DEĞİLDİR. Bunun yerine yeni / sil'i kullanın. IE, C ++ kodunda malloc /
free'ye

3
@ user3629249: içinden kullanılabilir olmasını gerektiren bir işlev yazarken ya kullanılarak, Cı-kodu veya C ++ kodu malloc/ freeher ikisini de kullanmak çalışırken daha iyi olması için uygun olan için mallocC ve newC ++, veri yapıları, C ve C arasında paylaşılır özellikle ++ kod ve bir nesnenin C kodunda oluşturulup C ++ kodunda yayınlanması veya bunun tersi olasılığı vardır.
supercat

3

C'deki bir void işaretçisi, açık bir çevrim olmadan herhangi bir işaretçiye atanabilir. Derleyici uyarı verir, ancak C ++ ' da türün malloc()karşılık gelen türe dönüştürülmesiyle yeniden kullanılabilir . Dışarı tip çevrim ile C de kullanılabilir , çünkü C katı tip denetimi değildir . Ancak C ++ kesinlikle tür denetimidir, bu nedenle malloc()C ++ 'da cast yazılması gerekir.


C ++ 'da malloc kullanıyorsanız, iyi bir sebebiniz olsa iyi olur! ; p
2016
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.