Apakah saya cor hasil malloc?
Dalam pertanyaan, seseorang disarankan dalam komentar bahwa saya harus tidak cast hasil dari malloc
, yaitu
int *sieve = malloc(sizeof(int) * length);
daripada:
int *sieve = (int *) malloc(sizeof(int) * length);
Mengapa hal ini bisa terjadi?
2305
26
No; anda don't cast hasilnya, karena:
void *
adalah secara otomatis dan aman dipromosikan ke lain jenis pointer dalam kasus ini.<stdlib.h>
. Hal ini dapat menyebabkan crash (atau, lebih buruk lagi, tidak penyebab kecelakaan sampai kemudian cara di beberapa benar-benar bagian yang berbeda dari kode). Pertimbangkan apa yang terjadi jika pointer dan bilangan bulat yang berbeda ukuran, kemudian anda're bersembunyi peringatan dengan casting dan mungkin kehilangan bit dari alamat kembali. Catatan: pada C99 fungsi implisit yang hilang dari C, dan titik ini adalah tidak relevan lagi karena ada's otomatis tidak ada asumsi yang dideklarasikan kembali fungsiint
.Sebagai klarifikasi, perhatikan bahwa saya mengatakan "anda don't cast" tidak "anda don't perlu untuk pemain". Dalam pendapat saya, itu's kegagalan untuk menyertakan pemain, bahkan jika anda sudah benar. Ada hanya tidak ada manfaat untuk melakukan hal itu, tetapi banyak dari potensi risiko, dan termasuk pemain menunjukkan bahwa anda don't tahu tentang risiko.
Perhatikan juga, sebagai komentator menunjukkan, bahwa di atas berbicara tentang lurus C, bukan C++. Saya sangat yakin dalam C dan C++ sebagai bahasa terpisah.
Untuk menambahkan lebih lanjut, kode anda sia-sia mengulangi jenis informasi (
int
) yang dapat menyebabkan kesalahan. It's baik untuk de-referensi pointer digunakan untuk menyimpan return value, untuk "lock" dua bersama-sama:Ini juga bergerak
panjang
ke depan untuk meningkatkan visibilitas, dan tetes berlebihan kurung denganukuran
; mereka hanya diperlukan ketika argumen adalah nama jenis. Banyak orang tampaknya tidak tahu (atau mengabaikan) ini, yang membuat kode mereka lebih verbose. Ingat:sizeof
bukan fungsi! :)Saat bergerak
panjang
untuk bagian depan mungkin meningkatkan visibilitas dalam beberapa kasus yang jarang terjadi, seseorang juga harus memperhatikan bahwa dalam kasus umum, itu harus lebih baik untuk menulis ekspresi seperti:Karena menjaga
sizeof
pertama, dalam kasus ini, memastikan perkalian ini dilakukan dengan setidaknyasize_t
matematika.Bandingkan:
malloc(sizeof *saringan * panjang * lebar)
vsmalloc(panjang * lebar * sizeof *saringan)
yang kedua mungkin meluappanjang * lebar
ketikawidth
danpanjang
adalah jenis yang lebih kecil darisize_t
.Di C, anda don't perlu untuk melemparkan kembali nilai
malloc
. Pointer untuk membatalkan kembali olehmalloc
otomatis dikonversi ke jenis yang tepat. Namun, jika anda ingin kode anda untuk compile dengan compiler C++, pemain yang dibutuhkan. Alternatif yang lebih disukai di kalangan masyarakat adalah dengan menggunakan kode berikut:yang selain itu membebaskan anda dari keharusan untuk khawatir tentang mengubah sisi kanan dari ekspresi jika anda mengubah jenis
saringan
.Gips yang buruk, karena orang-orang telah menunjukkan. Khusus pointer gips.
Anda do cast, karena:
tipe *
versustipe **
.#include
yang tepat header file merindukan hutan untuk pohon. It's sama dengan mengatakan "don't khawatir tentang fakta anda gagal untuk meminta compiler untuk mengeluh tentang tidak melihat prototipe -- yang sial stdlib.jam ini SEBENARNYA penting untuk diingat!"malloc()
bug tertangkap jauh lebih cepat ketika ada's cor. Seperti halnya dengan pernyataan-pernyataan, penjelasan yang mengungkapkan maksud mengurangi bug.Seperti yang lain menyatakan, hal ini tidak diperlukan untuk C, tapi untuk C++. Jika anda berpikir anda akan mengkompilasi kode C dengan C++ compiler, untuk alasan yang pernah, anda dapat menggunakan makro sebaliknya, seperti:
Dengan cara itu anda masih bisa menulis hal itu dalam cara yang sangat kompak:
dan itu akan mengkompilasi untuk C dan C++.
Dari Wikipedia:
Meskipun malloc tanpa casting adalah metode yang disukai dan paling berpengalaman programmer memilih, anda harus menggunakan apapun yang anda suka setelah menyadari masalah.
aku.e: Jika anda perlu untuk mengkompilasi program C karena C++ (Meskipun itu adalah bahasa yang terpisah) anda harus membuang hasil dari penggunaan
malloc
.Di C, anda dapat secara implisit mengubah void pointer ke jenis lain dari pointer, sehingga pemain tidak perlu. Menggunakan satu mungkin menyarankan untuk orang awam, bahwa ada beberapa alasan mengapa diperlukan, yang dapat menyesatkan.
Anda don't cast hasil malloc, karena hal itu menambah sia-sia kekacauan untuk kode anda.
Alasan yang paling umum mengapa orang-orang cast hasil malloc adalah karena mereka tidak yakin tentang bagaimana bahasa C bekerja. Yang's tanda peringatan: jika anda don't tahu bagaimana bahasa tertentu mekanisme kerjanya, maka don't mengambil menebak. Melihat itu atau meminta pada Stack Overflow.
Beberapa komentar:
Void pointer dapat dikonversi ke/dari setiap pointer lain jenis tanpa pemain eksplisit (C11 6.3.2.3 dan 6.5.16.1).
C++ namun tidak memungkinkan implisit cor antara
void*
dan lain penunjuk jenis. Jadi dalam C++, para pemain akan menjadi benar. Tetapi jika anda dalam program C++, anda harus menggunakanbaru
dan tidak malloc(). Dan anda harus mengkompilasi kode C dengan menggunakan compiler C++.Jika anda perlu dukungan baik C dan C++ dengan kode sumber yang sama, menggunakan compiler switch untuk menandai perbedaan. Jangan mencoba untuk sate bahasa standar dengan kode yang sama, karena mereka tidak kompatibel.
Jika C compiler tidak menemukan fungsi karena anda lupa untuk menyertakan header, anda akan mendapatkan sebuah compiler/linker kesalahan tentang itu. Jadi jika anda lupa untuk menyertakan
<stdlib.h>
yang's tidak ada masalah besar, anda tidak't dapat membangun program anda.Pada kuno compiler yang mengikuti versi standar yang lebih dari 25 tahun, lupa untuk menyertakan
<stdlib.h>
akan menghasilkan perilaku yang berbahaya. Karena itu kuno standar, fungsi tanpa terlihat prototipe secara implisit dikonversi jenis kembali keint
. Casting hasil dari malloc secara eksplisit kemudian akan menyembunyikan diri bug ini.Tapi yang ini benar-benar non-isu. Anda tidak't menggunakan 25 tahun komputer, jadi mengapa anda akan menggunakan 25 tahun compiler?
Dalam C anda mendapatkan konversi implisit dari
void*
lainnya (data) pointer.Casting nilai yang dikembalikan oleh
malloc()
adalah tidak perlu sekarang, tapi aku'd ingin menambahkan satu titik yang tampaknya tidak ada yang telah menunjukkan:Pada hari-hari kuno, yaitu, sebelum ANSI C menyediakan
void *
generik jenis pointer,char *
adalah tipe untuk penggunaan tersebut. Dalam hal ini, pemain dapat mematikan peringatan compiler.Referensi: C FAQ
Hal ini tidak wajib untuk cor hasil
malloc
, karena itu kembalivoid*
, danvoid*
dapat menunjuk ke tipe data.Hanya menambahkan pengalaman saya, belajar komputer teknik saya melihat bahwa dua atau tiga guru besar yang saya telah melihat tulisan di C selalu cor malloc, namun salah satu yang saya tanya (dengan besar CV dan pemahaman C) mengatakan kepada saya bahwa itu adalah benar-benar tidak perlu, tetapi hanya digunakan untuk menjadi benar-benar spesifik, dan untuk mendapatkan siswa menjadi mentalitas yang benar-benar spesifik. Pada dasarnya, pengecoran tidak akan mengubah apa pun dalam cara kerjanya, itu tidak persis apa yang dikatakannya, mengalokasikan memori, dan pengecoran tidak efek ini, anda mendapatkan memori yang sama, dan bahkan jika anda melemparkan itu ke sesuatu yang lain dengan kesalahan (dan entah bagaimana menghindari kesalahan compiler) C akan mengaksesnya dengan cara yang sama.
Edit: Casting memiliki titik tertentu. Ketika anda menggunakan notasi array, kode yang dihasilkan harus tahu berapa banyak memori tempat-tempat itu untuk maju untuk mencapai awal dari elemen berikutnya, hal ini dicapai melalui casting. Dengan cara ini anda tahu bahwa untuk double kau pergi 8 byte ke depan sementara untuk int kau pergi 4, dan seterusnya. Dengan demikian tidak memiliki efek jika anda menggunakan notasi pointer, dalam notasi array menjadi perlu.
Ini adalah apa yang GNU C Library Referensi mengatakan:
Dan memang ISO C11 standar (p347) mengatakan demikian:
Void pointer adalah sebuah objek generik pointer dan C mendukung konversi implisit dari kekosongan tipe pointer untuk tipe lainnya, sehingga tidak perlu secara eksplisit typecasting itu.
Namun, jika anda ingin kode yang sama bekerja dengan sempurna kompatibel pada C++, platform, yang tidak mendukung konversi implisit, yang perlu anda lakukan typecasting, jadi itu semua tergantung pada kegunaan.
Kembali tipe void*, yang dapat dilemparkan ke jenis yang diinginkan dari data pointer agar dapat dereferenceable.
Dalam bahasa C, sebuah void pointer dapat ditugaskan untuk setiap pointer, yang mengapa anda tidak harus menggunakan jenis cast. Jika anda ingin "type yang aman" alokasi, saya dapat merekomendasikan berikut fungsi makro, yang selalu saya gunakan dalam proyek C:
Dengan ini di tempat anda hanya bisa mengatakan
Untuk non-array dinamis, yang ketiga harus-memiliki fungsi makro adalah
yang membuat array loop lebih aman dan lebih nyaman:
Hal ini tergantung pada bahasa pemrograman dan compiler. Jika anda menggunakan
malloc
di C tidak ada kebutuhan untuk tipe cast itu, karena akan secara otomatis tipe cast, Namun jika anda menggunakan C++ maka anda harus jenis cast karenamalloc
akan kembalivoid*
jenis.Orang-orang digunakan untuk GCC dan Dentang yang manja. It's tidak semua yang baik di luar sana.
Saya telah cukup ngeri selama bertahun-tahun oleh bertumbuhnya usia penyusun I've been diperlukan untuk menggunakan. Seringkali perusahaan dan manajer mengadopsi ultra-konservatif pendekatan untuk mengubah compiler dan bahkan tidak akan tes jika compiler baru ( dengan baik sesuai standar dan kode optimization ) akan bekerja dalam sistem mereka. Dalam kenyataan praktis untuk bekerja pengembang adalah bahwa ketika anda're coding yang anda butuhkan untuk menutupi basis anda dan, sayangnya, casting mallocs adalah kebiasaan yang baik jika anda tidak dapat mengontrol apa yang penyusun dapat diterapkan untuk kode anda.
Saya juga akan menunjukkan bahwa banyak organisasi menerapkan standar pengkodean mereka sendiri dan bahwa yang harus dengan metode orang-orang yang mengikuti jika didefinisikan. Dengan tidak adanya eksplisit bimbingan saya cenderung untuk pergi untuk yang paling mungkin untuk mengkompilasi di mana-mana, bukan budak kepatuhan terhadap standar.
Argumen bahwa itu's tidak diperlukan di bawah standar saat ini cukup valid. Tetapi argumen yang menghilangkan praktik dunia nyata. Kami tidak kode dalam dunia yang dikuasai secara eksklusif oleh standar hari, tapi dengan kepraktisan apa yang saya suka menyebutnya "manajemen lokal's kenyataan lapangan". Dan yang's membungkuk dan memutar lebih dari ruang waktu yang pernah ada. :-)
YMMV.
Saya cenderung berpikir casting malloc sebagai defensif operasi. Tidak cukup, tidak sempurna, tetapi umumnya aman. ( Jujur saja, jika anda've tidak termasuk stdlib.jam maka anda've cara lebih banyak masalah daripada casting malloc ! ).
Tidak, anda don't cast hasil dari
malloc()
.Secara umum, anda *don't cast ke atau dari `void `**.
Khas diberikan alasan untuk tidak melakukannya adalah bahwa kegagalan untuk
#include <stdlib.h>
bisa pergi tanpa diketahui. Ini isn't masalah lagi untuk waktu yang lama sekarang sebagai C99 dibuat deklarasi fungsi implisit kegiatan ilegal, jadi jika anda compiler yang sesuai untuk setidaknya C99, anda akan mendapatkan pesan diagnostik.Tapi ada's a banyak alasan kuat tidak untuk memperkenalkan tidak perlu pointer gips:
Dalam C, pointer cor hampir selalu error. Hal ini karena aturan berikut (§6.5 p7 di N1570, draft terbaru untuk C11):
Hal ini juga dikenal sebagai ketat aliasing aturan. Jadi kode berikut ini undefined perilaku:
Dan, kadang-kadang mengejutkan, berikut ini juga:
Kadang-kadang, anda do perlu untuk cor pointer, tetapi mengingat ketat aliasing aturan, anda harus sangat berhati-hati dengan itu. Jadi, setiap kejadian dari suatu pointer cor dalam kode anda adalah tempat anda harus periksa untuk keabsahannya. Oleh karena itu, anda tidak pernah menulis tidak perlu pointer cor.
tl;dr
Singkatnya: Karena di C, semua terjadinya pointer cor harus menaikkan bendera merah untuk kode yang memerlukan perhatian khusus, anda harus tidak pernah menulis tidak perlu pointer gips.
Side catatan:
int x = 5; printf("%p\n", (void *)&x);
Pemain diperlukan di sini, karena
printf()
adalah variadic fungsi, sehingga konversi implisit don't bekerja.void *
ini tidak implisit. C++ memiliki seluruh rangkaian rasa yang berbeda dari casting.Aku dimasukkan ke dalam cast hanya untuk menunjukkan ketidaksetujuan dari lubang jelek dalam jenis sistem, yang memungkinkan kode seperti cuplikan berikut untuk mengkompilasi tanpa diagnostik, meskipun tidak ada gips yang digunakan untuk membawa tentang konversi buruk:
Saya berharap bahwa tidak't ada (dan itu doesn't dalam C++) dan jadi saya cor. Itu merupakan selera saya, dan saya pemrograman politik. I'm tidak hanya casting pointer, tapi efektif, casting pemungutan suara, dan mengusir setan-setan dari kebodohan. Jika aku bisa't benar-benar mengusir kebodohan, maka setidaknya biarkan aku mengungkapkan keinginan untuk melakukannya dengan gerakan protes.
Pada kenyataannya, sebuah praktek yang baik adalah untuk membungkus
malloc
(dan teman-teman) dengan fungsi yang kembaliunsigned char *
, dan pada dasarnya tidak pernah menggunakanvoid *
dalam kode anda. Jika anda membutuhkan generic pointer-ke-semua-objek, menggunakanchar *
atauunsigned char *
, dan gips di kedua arah. Salah satu relaksasi yang dapat memanjakan, mungkin, adalah menggunakan fungsi-fungsi sepertimemset
danmemcpy
tanpa gips.Pada topik casting dan C++ kompatibilitas, jika anda menulis kode anda sehingga mengkompilasi sebagai investasi C dan C++ (dalam hal ini anda harus cast yang mengembalikan nilai
malloc
ketika menetapkan hal ini untuk sesuatu yang lain darivoid *
), anda bisa melakukan hal yang sangat membantu untuk diri anda sendiri: anda dapat menggunakan makro untuk pengecoran yang menerjemahkan ke C++ style gips ketika menyusun seperti C++, tetapi mengurangi C cor ketika menyusun seperti C:Jika anda mematuhi makro tersebut, maka sederhana
grep
pencarian anda untuk kode dasar untuk pengidentifikasi ini akan menunjukkan anda di mana anda gips, sehingga anda dapat memeriksa apakah ada dari mereka yang salah.Kemudian, ke depan, jika anda secara teratur mengkompilasi kode C++, itu akan memberlakukan penggunaan yang tepat cast. Misalnya, jika anda menggunakan
strip_qual
hanya untuk menghilangkanconst
atauvolatile
, tetapi program perubahan sedemikian rupa sehingga jenis konversi kini terlibat, anda akan mendapatkan diagnostik, dan anda akan memiliki untuk menggunakan kombinasi dari gips untuk mendapatkan konversi yang diinginkan.Untuk membantu anda mematuhi makro ini, the GNU C++ (tidak C!) compiler memiliki fitur yang indah: opsional diagnostik yang dihasilkan untuk semua kejadian C gaya gips.
Jika anda mengkompilasi kode C seperti C++, anda dapat menggunakan ini
-Wold-gaya-cor
pilihan untuk mengetahui semua kejadian(jenis)
casting sintaks yang dapat merayap ke dalam kode, dan tindak lanjut pada diagnosa ini dengan menggantinya dengan pilihan yang tepat dari kalangan atas macro (atau kombinasi, jika diperlukan).Pengobatan ini konversi tunggal terbesar mandiri teknis pembenaran untuk bekerja di sebuah "Bersih C": gabungan C dan C++ dialek, yang pada gilirannya secara teknis membenarkan casting nilai kembali dari
malloc
.Saya lebih memilih untuk melakukan cast, tapi tidak secara manual. Favorit saya adalah menggunakan
g_new
dang_new0
macro dari glib. Jika glib tidak digunakan, saya akan menambahkan yang sama macro. Mereka makro mengurangi duplikasi kode tanpa mengorbankan safety tipe. Jika anda mendapatkan jenis yang salah, anda akan mendapatkan implisit cor antara non-void pointer, yang akan menyebabkan peringatan (error dalam C++). Jika anda lupa untuk menyertakan header yang mendefinisikang_new
dang_new0
, anda akan mendapatkan error.g_new
dang_new0
kedua mengambil argumen yang sama, tidak sepertimalloc
yang mengambil lebih sedikit argumen daricalloc
. Cukup tambahkan0
untuk mendapatkan nol-diinisialisasi memori. Kode dapat di-compile dengan compiler C++ yang tanpa perubahan.