Soru Spesifik olarak, malloc sonucunu dökmekle ilgili tehlikeli olan nedir?


Şimdi insanlar bunu bir çift olarak işaretlemeye başlamadan önce, aşağıdakileri okumadım:

  1. C SSS: Malloc'un dönüş değeriyle ilgili sorun nedir?
  2. SO: malloc () 'ın dönüş değerini açıkça atmalı mıyım?
  3. SO: C'deki gereksiz işaretçiler
  4. SO: malloc'un sonucunu ben mi atıyorum?

Hem C Sık Sorulan Sorular hem de yukarıdaki sorulara verilen cevaplar, dökümün esrarengiz bir hata olduğunu gösteriyor. mallocdönüş değeri gizlenebilir; bununla birlikte, hiçbiri uygulamada böyle bir hatanın spesifik bir örneğini vermez. Şimdi dikkat et dedim hata, değil uyarı.

Şimdi aşağıdaki kod verildi:

#include <string.h>
#include <stdio.h>
// #include <stdlib.h>

int main(int argc, char** argv) {

    char * p = /*(char*)*/malloc(10);
    strcpy(p, "hello");
    printf("%s\n", p);

    return 0;
}

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

anon@anon:~/$ gcc -Wextra nostdlib_malloc.c -o nostdlib_malloc
nostdlib_malloc.c: In function ‘main’:
nostdlib_malloc.c:7: warning: incompatible implicit declaration of built-in function ‘malloc’
anon@anon:~/$ ./nostdlib_malloc 
hello

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

Düzenle Bu konuyla ilgili iki iyi yazılmış argümana rastladım:

  1. Dökümün Lehinde: CERT Tavsiyesi: Bir bellek ayırma işlevi çağrısının sonucunu, tahsis edilen türe bir göstericiye derhal dök
  2. Döküm Karşı  (2012-02-14 itibariyle 404 hatası: kullanın İnternet Arşivi Wayback Makinası 2010-01-27 tarihinden itibaren. {2016-03-18: "Sayfa robots.txt nedeniyle taranamaz veya gösterilemez."})

77
2017-10-14 10:34


Menşei


döküm void işaretçiler, kodu C ++ olarak derlemeyi sağlar; Bazı insanlar bunun bir özellik olduğunu söylüyor, bir hata olduğunu söyleyebilirim;) - Christoph
Ayrıca, yorumlarınızı dökümden önce yapmanız gerekenleri açıkladığı için bağlantılarınızın ilkine okuyun: securecoding.cert.org/confluence/display/seccode/... - Christoph
Oyuncuları dahil etmek için CERTs tavsiyesi alacağım. Ayrıca, stdlib.h'yi asla dahil etmeyi unutmayacağım. :) - Abhinav
İşte bir örnek döküm nedeniyle bir derleme çalışma zamanı hatası mallocdönüş değeri: döküm int* 64-bit kemer üzerinde. - John_West
bu soru etiketlendi C değil C++ (bunlar iki farklı dildir) Bu yüzden herhangi bir tartışma (bazı cevaplarda olduğu gibi) bu soruyla ilgili değildir. - user3629249


Cevaplar:


Alamayacaksın derleyici hatası, ancak derleyici uyarısı. Söylediğiniz kaynaklar dediğiniz gibi (özellikle ilki), sen kutu tahmin edilemez olsun çalışma hatası cast kullanırken dahil etmeden stdlib.h.

Yani yanınızdaki hata döküm değil, eklemeyi unutuyor stdlib.h. Derleyiciler bunu kabul edebilir malloc dönen bir işlevdir intbu nedenle void* işaretçi aslında tarafından döndürüldü malloc için int ve daha sonra, açık döküm nedeniyle işaretçi türünüze. Bazı platformlarda, int ve işaretçiler farklı sayıda bayt alabilir, bu nedenle tür dönüşümleri veri bozulmasına neden olabilir.

Neyse ki, modern derleyiciler gerçek hatanıza işaret eden uyarılar verir. Bakın gcc Sağladığınız çıkış: Bu sizi uyarır üstü kapalı beyanname (int malloc(int)) yerleşik ile uyumsuz malloc. Yani gcc biliyor gibi görünüyor malloc bile stdlib.h.

Bu hatayı önlemek için oyuncudan ayrılmak çoğunlukla yazımla aynı mantıktır.

if (0 == my_var)

yerine

if (my_var == 0)

çünkü eğer biri kafa karıştırırsa ciddi bir hataya yol açabilir = ve ==Birincisi bir derleme hatasına yol açacaktır. Niyetini daha iyi yansıttığından ve bu hatayı yapma eğiliminde olmadığım için kişisel stili tercih ediyorum.

