Malloc'un sonucunu yayınlıyor muyum?
Gelen bu soruya , birisi bir önerilen yorumun ı gerektiğini değil sonucunu döküm malloc
, yani
int *sieve = malloc(sizeof(int) * length);
ziyade:
int *sieve = (int *) malloc(sizeof(int) * length);
Neden durum böyle olsun?
Yanıtlar
Hayır ; Eğer yok çünkü sonuç dökme:
- Bu durumda
void *
otomatik olarak ve güvenli bir şekilde başka herhangi bir işaretçi türüne yükseltildiği gibi, gereksizdir . - Koda karışıklık ekler, yayınların okunması çok kolay değildir (özellikle işaretçi türü uzunsa).
- Kendinizi tekrarlamanıza neden olur ki bu genellikle kötüdür.
- Eklemeyi unutursanız bir hatayı gizleyebilir
<stdlib.h>
. Bu, çökmelere neden olabilir (veya daha kötüsü, kodun tamamen farklı bir bölümünde çok sonraya kadar bir çökmeye neden olmaz ). İşaretçiler ve tam sayılar farklı boyutlara sahipse ne olacağını düşünün; o zaman bir uyarıyı yayınlayarak gizliyorsunuz ve döndürülen adresinizin bazı kısımlarını kaybedebilirsiniz. Not: C99 itibarıyla örtük işlevler C'den gitmiştir ve bu nokta artık geçerli değildir çünkü bildirilmemiş işlevlerin geri döndüğüne dair otomatik bir varsayım yokturint
.
Bir açıklama olarak, not dedim, değil "sen yok "Eğer döküm yok" ihtiyaç döküm için". Bence, doğru yapsanız bile, oyuncu kadrosunu dahil etmemek bir başarısızlık. Bunu yapmanın hiçbir faydası yoktur, ancak bir dizi potansiyel risk ve oyuncu kadrosu dahil olmak üzere riskler hakkında bilgi sahibi olmadığınızı gösterir.
Ayrıca, yorumcuların da belirttiği gibi, yukarıdakilerin C ++ 'dan değil, düz C'den bahsettiğine dikkat edin. Ayrı diller olarak C ve C ++ 'ya çok inanıyorum.
Daha fazla eklemek için, kodunuz int
hatalara neden olabilecek tür bilgilerini ( ) gereksiz yere tekrarlar . Dönüş değerini saklamak için kullanılan işaretçinin referansını kaldırmak, ikisini birbirine "kilitlemek" için daha iyidir:
int *sieve = malloc(length * sizeof *sieve);
Bu aynı zamanda length
daha fazla görünürlük için öne doğru hareket eder ve artık parantezleri sizeof
; onlar sadece ihtiyaç vardır argüman bir tür adı olduğunda. Pek çok insan bunu bilmiyor (veya görmezden geliyor), bu da kodlarını daha ayrıntılı hale getiriyor. Unutmayın: sizeof
bir işlev değildir! :)
Taşırken length
öne olabilecek bazı nadir durumlarda görünürlüğünü artırmak, bir de genel durumda, olarak ifade yazmak daha iyi olması gerektiğini dikkat etmelidir:
int *sieve = malloc(sizeof *sieve * length);
Birinciyi tutmak, sizeof
bu durumda, çarpmanın en azından size_t
matematikle yapılmasını sağlar .
Karşılaştırın: ikinciye malloc(sizeof *sieve * length * width)
kıyasla malloc(length * width * sizeof *sieve)
, length * width
ne zaman width
ve length
daha küçük türler aşabilir size_t
.
C'de, dönüş değerini çevirmenize gerek yoktur malloc
. Tarafından döndürülen void işaretçisi otomatik olarak malloc
doğru türe dönüştürülür. Ancak, kodunuzun bir C ++ derleyicisiyle derlenmesini istiyorsanız, bir döküm gerekir. Topluluk içinde tercih edilen bir alternatif, aşağıdakileri kullanmaktır:
int *sieve = malloc(sizeof *sieve * length);
Bu ayrıca, türünü değiştirirseniz, ifadenin sağ tarafını değiştirme konusunda endişelenmenize gerek kalmaz sieve
.
İnsanların belirttiği gibi, oyuncular kötüdür. Özellikle işaretçi atışları.
Oyuncuyu yapıyorsun çünkü:
- Kodunuzu C ve C ++ arasında daha taşınabilir hale getirir ve SO deneyiminin gösterdiği gibi, pek çok programcı gerçekten C ++ (veya C artı yerel derleyici uzantıları) ile yazarken C ile yazdıklarını iddia eder.
- Bunu yapmamak bir hatayı gizleyebilir : ne zaman yazılacağına
type *
karşı ne zaman yazılacağına dair kafa karıştırıcı tüm SO örneklerini not edintype **
. #include
Uygun bir başlık dosyasında başarısız olduğunuzu fark etmekten sizi alıkoyma fikri , ağaçların ormanını kaçırır . "Derleyiciden prototipleri görmemekten şikayet etmesini istemediğiniz için endişelenmeyin - bu sinir bozucu stdlib.h hatırlanması gereken GERÇEK önemli şeydir!"- Ekstra bir bilişsel çapraz kontrolü zorlar . O değişkenin ham boyutu için yaptığınız aritmetiğin hemen yanına (iddia edilen) istenen türü koyar. Bahse girerim,
malloc()
bir alçı olduğunda böceklerin çok daha hızlı yakalandığını gösteren bir SO çalışması yapabilirsiniz . İddialarda olduğu gibi, niyeti ortaya çıkaran ek açıklamalar hataları azaltır. - Makinenin kontrol edebileceği bir şekilde kendinizi tekrarlamak genellikle harika bir fikirdir. Aslında, bir iddia budur ve bu cast kullanımı bir iddiadır. Turing bu fikri yıllar önce ortaya attığından beri, iddialar kodu doğru almak için hala sahip olduğumuz en genel tekniktir.
Diğerlerinin de belirttiği gibi, C için gerekli değil, C ++ için gerekli. C kodunuzu bir C ++ derleyicisiyle derleyeceğinizi düşünüyorsanız, herhangi bir nedenle bunun yerine bir makro kullanabilirsiniz, örneğin:
#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif
Bu şekilde, yine de çok kompakt bir şekilde yazabilirsiniz:
int *sieve = NEW(int, 1);
ve C ve C ++ için derlenecektir.
Gönderen Vikipedi :
Dökümün avantajları
Dökümün dahil edilmesi, bir C programının veya işlevinin C ++ olarak derlenmesine izin verebilir.
Oyuncular, malloc'un 1989 öncesi ve orijinal olarak bir char * döndüren sürümlerine izin verir.
Dönüşüm, geliştiricinin, hedef işaretçi türünün değişmesi durumunda, özellikle de işaretçi malloc () çağrısından uzakta bildirilmişse (modern derleyiciler ve statik çözümleyiciler, bu tür davranışları dönüştürme gerektirmeden bu tür davranışlar konusunda uyarabilir) geliştiricinin tür boyutlandırmasındaki tutarsızlıkları belirlemesine yardımcı olabilir.
Dökümün dezavantajları
ANSI C standardına göre, çevrim fazlalıktır.
Dönüştürmeyi eklemek, malloc için prototipin bulunduğu stdlib.h başlığını dahil etme hatasını maskeleyebilir . Malloc için bir prototipin yokluğunda, standart C derleyicisinin malloc'un bir int döndürdüğünü varsaymasını gerektirir. Atama yoksa, işaretçiye bu tamsayı atandığında bir uyarı verilir; ancak, cast ile, bu uyarı üretilmez ve bir hatayı gizler. Belirli mimarilerde ve veri modellerinde (uzun ve işaretçilerin 64-bit ve int'in 32-bit olduğu 64-bit sistemlerde LP64 gibi), bu hata aslında tanımsız davranışa neden olabilir, çünkü örtük olarak bildirilen malloc bir 32- bit değeri, gerçekte tanımlanan işlev 64 bitlik bir değer döndürür. Arama kurallarına ve bellek düzenine bağlı olarak, bu yığın parçalanmasına neden olabilir. Bildirilmemiş bir işlevin kullanıldığına dair tek tip uyarılar ürettikleri için bu sorunun modern derleyicilerde fark edilmeme olasılığı daha düşüktür, bu nedenle bir uyarı yine de görünecektir. Örneğin, GCC'nin varsayılan davranışı, çevirmenin mevcut olup olmadığına bakılmaksızın "yerleşik işlevin uyumsuz örtük bildirimini" okuyan bir uyarı göstermektir.
İşaretçinin türü bildiriminde değiştirilirse, malloc'un çağrıldığı ve kullanıldığı tüm satırların da değiştirilmesi gerekebilir.
Malloc dökümsüz olarak tercih edilen yöntem olmasına ve deneyimli programcıların çoğu tercih etmesine rağmen , sorunların farkında olarak hangisini tercih ederseniz onu kullanmalısınız.
Yani: C programını C ++ olarak derlemeniz gerekiyorsa (ayrı bir dil olmasına rağmen) kullanım sonucunu çevirmelisiniz malloc
.
C'de bir void
işaretçiyi örtük olarak başka herhangi bir tür işaretçiye dönüştürebilirsiniz , bu nedenle bir atama gerekli değildir. Birini kullanmak, sıradan bir gözlemciye, neden ihtiyaç duyulduğunun bir nedeni olduğunu ve yanıltıcı olabileceğini düşündürür.
Sonucunu malloc
yayınlamazsınız çünkü bunu yapmak kodunuza anlamsız bir dağınıklık ekler.
İnsanların sonucu atmasının en yaygın nedeni malloc
, C dilinin nasıl çalıştığından emin olmamalarıdır. Bu bir uyarı işareti var: Belirli bir dil mekanizması çalışır, o zaman nasıl bilmiyorsanız yok bir tahminde bulun. Yukarıya bakın veya Stack Overflow'da sorun.
Bazı yorumlar:
Bir geçersiz işaretçi, açık bir çevrim olmadan başka herhangi bir işaretçi tipine / tipinden dönüştürülebilir (C11 6.3.2.3 ve 6.5.16.1).
Ancak C ++,
void*
başka bir işaretçi türü arasında örtük bir dönüşüm yapılmasına izin vermez . Yani C ++ 'da oyuncu kadrosu doğru olurdu. Ancak C ++ ile programlıyorsanız, kullanmalınew
ve kullanmamalısınızmalloc()
. Ve asla bir C ++ derleyicisi kullanarak C kodunu derlememelisiniz.Aynı kaynak koduyla hem C hem de C ++ 'yı desteklemeniz gerekiyorsa, farklılıkları işaretlemek için derleyici anahtarlarını kullanın. Uyumlu olmadıkları için her iki dil standardını da aynı kodla uydurmaya çalışmayın.
Başlığı eklemeyi unuttuğunuz için bir C derleyicisi bir işlev bulamazsa, bununla ilgili bir derleyici / bağlayıcı hatası alırsınız. Yani,
<stdlib.h>
bu çok önemli bir şeyi eklemeyi unuttuysanız , programınızı oluşturamazsınız.Standardın 25 yıldan daha eski bir versiyonunu takip eden eski derleyicilerde, dahil
<stdlib.h>
etmeyi unutmak tehlikeli davranışlarla sonuçlanacaktır. Çünkü o eski standartta, görünür prototipi olmayan işlevler, dönüş türünü örtük olarakint
. Sonucumalloc
açık bir şekilde kullanmak bu hatayı gizleyecektir.Ama bu gerçekten sorun değil. 25 yaşında bir bilgisayar kullanmıyorsunuz, öyleyse neden 25 yaşında bir derleyici kullanasınız?
C'de void *
herhangi bir diğer (veri) işaretçisine örtük bir dönüşüm elde edersiniz .
Döndürülen değeri çevirmek malloc()
artık gerekli değil, ancak kimsenin işaret etmediği bir noktayı eklemek istiyorum:
Önce eski günlerde olduğunu, ANSI C sağlar void *
işaretçiler jenerik türü olarak, char *
bu tür bir kullanım için türüdür. Bu durumda, yayın derleyici uyarılarını kapatabilir.
Referans: C SSS
Sadece deneyimlerimi ekleyerek, bilgisayar mühendisliği okuyarak, C'de yazdığımı gördüğüm iki veya üç profesörün her zaman malloc yaptığını görüyorum, ancak sorduğum (büyük bir özgeçmiş ve C anlayışı ile) bana bunun kesinlikle gereksiz olduğunu söyledi ama sadece tamamen spesifikti ve öğrencileri kesinlikle spesifik olma zihniyetine sokmak için kullanılırdı. Esasen döküm, nasıl çalıştığı konusunda hiçbir şeyi değiştirmez, tam olarak söylediği şeyi yapar, belleği ayırır ve döküm onu etkilemez, aynı belleği alırsınız ve yanlışlıkla başka bir şeye dönüştürseniz bile (ve bir şekilde derleyiciden kaçarsan) hataları) C de aynı şekilde erişecektir.
Düzenleme: Dökümün belli bir noktası vardır. Dizi gösterimini kullandığınızda, üretilen kodun bir sonraki öğenin başlangıcına ulaşmak için kaç bellek yeri ilerlemesi gerektiğini bilmesi gerekir, bu, çevrim yoluyla elde edilir. Bu şekilde, bir katlama için 8 bayt ileri giderken, bir int için 4'e gidersiniz ve bu böyle devam eder. Bu nedenle, işaretçi gösterimini kullanırsanız hiçbir etkisi olmaz, dizi gösteriminde gerekli hale gelir.
Sonuçları döküm zorunlu değildir malloc
Döndürdüğü beri, void*
ve void*
herhangi veri türüne sivri olabilir.
Bu nedir GNU C Kütüphanesi Başvuru kılavuzu diyor ki:
malloc
ISO C, gerektiğinde otomatik olarak türüvoid *
başka bir tür işaretçiye dönüştürdüğünden sonucunu herhangi bir işaretçi değişkenine çevrim yapmadan saklayabilirsiniz . Ancak atama operatörleri dışındaki bağlamlarda veya kodunuzun geleneksel C'de çalışmasını istiyorsanız, atama gereklidir.
Ve gerçekten de ISO C11 standardı (p347) şöyle diyor:
Tahsis başarılı olursa, döndürülen işaretçi uygun şekilde hizalanır, böylece temel bir hizalama gereksinimi olan herhangi bir nesne türüne bir işaretçiye atanabilir ve daha sonra bu tür bir nesneye veya bu tür nesnelerin bir dizisine tahsis edilen alanda erişmek için kullanılabilir ( alan açıkça serbest bırakılır)
Bir void işaretçisi, genel bir nesne işaretleyicisidir ve C, bir geçersiz işaretçi tipinden diğer türlere örtük dönüşümü destekler, bu nedenle açıkça tipleme yapmaya gerek yoktur.
Bununla birlikte, aynı kodun, örtük dönüştürmeyi desteklemeyen bir C ++ platformunda mükemmel bir şekilde uyumlu çalışmasını istiyorsanız, tipleme işlemini yapmanız gerekir, bu yüzden hepsi kullanılabilirliğe bağlıdır.
Döndürülen tür geçersiz * olup, referans alınabilmesi için istenen veri işaretçisi türüne dönüştürülebilir.
Programlama diline ve derleyiciye bağlıdır. malloc
C'de kullanırsanız , otomatik olarak cast yazacağı için cast yazmanıza gerek yoktur. Ancak, C ++ kullanıyorsanız, o zaman cast yazmalısınız çünkü malloc
bir void*
tür döndürecektir .
C dilinde, herhangi bir işaretçiye bir void işaretçisi atanabilir, bu nedenle bir tür ataması kullanmamalısınız. "Güvenli tip" tahsisat istiyorsanız, C projelerimde her zaman kullandığım aşağıdaki makro fonksiyonlarını önerebilirim:
#include <stdlib.h>
#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))
#define NEW(ptr) NEW_ARRAY((ptr), 1)
Bunlar yerinde olduğunda basitçe söyleyebilirsin
NEW_ARRAY(sieve, length);
Dinamik olmayan diziler için, üçüncü olması gereken işlev makrosu
#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])
bu, dizi döngülerini daha güvenli ve daha kullanışlı hale getirir:
int i, a[100];
for (i = 0; i < LEN(a); i++) {
...
}
GCC ve Clang'a alışkın insanlar şımarıktır. Dışarısı o kadar da iyi değil.
Yıllar boyunca, kullanmam gereken şaşırtıcı derecede yaşlı derleyiciler tarafından oldukça dehşete düştüm. Genellikle şirketler ve yöneticiler, değişen derleyiciler için son derece muhafazakar bir yaklaşım benimser ve sistemlerinde yeni bir derleyicinin (daha iyi standartlara uygunluk ve kod optimizasyonu ile) çalışıp çalışmayacağını test etmezler bile . Çalışan geliştiriciler için pratik gerçeklik, kodlama yaparken temellerinizi kapsamanız gerektiğidir ve maalesef, kodunuza hangi derleyicinin uygulanabileceğini kontrol edemezseniz, malloc dökümünün iyi bir alışkanlık olduğudur.
Ayrıca birçok kuruluşun kendi kodlama standardını uyguladığını ve eğer tanımlanmışsa insanların takip ettiği yöntem bu olmalıdır. Açık bir rehberliğin yokluğunda, bir standarda kölece bağlılıktan ziyade, büyük olasılıkla her yerde derleme eğilimindeyim.
Mevcut standartlara göre bunun gerekli olmadığı argümanı oldukça geçerlidir. Ancak bu argüman gerçek dünyanın pratikliklerini göz ardı ediyor. Yalnızca günün standardına göre yönetilen bir dünyada değil, benim "yerel yönetimin gerçeklik alanı" dediğim şeyin pratikliği tarafından kodlanıyoruz. Ve bu, uzay zamanının hiç olmadığı kadar bükülmüş ve bükülmüş. :-)
YMMV.
Malloc dökümünü savunma operasyonu olarak düşünme eğilimindeyim. Güzel değil, mükemmel değil ama genel olarak güvenli. (Sonra stdlib.h dahil değildir verdiyseniz Dürüstçe, ettik yolu malloc döküm daha fazla probleme!).
Hayır, sonucunu yayınlamazsınız malloc()
.
Genel olarak, yayın yapmazsınızvoid *
.
Bunu yapmamanın tipik bir nedeni, başarısızlığın #include <stdlib.h>
fark edilmeyeceğidir. C99 örtük işlev bildirimlerini yasa dışı yaptığı için bu artık uzun süredir bir sorun değil , bu nedenle derleyiciniz en az C99'a uygunsa, bir teşhis mesajı alacaksınız.
Ancak gereksiz işaretçi yayınlarını kullanmamak için çok daha güçlü bir neden var :
C'de, bir işaretçi kullanımı neredeyse her zaman bir hatadır . Bunun nedeni aşağıdaki kuraldır ( N1570'de §6.5 p7 , C11 için en son taslak):
Bir nesnenin depolanan değerine yalnızca aşağıdaki türlerden birine sahip olan bir değer ifadesi tarafından erişilmesi gerekir:
- nesnenin
etkin türü ile uyumlu bir tür,
- Nesnenin etkin türü ile uyumlu bir türün nitelikli bir sürümü, - nesnenin etkin türüne karşılık gelen
imzalı veya imzasız tür olan bir tür,
- nesnenin etkin türünün nitelikli bir sürümüne karşılık gelen imzalı veya imzasız tür olan bir tür, - birini içeren bir toplu veya birleşim türü üyeleri arasında yukarıda belirtilen türlerin (yinelemeli olarak, bir alt kümenin veya içerilen birliğin bir üyesi dahil) veya
- bir karakter türü.
Bu aynı zamanda katı örtüşme kuralı olarak da bilinir . Dolayısıyla aşağıdaki kod tanımsız bir davranıştır :
long x = 5;
double *p = (double *)&x;
double y = *p;
Ve bazen şaşırtıcı bir şekilde aşağıdakiler de geçerlidir:
struct foo { int x; };
struct bar { int x; int y; };
struct bar b = { 1, 2};
struct foo *p = (struct foo *)&b;
int z = p->x;
Bazen, do dökme işaretçiler gerekir, ancak verilen sıkı örtüşme kuralı , onunla çok dikkatli olmak zorunda. Dolayısıyla, kodunuzda herhangi bir işaretçi kullanıldığında, geçerliliğini iki kez kontrol etmeniz gereken bir yerdir . Bu nedenle, asla gereksiz bir işaretçi atışı yazmazsınız.
tl; dr
Özetle: C'de, bir işaretçi atımının herhangi bir oluşumu, özel dikkat gerektiren kod için kırmızı bir bayrak kaldırması gerektiğinden, asla gereksiz işaretçi dökümleri yazmamalısınız.
Yan notlar:
Aslında bir atama işlemine ihtiyaç duyduğunuz durumlar vardır
void *
, örneğin bir işaretçi yazdırmak istiyorsanız:int x = 5; printf("%p\n", (void *)&x);
Burada döküm gereklidir, çünkü
printf()
değişken bir fonksiyondur, bu nedenle örtük dönüştürmeler çalışmaz.C ++ 'da durum farklıdır. Türetilmiş sınıfların nesneleriyle uğraşırken işaretçi türlerinin atanması biraz yaygındır (ve doğrudur). Bu nedenle, C ++, ve dönüştürme mantıklı
void *
olduğunu değil örtük. C ++, bir dizi farklı döküm çeşidine sahiptir.
Tip sistemindeki çirkin deliğin reddini göstermek için döküm ekledim, bu da aşağıdaki kod parçacığı gibi kodun, kötü dönüşümü meydana getirmek için hiçbir yayın kullanılmasa bile tanılamadan derlenmesini sağlar:
double d;
void *p = &d;
int *q = p;
Keşke var olmasaydı (ve C ++ 'da olmasaydı) ve bu yüzden yayınladım. Benim zevkimi ve programlama politikamı temsil ediyor. Ben sadece bir işaretçi değil, aynı zamanda etkili bir şekilde oy pusulası atıyorum ve aptallık şeytanlarını kovuyorum . Ben yapamıyorsanız aslında aptallık kovdu , o zaman en azından beni protesto jesti ile bunu arzu ifade edelim.
Aslında, iyi bir uygulama malloc
(ve arkadaşları) geri dönen işlevlerle sarmalamak unsigned char *
ve temelde void *
kodunuzda asla kullanmamaktır . Herhangi bir nesneye genel bir işaretçi ihtiyacınız varsa, bir char *
veya kullanın unsigned char *
ve her iki yönde de yayınlar yapın. Belki de şımartılabilecek bir rahatlama, alçı gibi memset
ve memcpy
alçı olmayan işlevleri kullanmaktır .
Döküm ve C ++ uyumluluğu konusunda, kodunuzu hem C hem de C ++ olarak derleyecek şekilde yazarsanız (bu durumda , onu başka bir şeye atarken dönüş değerini atmanız gerekir ), çok yardımcı olabilirsiniz kendiniz için bir şey: C ++ olarak derlerken C ++ tarzı yayınlara çevrilen, ancak C olarak derlerken bir C dökümüne indirgeyen döküm için makroları kullanabilirsiniz:malloc
void *
/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif
Bu makrolara bağlı kalırsanız grep
, kod tabanınızda bu tanımlayıcılar için basit bir arama, tüm yayınlarınızın nerede olduğunu gösterir, böylece bunlardan herhangi birinin yanlış olup olmadığını gözden geçirebilirsiniz.
Ardından, kodu düzenli olarak C ++ ile derlerseniz, uygun bir döküm kullanımını zorunlu kılacaktır. Örneğin, strip_qual
yalnızca bir const
veya öğesini kaldırmak için kullanırsanız volatile
, ancak program, artık bir tür dönüşümü dahil olacak şekilde değişirse, bir tanı alırsınız ve istenen dönüşümü elde etmek için bir yayın kombinasyonu kullanmanız gerekir.
Bu makrolara bağlı kalmanıza yardımcı olmak için, GNU C ++ (C değil!) Derleyicisinin güzel bir özelliği vardır: tüm C tarzı yayınlar için üretilen isteğe bağlı bir tanılama.
-Wold tarzı döküm (yalnızca C ++ ve Objective-C ++) Eski stil (C stili) geçersiz olmayan bir türe dönüştürülürse uyar bir C ++ programı içinde. Yeni tarz yayınlar (dynamic_cast, static_cast, reinterpret_cast ve const_cast) daha az savunmasızdır istenmeyen etkilere ve aranması çok daha kolay.
C kodunuz C ++ olarak derlenirse, bu -Wold-style-cast
seçeneği kullanarak koda sızabilecek (type)
döküm sözdiziminin tüm oluşumlarını bulabilir ve yukarıdaki makrolar arasından uygun bir seçimle değiştirerek bu teşhisleri takip edebilirsiniz. kombinasyon, gerekirse).
Dönüşümlerin bu şekilde ele alınması, bir "Temiz C" de çalışmak için tek başına en büyük bağımsız teknik gerekçedir: birleşik C ve C ++ diyalekti, bu da dönüş değerinin hesaplanmasını teknik olarak haklı çıkarır malloc
.
Oyuncuyu yapmayı tercih ederim ama manuel olarak değil. Benim favorim glib'den kullanmak g_new
ve g_new0
makrolar. Glib kullanılmazsa benzer makrolar eklerim. Bu makrolar, tür güvenliğinden ödün vermeden kod çoğaltmasını azaltır. Türü yanlış anlarsanız, geçersiz olmayan işaretçiler arasında örtük bir dönüşüm elde edersiniz, bu da bir uyarıya neden olur (C ++ 'da hata). Eğer tanımlayıp bu başlık eklemeyi unutursanız g_new
ve g_new0
, bir hata olacaktı. g_new
ve g_new0
her ikisi de aynı argümanları malloc
alır , bunun aksine daha az argüman gerektirir calloc
. 0
Sıfır başlatılmış bellek elde etmek için ekleyin . Kod, değişiklik yapılmadan bir C ++ derleyicisi ile derlenebilir.
Mümkün olduğunda C ile programlama yaparken yapılacak en iyi şey:
- Programınızın tüm uyarıları açık olarak bir C derleyicisi aracılığıyla derlenmesini sağlayın
-Wall
ve tüm hataları ve uyarıları düzeltin - Olarak tanımlanmış değişken olmadığından emin olun
auto
- Ardından bir C ++ derleyicisi kullanarak
-Wall
ve-std=c++11
. Tüm hataları ve uyarıları düzeltin. - Şimdi tekrar C derleyicisini kullanarak derleyin. Programınız artık herhangi bir uyarı olmadan derlenmeli ve daha az hata içermelidir.
Bu prosedür, C ++ katı tür denetiminden yararlanmanızı sağlar, böylece hataların sayısını azaltır. Özellikle, bu prosedür sizi dahil etmeye zorlar stdlib.h
veya alacaksınız
malloc
bu kapsamda beyan edilmedi
ve ayrıca sizi sonucunu atmaya zorlar yoksa malloc
alacaksınız
Geçersiz dönüşüm
void*
içinT*
veya hedef türünüz ne olursa olsun.
C ++ yerine C ile yazmanın bulabildiğim tek faydası:
- C iyi belirlenmiş bir ABI'ye sahiptir
- C ++ daha fazla kod oluşturabilir [istisnalar, RTTI, şablonlar, çalışma zamanı polimorfizmi]
Statik polimorfik özellik ile birlikte C için ortak olan alt küme kullanıldığında ideal durumda ikinci eksilerin ortadan kalkması gerektiğine dikkat edin .
C ++ katı kuralları uygunsuz bulanlar için, C ++ 11 özelliğini çıkarsanan tipte kullanabiliriz.
auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...
Casting yalnızca C ++ içindir, C değildir. C ++ derleyicisi kullanıyorsanız, onu C derleyicisine değiştirseniz iyi olur.
Malloc'un dökümü C'de gereksizdir ancak C ++ 'da zorunludur.
Aşağıdaki nedenlerden dolayı C'de döküm gereksizdir:
void *
C durumunda otomatik ve güvenli bir şekilde diğer herhangi bir işaretçi türüne yükseltilir.- Eklemeyi unutursanız bir hatayı gizleyebilir
<stdlib.h>
. Bu, çökmelere neden olabilir. - İşaretçiler ve tamsayılar farklı boyuttaysa, çevrim yaparak bir uyarıyı gizlersiniz ve döndürülen adresinizin bitlerini kaybedebilirsiniz.
- Göstericinin türü bildiriminde değiştirilirse
malloc
, çağrılan ve atılan tüm satırların da değiştirilmesi gerekebilir .
Öte yandan, döküm programınızın taşınabilirliğini artırabilir. yani, bir C programının veya işlevinin C ++ olarak derlenmesine izin verir.
Boş göstericinin arkasındaki kavram, herhangi bir veri türüne dönüştürülebilmesidir, bu nedenle malloc void döndürür. Ayrıca otomatik yazımın farkında olmalısınız. Bu yüzden yapmanız gerekse de işaretçiyi atmanız zorunlu değildir. Kodun temiz tutulmasına yardımcı olur ve hata ayıklamaya yardımcı olur
Bir void işaretçisi, genel bir göstericidir ve C, bir geçersiz işaretçi tipinden diğer türlere örtük dönüşümü destekler, bu nedenle, onu açık bir şekilde yazmaya gerek yoktur.
Bununla birlikte, aynı kodun, örtük dönüştürmeyi desteklemeyen bir C ++ platformunda mükemmel bir şekilde uyumlu çalışmasını istiyorsanız, tipleme işlemini yapmanız gerekir, bu yüzden hepsi kullanılabilirliğe bağlıdır.
Diğer belirtildiği gibi, C için değil, C ++ için gerekli.
Dökümün dahil edilmesi, bir C programının veya işlevinin C ++ olarak derlenmesine izin verebilir.
C'de, void * otomatik olarak ve güvenli bir şekilde başka herhangi bir işaretçi tipine yükseltildiğinden gereksizdir.
Ancak o zaman yayınlarsanız , stdlib.h'yi eklemeyi unutursanız bir hatayı gizleyebilir . Bu, çökmelere neden olabilir (veya daha kötüsü, kodun tamamen farklı bir bölümünde çok sonraya kadar bir çökmeye neden olmaz).
Çünkü stdlib.h içeren malloc'dan prototipi bulunur. Malloc için bir prototipin yokluğunda, standart C derleyicisinin malloc'un bir int döndürdüğünü varsaymasını gerektirir. Atama yoksa, işaretçiye bu tamsayı atandığında bir uyarı verilir; ancak, cast ile, bu uyarı üretilmez ve bir hatayı gizler.
Bu soru fikir temelli kötüye kullanım konusudur.
Bazen böyle yorumlar fark ediyorum:
Malloc'un sonucunu atmayın
veya
Neden malloc'un sonucunu yayınlamıyorsun?
OP'nin döküm kullandığı sorular hakkında. Yorumların kendisi bu soruya bir köprü içerir.
Bu da olası herhangi bir şekilde uygunsuz ve yanlıştır. Gerçekten kişinin kendi kodlama tarzına bağlıyken doğru ve yanlış yoktur.
Bu neden oluyor?
İki nedene dayanmaktadır:
Bu soru aslında fikir temelli. Teknik olarak, sorunun yıllar önce kanaate dayalı olarak kapatılması gerekirdi. " Yapar mıyım " veya " Yapmamalı mıyım " veya eşdeğeri " Yapmalı mıyım " veya " Yapmamalı mıyım " sorusu, kişinin kendi fikrine dayalı bir tutum olmadan odaklanarak cevap veremezsiniz. Bir soruyu kapatmanın nedenlerinden biri, burada iyi gösterildiği gibi "fikir temelli yanıtlara yol açabileceğidir".
(En belirgin ve kabul edilmiş dahil birçok cevapları cevap ait @unwind ) olan ya tamamen ya da neredeyse tamamen düşünceye dayalı (eğer kötü olurdu kendini tekrar döküm veya yoksa kodunuzu eklenebilir fe gizemli "dağınıklığı") ve gösteri alçı atlamak için net ve odaklanmış bir eğilim. Bir tarafta dökümün fazlalığı hakkında tartışıyorlar ama aynı zamanda ve daha da kötüsü, programlama hatasından / hatasından kaynaklanan bir hatayı çözmeyi savunuyorlar -
#include <stdlib.h>
eğer kullanmak istiyorlarsa değilmalloc()
.
Kişisel görüşümden daha azıyla, tartışılan bazı noktalara gerçek bir bakış açısı getirmek istiyorum. Özellikle birkaç noktaya dikkat edilmelidir:
Kişinin kendi fikrine düşecek bu kadar hassas bir soru, tarafsız artıları ve eksileri olan bir cevaba ihtiyaç duyar. Sadece eksileri veya artıları değil.
Bu cevapta artı ve eksilere iyi bir genel bakış listelenmiştir:
https://stackoverflow.com/a/33047365/12139179
(Ben şahsen bunu bu nedenle şimdiye kadarki en iyi cevap olarak görüyorum.)
Oyuncu kadrosunun ihmal edilmesinin en çok karşılaşılan nedenlerinden biri, oyuncu kadrosunun bir hatayı gizleyebilmesidir.
Birisi
malloc()
, döndürenint
(örtük işlevler C99'dan beri standarttan çıkar) vesizeof(int) != sizeof(int*)
bu soruda gösterildiği gibi örtük bir beyan kullanırsaBu kod neden 64 bit mimaride segfault, ancak 32 bit üzerinde iyi çalışıyor?
oyuncu kadrosu bir hatayı gizlerdi.
Bu doğru olsa da, hikayenin yalnızca yarısını gösteriyor, çünkü oyuncu kadrosunun ihmal edilmesi, daha da büyük bir hataya sadece ileriye götüren bir çözüm olacaktı -
stdlib.h
kullanırken dahil değilmalloc()
.Bu asla ciddi bir sorun olmayacak, eğer sen,
C99 veya üstü ile uyumlu bir derleyici kullanın (bu önerilir ve zorunlu olmalıdır) ve
Kendi kodunuzda
stdlib.h
kullanmak istediğinizdemalloc()
, ki bu da büyük bir hata olan eklemeyi unutmak zorunda değilsiniz .
Bazı insanlar, C ++ 'da cast zorunlu olduğundan, C kodunun C ++ uyumluluğu hakkında tartışıyorlar.
Öncelikle genel olarak söylemek gerekirse: C kodunu C ++ derleyicisiyle derlemek iyi bir uygulama değildir.
C ve C ++ aslında farklı anlamlara sahip tamamen farklı iki dildir.
Ancak C kodunu gerçekten C ++ ile uyumlu hale getirmek istiyorsanız / buna ihtiyacınız varsa ve bunun tersi de herhangi bir döküm yerine derleyici anahtarlarını kullanın.
Oyuncu kadrosu gereksiz veya hatta zararlı olarak ilan edildiğinden, bu sorulara odaklanmak istiyorum, bu da dökümün neden yararlı ve hatta gerekli olabileceğine dair iyi nedenler veriyor:
https://stackoverflow.com/a/34094068/12139179
https://stackoverflow.com/a/36297486/12139179
https://stackoverflow.com/a/33044300/12139179
- Kodunuz, sırasıyla atanan göstericinin türü (ve bununla birlikte dönüşümün türü) değiştiğinde, çoğu durumda bu olası olmasa da, dönüştürme yararlı olmayabilir. O zaman tüm yayınları da korumanız / değiştirmeniz gerekir ve kodunuzda bellek yönetimi işlevlerine yönelik birkaç bin çağrı varsa, bu, bakım verimliliğini gerçekten özetleyebilir ve azaltabilir.
Özet:
Gerçek şu ki, atanan işaretçi temel hizalama gereksinimi olan (tüm nesnelerin çoğunu içeren) bir nesneye işaret ediyorsa, çevrim C standardına göre fazlalıktır (zaten ANSI-C (C89 / C90) 'dan beri).
Bu durumda işaretçi otomatik olarak hizalandığından, atımı yapmanız gerekmez:
"Align_alloc, calloc, malloc ve realloc işlevlerine ardışık çağrılarla ayrılan depolamanın sırası ve bitişikliği belirtilmemiştir. Tahsis başarılı olursa döndürülen işaretçi, herhangi bir nesne türüne bir işaretçiye atanabilmesi için uygun şekilde hizalanır temel bir hizalama gereksinimi ve daha sonra tahsis edilen alanda bu tür bir nesneye veya bu tür nesnelerin bir dizisine erişmek için kullanılır (alan açıkça serbest bırakılıncaya kadar). "
Kaynak: C18, §7.22.3 / 1
" Temel hizalama , geçerli hizalamadır veya buna eşittir
_Alignof (max_align_t)
. Temel hizalamalar, tüm depolama sürelerine sahip nesneler için uygulama tarafından desteklenecektir. Aşağıdaki türlerin hizalama gereksinimleri, temel hizalamalar olacaktır:- tüm atomik, nitelikli veya niteliksiz temel tipler;
- tüm atomik, nitelikli veya niteliksiz numaralandırılmış türler;
- tüm atomik, nitelikli veya niteliksiz işaretçi türleri;
- öğe türü temel bir hizalama gereksinimi olan tüm dizi türleri; 57)
- Madde 7'de tam nesne türleri olarak belirtilen tüm türler;
- tüm elemanları temel hizalama gereksinimleri olan türlere sahip olan ve hiçbir öğesinde temel bir hizalama olmayan bir hizalama belirten bir hizalama belirleyicisi olmayan tüm yapı veya birleşim türleri.
- 6.2.1'de belirtildiği gibi, sonraki beyan önceki beyanı gizleyebilir. "
Kaynak: C18, §6.2.8 / 2
Ancak, genişletilmiş hizalama gereksinimi olan uygulama tanımlı bir nesne için bellek ayırırsanız, dönüştürme gerekli olacaktır.
Bir genişletilmiş hizalama daha bir hizalama Greater ile temsil edilir
_Alignof (max_align_t)
. Herhangi bir uzatılmış hizalamanın desteklenip desteklenmediği ve desteklendikleri depolama süreleri uygulama tarafından tanımlanır. Uzatılmış bir hizalama gereksinimi olan bir tür, aşırı hizalanmış bir türdür. 58)Kaynak. C18, §6.2.8 / 3
Geri kalan her şey, belirli bir kullanım durumuna ve kişinin kendi görüşüne bağlıdır.
Lütfen kendinizi nasıl eğittiğinize dikkat edin.
Şimdiye kadar verilen tüm cevapları (ve bir başarısızlığa işaret edebilecek yorumları) dikkatlice okumanızı ve ardından sonucu malloc()
belirli bir vakaya atarsanız veya vermezseniz kendi fikrinizi oluşturmanızı öneririm .
Lütfen aklınızda bulundurun:
Bu sorunun doğru ve yanlış cevabı yok. Bu bir stil meselesidir ve hangi yolu seçeceğinize kendiniz karar verirsiniz (tabii eğitim veya iş nedeniyle mecbur kalmazsanız). Lütfen bunun farkında olun ve sizi kandırmasına izin vermeyin .
Son not: Bu soruyu görüşe dayalı olarak son zamanlarda kapatmak için oy verdim, ki bu gerçekten de yıllardır gerekli. Kapatma / yeniden açma ayrıcalığına sahipseniz, sizi de bunu yapmaya davet ediyorum.
Benim için, eve götürmek ve buradaki sonuç şudur malloc
: C'de döküm tamamen gerekli DEĞİLDİR, ancak nasıl malloc
kullanılırsa kullanılsın malloc
, yine de talep ettiğiniz kutsanmış bellek alanını size tahsis edeceği için etkilemeyecektir . Diğer bir sonuç, insanların döküm yapmalarının nedeni veya nedenlerinden biridir ve bu, aynı programı C veya C ++ 'da derlemelerini sağlamaktır.
Başka nedenler olabilir, ancak başka nedenler, neredeyse kesinlikle, er ya da geç başınızı ciddi belaya sokacaktır.
You can, but don't need to cast in C. You have to cast if that code is compiled as C++.