Sekarang sebelum orang mulai menandai ini sebagai dup, saya telah membaca semua yang berikut, tidak ada yang memberikan jawaban yang saya cari:
- C FAQ: Apa yang salah dengan nilai pengembalian malloc?
- SO: Haruskah saya secara eksplisit menggunakan nilai kembalian malloc ()?
- SO: Pointer-cast yang tidak perlu di C
- SO: Apakah saya memberikan hasil malloc?
C FAQ dan banyak jawaban atas pertanyaan di atas mengutip kesalahan misterius yang malloc
dapat disembunyikan oleh nilai pengembalian casting ; namun, tidak satupun dari mereka memberikan contoh spesifik dari kesalahan seperti itu dalam praktiknya. Sekarang perhatikan bahwa saya mengatakan kesalahan , bukan peringatan .
Sekarang diberi kode berikut:
#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;
}
Mengompilasi kode di atas dengan gcc 4.2, dengan dan tanpa cast memberikan peringatan yang sama, dan program dijalankan dengan benar dan memberikan hasil yang sama pada kedua kasus.
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
Jadi adakah yang bisa memberikan contoh kode spesifik dari kompilasi atau runtime error yang dapat terjadi karena malloc
nilai kembalian casting , atau apakah ini hanya legenda urban?
Sunting Saya menemukan dua argumen yang ditulis dengan baik mengenai masalah ini:
- Mendukung Casting: CERT Advisory: Segera mentransmisikan hasil panggilan fungsi alokasi memori ke pointer ke tipe yang dialokasikan
- Against Casting (kesalahan 404 per 2012-02-14: gunakan salinan Internet Archive Wayback Machine dari 2010-01-27. {2016-03-18: "Halaman tidak dapat dirayapi atau ditampilkan karena robots.txt."})
void
pointer memungkinkan untuk mengkompilasi kode sebagai C ++; beberapa orang mengatakan itu fitur, menurut saya ini bug;)malloc
nilai kembalian casting: casting keint*
pada arch 64-bit.C
tidak diberi tagC++
(keduanya adalah bahasa yang berbeda) Jadi diskusi apa pun (seperti pada beberapa jawaban) tidak relevan dengan pertanyaan ini.Jawaban:
Anda tidak akan mendapatkan kesalahan kompilator , tetapi peringatan kompilator . Seperti yang dikatakan sumber yang Anda kutip (terutama yang pertama ), Anda bisa mendapatkan error runtime yang tidak dapat diprediksi saat menggunakan cast tanpa menyertakannya
stdlib.h
.Jadi kesalahan di pihak Anda bukanlah pada pemerannya, melainkan lupa untuk dimasukkan
stdlib.h
. Penyusun dapat menganggap bahwa itumalloc
adalah fungsi yang kembaliint
, oleh karena itu mengonversivoid*
penunjuk yang sebenarnya dikembalikan olehmalloc
keint
dan kemudian ke jenis penunjuk Anda karena cast eksplisit. Pada beberapa platform,int
dan pointer dapat mengambil jumlah byte yang berbeda, sehingga jenis konversi dapat menyebabkan kerusakan data.Untungnya, kompiler modern memberikan peringatan yang mengarah ke kesalahan Anda yang sebenarnya. Lihat
gcc
keluaran yang Anda berikan: Ini memperingatkan Anda bahwa deklarasi implisit (int malloc(int)
) tidak kompatibel dengan bawaanmalloc
. Jadigcc
sepertinya tahumalloc
bahkan tanpastdlib.h
.Meninggalkan pemeran untuk mencegah kesalahan ini sebagian besar merupakan alasan yang sama seperti menulis
if (0 == my_var)
dari pada
if (my_var == 0)
karena yang terakhir dapat menyebabkan bug yang serius jika ada yang bingung
=
dan==
, sedangkan yang pertama akan menyebabkan kesalahan kompilasi. Saya pribadi lebih suka gaya yang terakhir karena lebih mencerminkan niat saya dan saya tidak cenderung melakukan kesalahan ini.Hal yang sama berlaku untuk casting nilai yang dikembalikan oleh
malloc
: Saya lebih suka eksplisit dalam pemrograman dan saya biasanya memeriksa ulang untuk memasukkan file header untuk semua fungsi yang saya gunakan.sumber
stdlib.h
sudah merupakan kesalahan dengan sendirinya, meskipun Anda hanya mendapatkan peringatan "deklarasi implisit".Salah satu argumen tingkat tinggi yang baik terhadap casting hasil
malloc
sering tidak disebutkan, meskipun, menurut pendapat saya, itu lebih penting daripada masalah tingkat rendah yang terkenal (seperti memotong penunjuk ketika deklarasi hilang).Praktik pemrograman yang baik adalah menulis kode, yang sebisa mungkin tidak tergantung tipe. Artinya, secara khusus, nama tipe harus disebutkan dalam kode sesedikit mungkin atau sebaiknya tidak disebutkan sama sekali. Ini berlaku untuk cast (hindari cast yang tidak perlu), jenis sebagai argumen
sizeof
(hindari menggunakan nama jenis dalamsizeof
) dan, umumnya, semua referensi lain untuk nama jenis.Nama jenis termasuk dalam deklarasi. Sebisa mungkin, nama tipe harus dibatasi pada deklarasi dan hanya pada deklarasi.
Dari sudut pandang ini, sedikit kode ini buruk
int *p; ... p = (int*) malloc(n * sizeof(int));
dan ini jauh lebih baik
int *p; ... p = malloc(n * sizeof *p);
bukan hanya karena "tidak mentransmisikan hasil dari
malloc
", melainkan karena ini adalah tipe-independen (atau tipe-agnositic, jika Anda lebih suka), karena secara otomatis menyesuaikan diri dengan tipe apa punp
yang dideklarasikan dengan, tanpa memerlukan intervensi apa pun dari pengguna.sumber
Fungsi non-prototipe diasumsikan kembali
int
.Jadi Anda mentransmisikan
int
ke sebuah pointer. Jika petunjuk lebih luas dariint
pada platform Anda, ini adalah perilaku yang sangat berisiko.Ditambah, tentu saja, beberapa orang menganggap peringatan sebagai kesalahan, yaitu kode harus dikompilasi tanpa mereka.
Secara pribadi, saya pikir fakta bahwa Anda tidak perlu mentransmisikan
void *
ke tipe penunjuk lain adalah fitur di C, dan menganggap kode yang tidak rusak.sumber
void*
.int
." - Apakah yang Anda maksud adalah mungkin untuk mengubah tipe kembalian dari fungsi non-prototipe?#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
. Sekarang, mengapa Anda ingin memanggil malloc dalam fungsi sebaris statis saya benar-benar tidak tahu, tetapi header yang berfungsi di keduanya hampir tidak pernah terdengar.Jika Anda melakukan ini saat mengompilasi dalam mode 64-bit, pointer yang Anda kembalikan akan dipotong menjadi 32-bit.
EDIT: Maaf terlalu singkat. Berikut adalah contoh fragmen kode untuk tujuan diskusi.
Misalkan penunjuk heap yang dikembalikan adalah sesuatu yang lebih besar dari apa yang dapat direpresentasikan dalam int, katakanlah 0xAB00000000.
Jika malloc tidak dibuat prototipe untuk mengembalikan pointer, nilai int yang dikembalikan awalnya akan berada di beberapa register dengan semua bit signifikan yang ditetapkan. Sekarang kompilator berkata, "oke, bagaimana cara mengubah dan int menjadi pointer". Itu akan menjadi salah satu ekstensi tanda atau ekstensi nol dari 32-bit orde rendah yang telah diberitahu malloc "kembali" dengan menghilangkan prototipe. Karena int ditandatangani, saya pikir konversinya akan menjadi ekstensi tanda, yang dalam hal ini akan mengubah nilai menjadi nol. Dengan nilai kembali 0xABF0000000 Anda akan mendapatkan penunjuk bukan nol yang juga akan menyenangkan saat Anda mencoba membedakannya.
sumber
Aturan Perangkat Lunak yang Dapat Digunakan Kembali:
Dalam kasus penulisan fungsi inline yang menggunakan malloc (), agar dapat digunakan kembali untuk kode C ++ juga, lakukan pengecoran tipe eksplisit (mis. (Char *)); jika tidak, kompilator akan mengeluh.
sumber
malloc
/free
untuk kedua cenderung untuk menjadi lebih baik daripada mencoba untuk menggunakanmalloc
di C dannew
C ++, terutama jika struktur data dibagi antara C dan C ++ kode dan ada kemungkinan bahwa objek mungkin dibuat dalam kode C dan dirilis dalam kode C ++ atau sebaliknya.Sebuah pointer kosong di C dapat diberikan ke pointer apapun tanpa cast eksplisit. Compiler akan memberikan peringatan tetapi dapat digunakan kembali di C ++ dengan mentransmisikan tipe
malloc()
ke tipe yang sesuai. Tanpa pengecoran tipe juga dapat digunakan di C , karena C tidak ada pengecekan tipe yang ketat . Tetapi C ++ secara ketat memeriksa tipe sehingga diperlukan untuk mengetikkan castmalloc()
di C ++.sumber