Aynısı, döndürülen değeri yayınlamak için de geçerlidir malloc: Programlamada açık olmayı tercih ediyorum ve genellikle kullandığım tüm işlevler için başlık dosyalarını dahil etmeyi iki kez kontrol ediyorum.


61
2017-10-14 10:48



Derleyici, uyumsuz örtük bildirimi uyardığı için derleyici uyarılarınıza dikkat ettiğiniz sürece bu bir sorun değildir. - Robert S. Barnes
@Robert: evet, derleyici hakkında belirli varsayımlar verildi. İnsanlar en iyi nasıl yazacakları konusunda tavsiyelerde bulunduklarında Genel olaraktavsiyeyi alan kişinin yeni bir gcc sürümünü kullandığını düşünemezler. - Steve Jessop
Oh, ve ikinci soruya cevap, arayanın dönüş değerini (int olduğunu düşündüğü) almak ve T * 'ye dönüştürmek için kod içerdiği şeklindedir. Callee sadece dönüş değerini (void * olarak) yazar ve geri döner. Bu nedenle çağrı kuralına bağlı olarak: int döner ve void * döner "aynı yerde" olabilir veya olmayabilir (kayıt veya yığın yuvası); int ve void * aynı boyutta olabilir veya olmayabilir; ikisi arasında dönüştürme işlemi op-op olmayabilir veya olmayabilir. Yani "sadece çalışır" veya değer bozulabilir (belki de bazı bitler kaybedilebilir) veya arayan kişi tamamen yanlış değeri alabilir. - Steve Jessop
@ RobertS.Barnes partiye geç kalmış, ancak: Dönüş değeri genellikle C ++ 'da değil, fonksiyon imzasının bir parçası değildir. Linker sadece bir sembol için bir sıçrama üretir, hepsi bu. - Peter A. Schneider
Stdlib.h eklemeden cast kullanırken, öngörülemeyen bir çalışma zamanı hatası alabilirsiniz.. Bu doğru, ama dahil değil stdlib.h sadece "örtülü beyan" uyarıları alsanız bile, kendi başına bir hatadır. - Jabberwocky


Sonucunu dökümüne karşı iyi üst düzey argümanlarından biri malloc benim görüşüme göre, iyi bilinen alt düzey sorunlardan (beyanın eksik olduğu durumlarda işaretçiyi kırpmak gibi) daha önemli olmakla birlikte, çoğunlukla belirtilmemiş durumdadır.

İyi bir programlama pratiği, mümkün olduğunca tip-bağımsız olan kod yazmaktır. Bu, özellikle, bu tür isimlerdeki kodun mümkün olduğunca az veya en iyisi belirtilmemiş en az değinilmesi gerektiği anlamına gelir. Bu, dökümler için geçerlidir (gereksiz dökümlerden sakınmak), sizeof (tür isimlerini kullanmaktan kaçının sizeof) ve genel olarak, tüm diğer tür isimleriyle ilgilidir.

Tip isimleri beyanlara dahildir. Mümkün olduğu kadarıyla, yazım adları deklarasyonlarla ve yalnızca beyanlarla sınırlandırılmalıdır.

Bu bakış açısından, bu kod biraz bozuk.

int *p;
...
p = (int*) malloc(n * sizeof(int));

ve bu çok daha iyi

int *p;
...
p = malloc(n * sizeof *p);

basitçe değil çünkü "sonucunu çevirmiyor malloc", ancak bunun yerine türden bağımsız (ya da tercih ederseniz, tip-agnozik) olduğu için, otomatik olarak kendisini her hangi bir şeye ayarlar. p Kullanıcıdan herhangi bir müdahale gerektirmeden beyan edilir.


43
2017-10-14 17:46



Fwiw, bence bu, bununla aynı nedenden mi? stackoverflow.com/questions/953112/... ama DIY'den ziyade tip bağımsızlığa odaklandı. Tabii ki ilk ikincisi (ya da tersi), yani en azından bundan bahseder ara sıra. :) - unwind
büyük olasılıkla KURU ziyade DIY - kratenko


Prototip edilmeyen işlevlerin geri döndüğü varsayılır. int.

Yani sen bir int işaretçiye Eğer işaretçiler daha genişse intPlatformunuzda, bu oldukça riskli bir davranış.

Artı, elbette, bazı insanlar uyarıları dikkate alıyor olmak Hatalar, yani kod onlarsız derlemelidir.

Şahsen, bence almanız gerekmiyor void * Başka bir işaretçi türü için C'deki bir özelliktir ve bozulacak kodları düşünün.


16
2017-10-14 10:35



Derleyicinin dili bildiğimden daha fazla bildiğine inanıyorum, bu yüzden bana bir şey hakkında uyarırsa dikkat ederim. - György Andrasek
Birçok projede, C kodu C ++ olarak derlenir. void*. - laalto
nit: "varsayılan olarak, prototip olmayan işlevlerin döndüğü varsayılır int"- Prototip edilmemiş işlevlerin dönüş türünü değiştirmek mümkün mü demek istiyorsunuz? - pmg
@ laalto - Bu, ama olmamalı. C C, C ++ değil ve bir C derleyici ile derlenmelidir, bir C ++ derleyicisi değil. Mazeret yok: GCC (orada en iyi C derleyicilerinden biri) akla gelebilecek hemen hemen her platformda çalışır (ve yüksek düzeyde optimize edilmiş kodlar üretir). Hangi nedenleri C tembelliği ve gevşek standartlar dışında bir C ++ derleyicisi ile derlemeniz gerekebilir? - Chris Lutz
Hem C hem de C ++ olarak derlemek isteyebileceğiniz kod ö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, neden aslında bilmiyorum statik inline fonksiyonu malloc aramak istiyorum, ama her ikisi de çalışan başlıkları neredeyse hiç duyulmamış. - Steve Jessop


Bunu 64 bit modunda derlerken yaparsanız, döndürülen işaretçiniz 32 bit'e kesilir.

DÜZENLE: Çok kısa olmasından dolayı üzgünüm. İşte tartışma amaçlı bir örnek kod parçası.

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

Döndürülmüş yığın işaretçisinin, bir int, 0xAB00000000 ifadesinde görülebilir olandan daha büyük bir şey olduğunu varsayalım.

Malloc bir işaretçi döndürmek için prototipli değilse, döndürülen int değeri başlangıçta tüm önemli bit kümeleriyle birlikte bazı kayıtlarda olur. Şimdi derleyici, "tamam, bir işaretçiye nasıl dönüştürebilirim" diyor. Bu, düşük siparişin 32-bitinin bir prototipini kaldırarak malloc "geri döndüğü" söylenen bir işaret uzantısı ya da sıfır uzantısı olacak. İnt imzalandığından, dönüşümün uzantı işareti olacağını düşünüyorum, bu durumda bu değeri sıfıra dönüştürecektir. 0xABF0000000 dönüş değeriyle, sıfırlamayı denediğinizde biraz da eğlenceye neden olacak sıfır olmayan bir işaretçi elde edersiniz.


9
2017-10-14 11:56



Bunun nasıl olacağını detaylı olarak açıklayabilir misiniz? - Robert S. Barnes
Peeter Joot'un, "Varsayılan olarak, prototip olmayan işlevlerin stdlib.h dahil olmak üzere" int / int "olarak kabul edildiğini ve sizeof (int) 'nin 32 bit olduğunu ve sizeof (ptr)' nin 64 olduğunu öğrendiğini düşünüyorum. - Test


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

Kullanılan malloc () 'ın, C ++ kodu için de yeniden kullanılabilir hale getirilmesi amacıyla bir satır içi işlevi yazılması durumunda, lütfen açık bir tip dökümü yapın (ör. (Char *)); Aksi takdirde derleyici şikayette bulunacaktır.


2
2017-10-14 12:22



umarım, (güncel) gcc'de bağlantı zaman optimizasyonlarının dahil edilmesiyle (bkz.) gcc.gnu.org/ml/gcc/2009-10/msg00060.html ), başlık dosyalarındaki satır içi işlevlerin bildirilmesi 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, yapamazsın. o zaman yeniden kullanılabilir ne demektir? - Test
C ++ yazarken malloc / free doğru metodoloji DEĞİLDİR. Daha ziyade yeni / sil kullanın. I.E. C ++ kodunda malloc / free için hiçbir / nada / zero çağrı olmamalıdır - user3629249
@ user3629249: Kullanılabilir olması gereken bir işlev yazarken ya Kullanarak C kodu veya C ++ kodu malloc/free her ikisi de kullanmaktan daha iyi olmaya uygun malloc C ve new C ++ 'da, özellikle veri yapıları C ve C ++ kodları arasında paylaşılıyorsa ve C kodunda bir nesnenin oluşturulup C ++ kodunda ya da tam tersi olarak yayınlanabilme olasılığı vardır. - supercat


C cinsinden bir boşluk işaretçisi, herhangi bir işaretçiye açık bir yayın olmadan atanabilir. Derleyici uyarı verecek ama C ++ 'da yeniden kullanılabilir türüne göre döküm malloc() karşılık gelen tipte. Dışarı tipi döküm ile de kullanılabilir C, Çünkü C katı tip kontrol değildir. Fakat C ++ kesinlikle kontrol edilir bu yüzden cast yazmak gerekli malloc() C ++ 'da.


2
2017-11-27 12:58



C ++ 'da malloc kullanırsanız, iyi bir sebebiniz var! s - antred