Menariknya, ini adalah pertanyaan yang hampir persis sama dengan stackoverflow.com/questions/1637332/static-const-vs-define . Satu-satunya perbedaan adalah bahwa pertanyaan itu adalah tentang C ++, dan ini adalah tentang C. Karena jawaban saya adalah khusus C ++, saya katakan itu membuat mereka tidak identik, tetapi yang lain mungkin tidak setuju.
TED
53
Tidak identik, pasti. Ada banyak area di mana C ++ memungkinkan sintaks C untuk alasan kompatibilitas. Dalam kasus tersebut, pertanyaan seperti "apa cara terbaik untuk melakukan X" akan memiliki jawaban berbeda dalam C ++. Misalnya inisialisasi objek.
Bagaimana ini bukan berdasarkan opini? Mereka masing-masing memiliki tujuan yang berbeda
Sam Hammamy
1
@RobertSsupportsMonicaCellio, Ya. Terima kasih atas intimasinya
Vijay
Jawaban:
690
Tergantung pada apa yang Anda butuhkan nilainya. Anda (dan semua orang sejauh ini) menghilangkan alternatif ketiga:
static const int var = 5;
#define var 5
enum { var = 5 };
Mengabaikan masalah tentang pilihan nama, maka:
Jika Anda harus melewatkan pointer, Anda harus menggunakan (1).
Karena (2) tampaknya merupakan pilihan, Anda tidak perlu melewati petunjuk.
Baik (1) dan (3) memiliki simbol di tabel simbol debugger - yang membuat proses debug lebih mudah. Kemungkinan besar (2) tidak memiliki simbol, membuat Anda bertanya-tanya apa itu.
(1) tidak dapat digunakan sebagai dimensi untuk array pada lingkup global; keduanya (2) dan (3) bisa.
(1) tidak dapat digunakan sebagai dimensi untuk array statis pada ruang lingkup fungsi; keduanya (2) dan (3) bisa.
Di bawah C99, semua ini dapat digunakan untuk array lokal. Secara teknis, menggunakan (1) akan menyiratkan penggunaan VLA (variabel-panjang array), meskipun dimensi yang dirujuk oleh 'var' tentu saja akan tetap pada ukuran 5.
(1) tidak dapat digunakan di tempat-tempat seperti pernyataan pergantian; keduanya (2) dan (3) bisa.
(1) tidak dapat digunakan untuk menginisialisasi variabel statis; keduanya (2) dan (3) bisa.
(2) dapat mengubah kode yang Anda tidak ingin diubah karena digunakan oleh preprocessor; baik (1) dan (3) tidak akan memiliki efek samping yang tidak terduga seperti itu.
Anda dapat mendeteksi apakah (2) telah diatur dalam preprosesor; (1) maupun (3) tidak memungkinkan.
Jadi, dalam sebagian besar konteks, lebih suka 'enum' daripada alternatif. Jika tidak, poin pertama dan terakhir cenderung menjadi faktor pengendali - dan Anda harus berpikir lebih keras jika Anda harus memuaskan keduanya sekaligus.
Jika Anda bertanya tentang C ++, maka Anda akan menggunakan opsi (1) - konstanta statis - setiap saat.
daftar fantastis! Salah satu kekurangannya enumadalah mereka diimplementasikan sebagai int([C99] 6.7.2.2/3). A #definememungkinkan Anda menentukan tanda unsigned dan long with Udan Lsuffix, dan constmemungkinkan Anda memberikan tipe. enumdapat menyebabkan masalah dengan konversi jenis yang biasa.
Gauthier
37
(2) orang SELALU mengeluh tentang keamanan jenis. Saya tidak pernah mengerti mengapa tidak hanya menggunakan "#define var ((int) 5)" "dan hore Anda mendapatkan tipe safety dengan define.
Ingo Blackman
6
@RedX: Anda harus berada di lingkungan yang sangat aneh untuk ruang yang menjadi perhatian. Yang mengatakan, tidak enumjuga tidak #definemenggunakan ruang ekstra, per se. Nilai akan muncul dalam kode objek sebagai bagian dari instruksi daripada dialokasikan penyimpanan di segmen data atau di tumpukan atau di tumpukan. Anda akan memiliki beberapa ruang yang dialokasikan untuk static const int, tetapi kompiler mungkin mengoptimalkannya jika Anda tidak mengambil alamat.
Jonathan Leffler
15
'Suara' lain untuk enums (dan static const): mereka tidak dapat diubah. a definebisa di #undefinemana a enumdan static constditetapkan ke nilai yang diberikan.
Daan Timmer
15
@QED: Tidak, terima kasih. Konstanta sederhana aman di luar tanda kurung. Atau, perlihatkan kepada saya bagaimana program yang dapat dikompilasi secara sah akan diubah dengan tidak memiliki tanda kurung dalam tanda kurung. Jika itu argumen untuk makro fungsi-gaya, atau jika ada operator dalam ekspresi, maka Anda akan salah menyalahkan saya seandainya saya tidak menyertakan tanda kurung. Tapi bukan itu masalahnya di sini.
Jonathan Leffler
282
Secara umum:
staticconst
Karena itu menghormati ruang lingkup dan tipe-aman.
Satu-satunya peringatan yang bisa saya lihat: jika Anda ingin variabel yang mungkin didefinisikan pada baris perintah. Masih ada alternatif:
#ifdef VAR // Very bad name, not long enough, too general, etc..staticintconst var = VAR;#elsestaticintconst var =5;// default value#endif
Kapan pun memungkinkan, alih-alih makro / elipsis, gunakan alternatif yang aman untuk jenis.
Jika Anda benar-benar PERLU untuk pergi dengan makro (misalnya, Anda inginkan __FILE__atau __LINE__), maka Anda sebaiknya memberi nama makro Anda SANGAT hati-hati: dalam konvensi penamaannya, Boost merekomendasikan semua huruf besar, dimulai dengan nama proyek (di sini BOOST_ ), sambil membaca dengan teliti perpustakaan Anda akan melihat ini (umumnya) diikuti dengan nama area tertentu (perpustakaan) kemudian dengan nama yang bermakna.
Setuju - juga dengan #define ada bahaya umum kode mangling karena preprocessor tidak mengetahui sintaks.
NeilDurant
10
Lebih baik menggunakan # jika daripada # jika, tetapi sebaliknya saya setuju. +1.
Tim Post
58
Ini adalah penginjilan standar C ++. Jawaban di bawah ini JAUH lebih jelas dalam menjelaskan apa opsi sebenarnya dan artinya. Secara khusus: Saya hanya punya masalah dengan "const statis". Seseorang menggunakannya untuk mendefinisikan sekitar 2000 "konstanta" dalam file header. Kemudian file header ini dimasukkan dalam sekitar 100 file ".c" dan ".cpp". => 8Mbytes untuk "consts". Bagus. Ya saya tahu bahwa Anda mungkin menggunakan tautan untuk menghapus konstanta yang tidak direferensikan, tetapi ini masih membuat Anda "konstanta" mana yang direferensikan. Kehabisan ruang, apa yang salah dengan jawaban ini.
Ingo Blackman
2
@IngoBlackman: Dengan kompiler yang bagus, hanya mereka staticyang alamatnya diambil yang akan tetap; dan jika alamat itu diambil seseorang tidak bisa menggunakan #defineatau enum(tidak ada alamat) ... jadi saya benar-benar gagal melihat alternatif apa yang bisa digunakan. Jika Anda dapat menghapus "kompilasi evaluasi waktu", Anda mungkin mencari extern const.
Matthieu M.
15
@ Tim Post: #ifmungkin lebih lebih #ifdefuntuk bendera boolean, tapi dalam hal ini akan membuat tidak mungkin untuk menentukan varsebagai 0dari baris perintah. Jadi dalam hal ini, #ifdeflebih masuk akal, asalkan 0merupakan nilai legal untuk var.
Maarten
108
Di C, khususnya? Dalam C jawaban yang benar adalah: gunakan #define(atau, jika sesuai, enum)
Meskipun menguntungkan untuk memiliki scoping dan mengetik properti suatu constobjek, pada kenyataannya constobjek dalam C (sebagai lawan dari C ++) bukanlah konstanta yang benar dan oleh karena itu biasanya tidak berguna dalam kebanyakan kasus praktis.
Jadi, dalam C pilihan harus ditentukan oleh bagaimana Anda berencana untuk menggunakan konstanta Anda. Misalnya, Anda tidak bisa menggunakan const intobjek sebagai caselabel (sementara makro akan berfungsi). Anda tidak bisa menggunakan const intobjek sebagai lebar bidang-bit (selagi makro akan berfungsi). Di C89 / 90 Anda tidak bisa menggunakan constobjek untuk menentukan ukuran array (sementara makro akan bekerja). Bahkan di C99 Anda tidak dapat menggunakan constobjek untuk menentukan ukuran array saat Anda membutuhkan array non- VLA .
Jika ini penting bagi Anda maka itu akan menentukan pilihan Anda. Sebagian besar waktu, Anda tidak akan punya pilihan selain menggunakan #definedalam C. Dan jangan lupa alternatif lain, yang menghasilkan konstanta benar dalam C - enum.
Dalam C ++ constobjek adalah konstanta yang benar, jadi dalam C ++ hampir selalu lebih baik untuk memilih constvarian (tidak perlu eksplisit staticdalam C ++ sekalipun).
"Anda tidak dapat menggunakan objek const int sebagai label kasus (sementara makro akan bekerja)" ---> Mengenai pernyataan ini saya menguji variabel const int dalam C di switch-case itu berfungsi ....
john
8
@ John: Ya, Anda perlu memberikan kode yang Anda uji dan beri nama kompiler tertentu. Menggunakan const intobjek dalam label kasus adalah ilegal di semua versi bahasa C. (Tentu saja, kompiler Anda bebas untuk mendukungnya sebagai ekstensi bahasa mirip C ++ yang tidak standar.)
AnT
11
"... dan karena itu biasanya tidak berguna dalam kebanyakan kasus praktis ." Saya tidak setuju. Mereka sangat berguna selama Anda tidak perlu menggunakan nama sebagai ekspresi konstan. Kata "konstan" dalam C berarti sesuatu yang dapat dievaluasi pada waktu kompilasi; constberarti hanya baca. const int r = rand();sangat legal.
Keith Thompson
Dalam c ++, lebih baik digunakan constexprdibandingkan constdengan stlwadah khusus seperti arrayatau bitset.
Mayukh Sarkar
1
@ John Anda harus menguji dalam switch()pernyataan, bukan dalam casesatu. Saya baru saja terjebak dengan yang ini juga ☺
Hi-Angel
32
Perbedaan antara static constdan #defineyang pertama menggunakan memori dan yang berikutnya tidak menggunakan memori untuk penyimpanan. Kedua, Anda tidak bisa memasukkan alamat #definesedangkan Anda dapat melewati alamat a static const. Sebenarnya itu tergantung pada keadaan apa kita, kita perlu memilih satu di antara keduanya. Keduanya dalam kondisi terbaik dalam situasi yang berbeda. Tolong jangan berasumsi bahwa yang satu lebih baik dari yang lain ... :-)
Jika itu yang terjadi, Dennis Ritchie akan menyimpan yang terbaik sendirian ... hahaha ... :-)
+1 untuk menyebutkan memori, beberapa sistem tertanam masih tidak memiliki sebanyak itu, meskipun saya mungkin akan mulai menggunakan konstanta statis dan hanya mengubah ke #define jika diperlukan.
fluffyben
3
Saya baru saja mengujinya. Memang, const int menggunakan memori tambahan dibandingkan dengan #define atau enum. Karena kami memprogram sistem tertanam, kami tidak mampu membeli penggunaan memori tambahan. Jadi, kita akan kembali menggunakan #define atau enum.
Davide Andrea
2
Secara praktis itu tidak benar (lagi) bahwa consttidak menggunakan memori. GCC (diuji dengan 4.5.3 dan beberapa versi yang lebih baru) dengan mudah mengoptimalkan const intke dalam literal langsung dalam kode Anda saat menggunakan -O3. Jadi, jika Anda melakukan pengembangan yang tertanam dalam RAM rendah (mis. AVR), Anda dapat dengan aman menggunakan konstanta C jika Anda menggunakan GCC atau kompiler lain yang kompatibel. Saya belum mengujinya tetapi mengharapkan Dentang melakukan hal yang sama btw.
Raphael
19
Dalam C #definejauh lebih populer. Anda dapat menggunakan nilai-nilai itu untuk mendeklarasikan ukuran array misalnya:
#define MAXLEN 5void foo(void){int bar[MAXLEN];}
ANSI C tidak mengizinkan Anda untuk menggunakan static constdalam konteks ini sejauh yang saya tahu. Dalam C ++ Anda harus menghindari makro dalam kasus ini. Kamu bisa menulis
constint maxlen =5;void foo(){int bar[maxlen];}
dan bahkan meninggalkan statickarena hubungan internal sudah tersirat const[hanya dalam C ++].
Apa yang Anda maksud dengan "hubungan internal"? Saya dapat memiliki const int MY_CONSTANT = 5;dalam satu file dan mengaksesnya dengan yang extern const int MY_CONSTANT;lain. Saya tidak dapat menemukan info apa pun dalam standar (setidaknya C99) tentang constmengubah perilaku default "6.2.2: 5 Jika deklarasi pengidentifikasi untuk suatu objek memiliki cakupan file dan tidak ada kelas penyimpanan yang ditentukan, kaitannya adalah eksternal".
Gauthier
@ Gauthier: Maaf, tentang itu. Saya seharusnya mengatakan "sudah disiratkan oleh const sudah dalam bahasa C ++". Ini khusus untuk C ++.
sellibitze
@sellibitze senang melihat beberapa argumen di sepanjang jalan, bukannya ton PENDAPAT Jika ada bonus untuk argumen yang benar, Anda mengerti!
Paul
1
Mulai C99, cuplikan kedua Anda sah. baradalah VLA (array panjang variabel); kompiler cenderung menghasilkan kode seolah-olah panjangnya konstan.
Keith Thompson
14
Kelemahan lain dari constdalam C adalah bahwa Anda tidak dapat menggunakan nilai dalam menginisialisasi yang lain const.
staticintconst NUMBER_OF_FINGERS_PER_HAND =5;staticintconst NUMBER_OF_HANDS =2;// initializer element is not constant, this does not work.staticintconst NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND
* NUMBER_OF_HANDS;
Bahkan ini tidak bekerja dengan const karena kompilator tidak melihatnya sebagai konstanta:
staticuint8_tconst ARRAY_SIZE =16;staticint8_tconst lookup_table[ARRAY_SIZE]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};// ARRAY_SIZE not a constant!
Saya akan senang menggunakan mengetik constdalam kasus ini, jika tidak ...
Sedikit terlambat ke permainan, tetapi pertanyaan ini muncul di pertanyaan lain. Mengejar mengapa static uint8_t const ARRAY_SIZE = 16;tiba-tiba kompilasi Anda tidak lagi bisa sedikit menantang, terutama ketika #define ARRAY_SIZE 256terkubur sepuluh lapisan jauh di web header kusut. Itu semua nama topi ARRAY_SIZEmeminta masalah. Cadangan ALL_CAPS untuk makro, dan jangan pernah mendefinisikan makro yang tidak dalam bentuk ALL_CAPS.
David Hammen
@ David: saran suara, yang akan saya ikuti.
Gauthier
1
4 tahun kemudian Anda menyelamatkan saya banyak waktu mencari tahu mengapa saya tidak bisa "bersarang" const. Ini bisa lebih ditingkatkan!
Plouff
11
Jika bisa lolos begitu saja, static constpunya banyak keuntungan. Itu mematuhi prinsip-prinsip ruang lingkup normal, terlihat di debugger, dan umumnya mematuhi aturan yang dipatuhi variabel.
Namun, setidaknya dalam standar C asli, itu sebenarnya tidak konstan. Jika Anda menggunakan #define var 5, Anda dapat menulis int foo[var];sebagai deklarasi, tetapi Anda tidak dapat melakukan itu (kecuali sebagai ekstensi kompiler "dengan static const int var = 5;. Ini bukan kasus di C ++, di mana static constversi dapat digunakan di mana saja #defineversi dapat, dan saya percaya ini juga halnya dengan C99.
Namun, jangan pernah menyebutkan #definekonstanta dengan nama huruf kecil. Ini akan mengesampingkan kemungkinan penggunaan nama itu sampai akhir unit terjemahan. Konstanta makro harus dalam apa yang secara efektif namespace mereka sendiri, yang secara tradisional semua huruf kapital, mungkin dengan awalan.
Sayangnya, tidak demikian halnya dengan C99. constdi C99 masih belum konstan nyata. Anda dapat mendeklarasikan ukuran array dengan constdalam C99, tetapi hanya karena C99 mendukung Array Panjang Variabel. Karena alasan ini, ini hanya akan berfungsi jika VLA diizinkan. Misalnya, bahkan di C99, Anda masih tidak bisa menggunakan a constuntuk menyatakan ukuran array anggota di a struct.
AnT
Meskipun benar bahwa C99 tidak akan membiarkan Anda melakukan itu, GCC (diuji dengan 4.5.3) akan membiarkan Anda menginisialisasi array dengan const intukuran seolah-olah itu adalah konstanta C ++ atau makro. Apakah Anda ingin bergantung pada penyimpangan GCC dari standar ini tentu saja merupakan pilihan Anda, saya pribadi akan menggunakannya kecuali Anda benar-benar dapat mengabaikan menggunakan kompiler lain daripada GCC atau Dentang, yang terakhir memiliki fitur yang sama di sini (diuji dengan Dentang 3.7).
Raphael
7
Itu SELALU lebih baik menggunakan const, daripada #define. Itu karena const diperlakukan oleh compiler dan #define oleh preprocessor. Ini seperti #define sendiri bukan bagian dari kode (secara kasar).
Contoh:
#define PI 3.1416
Nama simbolis PI mungkin tidak pernah dilihat oleh kompiler; mungkin dihapus oleh preprocessor sebelum kode sumber bahkan sampai ke kompiler. Akibatnya, nama PI mungkin tidak dimasukkan ke dalam tabel simbol. Ini bisa membingungkan jika Anda mendapatkan kesalahan selama kompilasi yang melibatkan penggunaan konstanta, karena pesan kesalahan dapat merujuk ke 3.1416, bukan PI. Jika PI didefinisikan dalam file header yang tidak Anda tulis, Anda tidak akan tahu dari mana 3,1416 berasal.
Masalah ini juga dapat muncul dalam debugger simbolis, karena, sekali lagi, nama yang Anda pemrograman mungkin tidak ada dalam tabel simbol.
Preprocessor akan menggantinya dan kode tidak akan dikompilasi. Untuk alasan ini, gaya pengkodean tradisional menyarankan semua #definehuruf kapital konstan menggunakan untuk menghindari konflik.
tidak selalu mendefinisikan nilai konstan. Beberapa kompiler (misalnya tcc 0.9.26 ) hanya mengalokasikan memori yang diidentifikasi dengan nama "const_value". Menggunakan pengenal "const_value" Anda tidak dapat mengubah memori ini. Tetapi Anda masih bisa memodifikasi memori menggunakan pengenal lain:
constint const_value =5;int*mutable_value =(int*)&const_value;*mutable_value =3;
printf("%i", const_value);// The output may be 5 or 3, depending on the compiler.
Ini artinya definisi
#define CONST_VALUE 5
adalah satu-satunya cara untuk mendefinisikan nilai konstan yang tidak dapat dimodifikasi dengan cara apa pun.
Mengubah nilai konstan menggunakan pointer adalah perilaku yang tidak terdefinisi. Jika Anda ingin pergi ke sana, #definejuga dapat dimodifikasi, dengan mengedit kode mesin.
ugoren
Anda sebagian benar. Saya menguji kode dengan Visual Studio 2012 dan mencetak 5. Tetapi orang tidak dapat memodifikasi #definekarena ini adalah makro preprosesor. Itu tidak ada dalam program biner. Jika seseorang ingin memodifikasi semua tempat CONST_VALUEyang digunakan, orang harus melakukannya satu per satu.
user2229691
3
@ugoren: Misalkan Anda menulis #define CONST 5, maka if (CONST == 5) { do_this(); } else { do_that(); }, dan kompiler menghilangkan elsecabang. Bagaimana Anda mengusulkan untuk mengedit kode mesin untuk mengubah CONSTke 6?
Keith Thompson
@KeithThompson, saya tidak pernah mengatakan itu bisa dilakukan dengan mudah dan andal. Hanya saja itu #definetidak tahan peluru.
ugoren
3
@ugoren: Maksud saya adalah "mengedit kode mesin" bukan cara yang masuk akal untuk menduplikasi efek mengubah nilai a #define. Satu-satunya cara nyata untuk melakukannya adalah dengan mengedit kode sumber dan mengkompilasi ulang.
Keith Thompson
4
Meskipun pertanyaannya adalah tentang bilangan bulat, perlu dicatat bahwa #define dan enum tidak berguna jika Anda membutuhkan struktur atau string yang konstan. Keduanya biasanya dilewatkan ke fungsi sebagai pointer. (Dengan string itu diperlukan; dengan struktur itu jauh lebih efisien.)
Sedangkan untuk bilangan bulat, jika Anda berada di lingkungan tertanam dengan memori yang sangat terbatas, Anda mungkin perlu khawatir tentang di mana konstanta disimpan dan bagaimana mengaksesnya dikompilasi. Compiler mungkin menambahkan dua const pada saat run time, tetapi menambahkan dua #define pada waktu kompilasi. Konstanta #define dapat dikonversi menjadi satu atau lebih instruksi [segera] MOV, yang berarti konstanta disimpan secara efektif dalam memori program. Konstanta konstanta akan disimpan di bagian .const di memori data. Dalam sistem dengan arsitektur Harvard, mungkin ada perbedaan dalam kinerja dan penggunaan memori, meskipun mereka kemungkinan kecil. Mereka mungkin penting untuk optimasi hard-core loop batin.
Jangan berpikir ada jawaban untuk "yang selalu terbaik" tetapi, seperti yang dikatakan Matthieu
static const
tipe aman. Kesulitan terbesar saya dengan #define, ketika debugging di Visual Studio Anda tidak dapat menonton variabel. Ini memberikan kesalahan bahwa simbol tidak dapat ditemukan.
"Anda tidak dapat menonton variabel" Benar, itu bukan variabel. Itu tidak berubah, mengapa Anda harus menontonnya? Anda dapat menemukan di mana-mana itu digunakan hanya dengan mencari label. Mengapa Anda perlu (atau bahkan ingin) menonton #define?
Marshall Eubanks
3
Secara kebetulan, alternatif untuk #define, yang menyediakan pelingkupan yang tepat tetapi berperilaku seperti konstanta "nyata", adalah "enum". Sebagai contoh:
enum{number_ten =10;}
Dalam banyak kasus, ini berguna untuk mendefinisikan tipe yang disebutkan dan membuat variabel dari tipe tersebut; jika itu dilakukan, debugger mungkin dapat menampilkan variabel sesuai dengan nama enumerasinya.
Satu peringatan penting dengan melakukan itu, bagaimanapun: di C ++, tipe yang disebutkan memiliki kompatibilitas terbatas dengan bilangan bulat. Sebagai contoh, secara default, seseorang tidak dapat melakukan aritmatika pada mereka. Saya menemukan bahwa menjadi perilaku default yang aneh untuk enum; sementara itu akan menyenangkan untuk memiliki tipe "enum yang ketat", mengingat keinginan untuk memiliki C ++ yang umumnya kompatibel dengan C, saya akan berpikir perilaku default tipe "enum" harus dipertukarkan dengan bilangan bulat.
Dalam C, konstanta enumerasi selalu bertipe int, jadi "enum hack" tidak dapat digunakan dengan tipe integer lainnya. ( Jenis enumerasi kompatibel dengan beberapa tipe integer yang didefinisikan implementasi, tidak harus int, tetapi dalam hal ini tipe tersebut anonim sehingga tidak masalah.)
Keith Thompson
@KeithThompson: Karena saya menulis di atas, saya telah membaca bahwa MISRA-C akan mengomel jika kompiler memberikan tipe selain dari intvariabel yang diketik enumerasi (kompiler yang diizinkan untuk melakukan) dan seseorang mencoba untuk menetapkan variabel tersebut anggota enumerasi sendiri. Saya berharap komite standar akan menambahkan cara portabel untuk mendeklarasikan tipe integer dengan semantik yang ditentukan. Platform APA PUN , terlepas dari charukurannya, harus dapat misalnya menyatakan jenis yang akan membungkus mod 65536, bahkan jika kompiler harus menambahkan banyak AND R0,#0xFFFFinstruksi atau setara.
supercat
Anda dapat menggunakan uint16_t, meskipun tentu saja itu bukan tipe enumerasi. Akan lebih baik untuk membiarkan pengguna menentukan tipe integer yang digunakan untuk mewakili tipe enumerasi yang diberikan, tetapi Anda dapat mencapai banyak efek yang sama dengan typedeffor uint16_tdan serangkaian #defines untuk nilai individual.
Keith Thompson
1
@KeithThompson: Saya mengerti bahwa karena alasan historis, kami terjebak dengan fakta bahwa beberapa platform akan menilai 2U < -1Lbenar dan yang lain salah, dan kami sekarang terjebak dengan fakta bahwa beberapa platform akan menerapkan perbandingan antara uint32_tdan int32_tsebagaimana ditandatangani dan beberapa tidak ditandatangani, tetapi itu tidak berarti Komite tidak dapat menentukan pengganti C yang kompatibel ke atas yang mencakup jenis yang semantiknya akan konsisten pada semua kompiler.
supercat
1
Perbedaan sederhana:
Pada waktu pra-pemrosesan, konstanta diganti dengan nilainya. Jadi Anda tidak bisa menerapkan operator dereference ke definisi, tetapi Anda dapat menerapkan operator dereference ke variabel.
Seperti yang Anda duga, definisikan konstanta statis lebih cepat.
Misalnya, memiliki:
#define mymax 100
Anda tidak dapat melakukan printf("address of constant is %p",&mymax);.
Tetapi memiliki
constint mymax_var=100
Anda dapat melakukan printf("address of constant is %p",&mymax_var);.
Untuk lebih jelasnya, define digantikan oleh nilainya pada tahap pra-pemrosesan, jadi kami tidak memiliki variabel yang disimpan dalam program. Kami baru saja kode dari segmen teks dari program di mana define digunakan.
Namun, untuk const statis kami memiliki variabel yang dialokasikan di suatu tempat. Untuk gcc, konstanta statis dialokasikan di segmen teks program.
Di atas, saya ingin memberi tahu tentang operator referensi sehingga ganti dereference dengan referensi.
Jawaban Anda sangat salah. Ini tentang C, jawaban Anda terkait dengan C ++, yang memiliki semantik yang sangat berbeda untuk constkualifikasi. C tidak memiliki konstanta simbolika selain konstanta enum . A const intadalah variabel. Anda juga membingungkan bahasa dan implementasi spesifik. Tidak ada persyaratan di mana menempatkan objek. Dan itu bahkan tidak berlaku untuk gcc: biasanya ia menempatkan constvariabel yang memenuhi syarat di .rodatabagian ini. Tapi itu tergantung pada platform target. Dan maksud Anda adalah alamat dari operator &.
terlalu jujur untuk situs ini
0
Kami melihat kode assembler yang dihasilkan pada MBF16X ... Kedua varian menghasilkan kode yang sama untuk operasi aritmatika (ADD Immediate, misalnya).
Jadi const intlebih disukai untuk cek tipe sementara #definegaya lama. Mungkin ini khusus untuk kompiler. Jadi, periksa kode assembler yang Anda hasilkan.
Saya tidak yakin apakah saya benar tetapi menurut pendapat saya memanggil #definenilai d jauh lebih cepat daripada memanggil variabel lain yang biasanya dinyatakan (atau nilai const). Itu karena ketika program sedang berjalan dan perlu menggunakan beberapa variabel yang biasanya dinyatakan perlu melompat ke tempat yang tepat di memori untuk mendapatkan variabel itu.
Sebaliknya ketika menggunakan #definenilai d, program tidak perlu melompat ke memori yang dialokasikan, hanya mengambil nilainya. Jika #define myValue 7dan panggilan program myValue, itu berperilaku persis sama seperti ketika itu hanya panggilan 7.
Jawaban:
Tergantung pada apa yang Anda butuhkan nilainya. Anda (dan semua orang sejauh ini) menghilangkan alternatif ketiga:
static const int var = 5;
#define var 5
enum { var = 5 };
Mengabaikan masalah tentang pilihan nama, maka:
Jadi, dalam sebagian besar konteks, lebih suka 'enum' daripada alternatif. Jika tidak, poin pertama dan terakhir cenderung menjadi faktor pengendali - dan Anda harus berpikir lebih keras jika Anda harus memuaskan keduanya sekaligus.
Jika Anda bertanya tentang C ++, maka Anda akan menggunakan opsi (1) - konstanta statis - setiap saat.
sumber
enum
adalah mereka diimplementasikan sebagaiint
([C99] 6.7.2.2/3). A#define
memungkinkan Anda menentukan tanda unsigned dan long withU
danL
suffix, danconst
memungkinkan Anda memberikan tipe.enum
dapat menyebabkan masalah dengan konversi jenis yang biasa.enum
juga tidak#define
menggunakan ruang ekstra, per se. Nilai akan muncul dalam kode objek sebagai bagian dari instruksi daripada dialokasikan penyimpanan di segmen data atau di tumpukan atau di tumpukan. Anda akan memiliki beberapa ruang yang dialokasikan untukstatic const int
, tetapi kompiler mungkin mengoptimalkannya jika Anda tidak mengambil alamat.enum
s (danstatic const
): mereka tidak dapat diubah. adefine
bisa di#undefine
mana aenum
danstatic const
ditetapkan ke nilai yang diberikan.Secara umum:
Karena itu menghormati ruang lingkup dan tipe-aman.
Satu-satunya peringatan yang bisa saya lihat: jika Anda ingin variabel yang mungkin didefinisikan pada baris perintah. Masih ada alternatif:
Kapan pun memungkinkan, alih-alih makro / elipsis, gunakan alternatif yang aman untuk jenis.
Jika Anda benar-benar PERLU untuk pergi dengan makro (misalnya, Anda inginkan
__FILE__
atau__LINE__
), maka Anda sebaiknya memberi nama makro Anda SANGAT hati-hati: dalam konvensi penamaannya, Boost merekomendasikan semua huruf besar, dimulai dengan nama proyek (di sini BOOST_ ), sambil membaca dengan teliti perpustakaan Anda akan melihat ini (umumnya) diikuti dengan nama area tertentu (perpustakaan) kemudian dengan nama yang bermakna.Biasanya membuat nama panjang :)
sumber
static
yang alamatnya diambil yang akan tetap; dan jika alamat itu diambil seseorang tidak bisa menggunakan#define
atauenum
(tidak ada alamat) ... jadi saya benar-benar gagal melihat alternatif apa yang bisa digunakan. Jika Anda dapat menghapus "kompilasi evaluasi waktu", Anda mungkin mencariextern const
.#if
mungkin lebih lebih#ifdef
untuk bendera boolean, tapi dalam hal ini akan membuat tidak mungkin untuk menentukanvar
sebagai0
dari baris perintah. Jadi dalam hal ini,#ifdef
lebih masuk akal, asalkan0
merupakan nilai legal untukvar
.Di C, khususnya? Dalam C jawaban yang benar adalah: gunakan
#define
(atau, jika sesuai,enum
)Meskipun menguntungkan untuk memiliki scoping dan mengetik properti suatu
const
objek, pada kenyataannyaconst
objek dalam C (sebagai lawan dari C ++) bukanlah konstanta yang benar dan oleh karena itu biasanya tidak berguna dalam kebanyakan kasus praktis.Jadi, dalam C pilihan harus ditentukan oleh bagaimana Anda berencana untuk menggunakan konstanta Anda. Misalnya, Anda tidak bisa menggunakan
const int
objek sebagaicase
label (sementara makro akan berfungsi). Anda tidak bisa menggunakanconst int
objek sebagai lebar bidang-bit (selagi makro akan berfungsi). Di C89 / 90 Anda tidak bisa menggunakanconst
objek untuk menentukan ukuran array (sementara makro akan bekerja). Bahkan di C99 Anda tidak dapat menggunakanconst
objek untuk menentukan ukuran array saat Anda membutuhkan array non- VLA .Jika ini penting bagi Anda maka itu akan menentukan pilihan Anda. Sebagian besar waktu, Anda tidak akan punya pilihan selain menggunakan
#define
dalam C. Dan jangan lupa alternatif lain, yang menghasilkan konstanta benar dalam C -enum
.Dalam C ++
const
objek adalah konstanta yang benar, jadi dalam C ++ hampir selalu lebih baik untuk memilihconst
varian (tidak perlu eksplisitstatic
dalam C ++ sekalipun).sumber
const int
objek dalam label kasus adalah ilegal di semua versi bahasa C. (Tentu saja, kompiler Anda bebas untuk mendukungnya sebagai ekstensi bahasa mirip C ++ yang tidak standar.)const
berarti hanya baca.const int r = rand();
sangat legal.constexpr
dibandingkanconst
denganstl
wadah khusus sepertiarray
ataubitset
.switch()
pernyataan, bukan dalamcase
satu. Saya baru saja terjebak dengan yang ini juga ☺Perbedaan antara
static const
dan#define
yang pertama menggunakan memori dan yang berikutnya tidak menggunakan memori untuk penyimpanan. Kedua, Anda tidak bisa memasukkan alamat#define
sedangkan Anda dapat melewati alamat astatic const
. Sebenarnya itu tergantung pada keadaan apa kita, kita perlu memilih satu di antara keduanya. Keduanya dalam kondisi terbaik dalam situasi yang berbeda. Tolong jangan berasumsi bahwa yang satu lebih baik dari yang lain ... :-)Jika itu yang terjadi, Dennis Ritchie akan menyimpan yang terbaik sendirian ... hahaha ... :-)
sumber
const
tidak menggunakan memori. GCC (diuji dengan 4.5.3 dan beberapa versi yang lebih baru) dengan mudah mengoptimalkanconst int
ke dalam literal langsung dalam kode Anda saat menggunakan -O3. Jadi, jika Anda melakukan pengembangan yang tertanam dalam RAM rendah (mis. AVR), Anda dapat dengan aman menggunakan konstanta C jika Anda menggunakan GCC atau kompiler lain yang kompatibel. Saya belum mengujinya tetapi mengharapkan Dentang melakukan hal yang sama btw.Dalam C
#define
jauh lebih populer. Anda dapat menggunakan nilai-nilai itu untuk mendeklarasikan ukuran array misalnya:ANSI C tidak mengizinkan Anda untuk menggunakan
static const
dalam konteks ini sejauh yang saya tahu. Dalam C ++ Anda harus menghindari makro dalam kasus ini. Kamu bisa menulisdan bahkan meninggalkan
static
karena hubungan internal sudah tersiratconst
[hanya dalam C ++].sumber
const int MY_CONSTANT = 5;
dalam satu file dan mengaksesnya dengan yangextern const int MY_CONSTANT;
lain. Saya tidak dapat menemukan info apa pun dalam standar (setidaknya C99) tentangconst
mengubah perilaku default "6.2.2: 5 Jika deklarasi pengidentifikasi untuk suatu objek memiliki cakupan file dan tidak ada kelas penyimpanan yang ditentukan, kaitannya adalah eksternal".bar
adalah VLA (array panjang variabel); kompiler cenderung menghasilkan kode seolah-olah panjangnya konstan.Kelemahan lain dari
const
dalam C adalah bahwa Anda tidak dapat menggunakan nilai dalam menginisialisasi yang lainconst
.Bahkan ini tidak bekerja dengan const karena kompilator tidak melihatnya sebagai konstanta:
Saya akan senang menggunakan mengetik
const
dalam kasus ini, jika tidak ...sumber
static uint8_t const ARRAY_SIZE = 16;
tiba-tiba kompilasi Anda tidak lagi bisa sedikit menantang, terutama ketika#define ARRAY_SIZE 256
terkubur sepuluh lapisan jauh di web header kusut. Itu semua nama topiARRAY_SIZE
meminta masalah. Cadangan ALL_CAPS untuk makro, dan jangan pernah mendefinisikan makro yang tidak dalam bentuk ALL_CAPS.const
. Ini bisa lebih ditingkatkan!Jika bisa lolos begitu saja,
static const
punya banyak keuntungan. Itu mematuhi prinsip-prinsip ruang lingkup normal, terlihat di debugger, dan umumnya mematuhi aturan yang dipatuhi variabel.Namun, setidaknya dalam standar C asli, itu sebenarnya tidak konstan. Jika Anda menggunakan
#define var 5
, Anda dapat menulisint foo[var];
sebagai deklarasi, tetapi Anda tidak dapat melakukan itu (kecuali sebagai ekstensi kompiler "denganstatic const int var = 5;
. Ini bukan kasus di C ++, di manastatic const
versi dapat digunakan di mana saja#define
versi dapat, dan saya percaya ini juga halnya dengan C99.Namun, jangan pernah menyebutkan
#define
konstanta dengan nama huruf kecil. Ini akan mengesampingkan kemungkinan penggunaan nama itu sampai akhir unit terjemahan. Konstanta makro harus dalam apa yang secara efektif namespace mereka sendiri, yang secara tradisional semua huruf kapital, mungkin dengan awalan.sumber
const
di C99 masih belum konstan nyata. Anda dapat mendeklarasikan ukuran array denganconst
dalam C99, tetapi hanya karena C99 mendukung Array Panjang Variabel. Karena alasan ini, ini hanya akan berfungsi jika VLA diizinkan. Misalnya, bahkan di C99, Anda masih tidak bisa menggunakan aconst
untuk menyatakan ukuran array anggota di astruct
.const int
ukuran seolah-olah itu adalah konstanta C ++ atau makro. Apakah Anda ingin bergantung pada penyimpangan GCC dari standar ini tentu saja merupakan pilihan Anda, saya pribadi akan menggunakannya kecuali Anda benar-benar dapat mengabaikan menggunakan kompiler lain daripada GCC atau Dentang, yang terakhir memiliki fitur yang sama di sini (diuji dengan Dentang 3.7).Itu SELALU lebih baik menggunakan const, daripada #define. Itu karena const diperlakukan oleh compiler dan #define oleh preprocessor. Ini seperti #define sendiri bukan bagian dari kode (secara kasar).
Contoh:
Nama simbolis PI mungkin tidak pernah dilihat oleh kompiler; mungkin dihapus oleh preprocessor sebelum kode sumber bahkan sampai ke kompiler. Akibatnya, nama PI mungkin tidak dimasukkan ke dalam tabel simbol. Ini bisa membingungkan jika Anda mendapatkan kesalahan selama kompilasi yang melibatkan penggunaan konstanta, karena pesan kesalahan dapat merujuk ke 3.1416, bukan PI. Jika PI didefinisikan dalam file header yang tidak Anda tulis, Anda tidak akan tahu dari mana 3,1416 berasal.
Masalah ini juga dapat muncul dalam debugger simbolis, karena, sekali lagi, nama yang Anda pemrograman mungkin tidak ada dalam tabel simbol.
Larutan:
sumber
#define var 5
akan menyebabkan Anda kesulitan jika Anda memiliki hal-hal sepertimystruct.var
.Sebagai contoh,
Preprocessor akan menggantinya dan kode tidak akan dikompilasi. Untuk alasan ini, gaya pengkodean tradisional menyarankan semua
#define
huruf kapital konstan menggunakan untuk menghindari konflik.sumber
Saya menulis program tes cepat untuk menunjukkan satu perbedaan:
Ini mengkompilasi dengan kesalahan dan peringatan ini:
Perhatikan bahwa enum memberikan kesalahan saat define memberikan peringatan.
sumber
Definisi
tidak selalu mendefinisikan nilai konstan. Beberapa kompiler (misalnya tcc 0.9.26 ) hanya mengalokasikan memori yang diidentifikasi dengan nama "const_value". Menggunakan pengenal "const_value" Anda tidak dapat mengubah memori ini. Tetapi Anda masih bisa memodifikasi memori menggunakan pengenal lain:
Ini artinya definisi
adalah satu-satunya cara untuk mendefinisikan nilai konstan yang tidak dapat dimodifikasi dengan cara apa pun.
sumber
#define
juga dapat dimodifikasi, dengan mengedit kode mesin.5
. Tetapi orang tidak dapat memodifikasi#define
karena ini adalah makro preprosesor. Itu tidak ada dalam program biner. Jika seseorang ingin memodifikasi semua tempatCONST_VALUE
yang digunakan, orang harus melakukannya satu per satu.#define CONST 5
, makaif (CONST == 5) { do_this(); } else { do_that(); }
, dan kompiler menghilangkanelse
cabang. Bagaimana Anda mengusulkan untuk mengedit kode mesin untuk mengubahCONST
ke 6?#define
tidak tahan peluru.#define
. Satu-satunya cara nyata untuk melakukannya adalah dengan mengedit kode sumber dan mengkompilasi ulang.Meskipun pertanyaannya adalah tentang bilangan bulat, perlu dicatat bahwa #define dan enum tidak berguna jika Anda membutuhkan struktur atau string yang konstan. Keduanya biasanya dilewatkan ke fungsi sebagai pointer. (Dengan string itu diperlukan; dengan struktur itu jauh lebih efisien.)
Sedangkan untuk bilangan bulat, jika Anda berada di lingkungan tertanam dengan memori yang sangat terbatas, Anda mungkin perlu khawatir tentang di mana konstanta disimpan dan bagaimana mengaksesnya dikompilasi. Compiler mungkin menambahkan dua const pada saat run time, tetapi menambahkan dua #define pada waktu kompilasi. Konstanta #define dapat dikonversi menjadi satu atau lebih instruksi [segera] MOV, yang berarti konstanta disimpan secara efektif dalam memori program. Konstanta konstanta akan disimpan di bagian .const di memori data. Dalam sistem dengan arsitektur Harvard, mungkin ada perbedaan dalam kinerja dan penggunaan memori, meskipun mereka kemungkinan kecil. Mereka mungkin penting untuk optimasi hard-core loop batin.
sumber
Jangan berpikir ada jawaban untuk "yang selalu terbaik" tetapi, seperti yang dikatakan Matthieu
static const
tipe aman. Kesulitan terbesar saya dengan
#define
, ketika debugging di Visual Studio Anda tidak dapat menonton variabel. Ini memberikan kesalahan bahwa simbol tidak dapat ditemukan.sumber
Secara kebetulan, alternatif untuk
#define
, yang menyediakan pelingkupan yang tepat tetapi berperilaku seperti konstanta "nyata", adalah "enum". Sebagai contoh:Dalam banyak kasus, ini berguna untuk mendefinisikan tipe yang disebutkan dan membuat variabel dari tipe tersebut; jika itu dilakukan, debugger mungkin dapat menampilkan variabel sesuai dengan nama enumerasinya.
Satu peringatan penting dengan melakukan itu, bagaimanapun: di C ++, tipe yang disebutkan memiliki kompatibilitas terbatas dengan bilangan bulat. Sebagai contoh, secara default, seseorang tidak dapat melakukan aritmatika pada mereka. Saya menemukan bahwa menjadi perilaku default yang aneh untuk enum; sementara itu akan menyenangkan untuk memiliki tipe "enum yang ketat", mengingat keinginan untuk memiliki C ++ yang umumnya kompatibel dengan C, saya akan berpikir perilaku default tipe "enum" harus dipertukarkan dengan bilangan bulat.
sumber
int
, jadi "enum hack" tidak dapat digunakan dengan tipe integer lainnya. ( Jenis enumerasi kompatibel dengan beberapa tipe integer yang didefinisikan implementasi, tidak harusint
, tetapi dalam hal ini tipe tersebut anonim sehingga tidak masalah.)int
variabel yang diketik enumerasi (kompiler yang diizinkan untuk melakukan) dan seseorang mencoba untuk menetapkan variabel tersebut anggota enumerasi sendiri. Saya berharap komite standar akan menambahkan cara portabel untuk mendeklarasikan tipe integer dengan semantik yang ditentukan. Platform APA PUN , terlepas darichar
ukurannya, harus dapat misalnya menyatakan jenis yang akan membungkus mod 65536, bahkan jika kompiler harus menambahkan banyakAND R0,#0xFFFF
instruksi atau setara.uint16_t
, meskipun tentu saja itu bukan tipe enumerasi. Akan lebih baik untuk membiarkan pengguna menentukan tipe integer yang digunakan untuk mewakili tipe enumerasi yang diberikan, tetapi Anda dapat mencapai banyak efek yang sama dengantypedef
foruint16_t
dan serangkaian#define
s untuk nilai individual.2U < -1L
benar dan yang lain salah, dan kami sekarang terjebak dengan fakta bahwa beberapa platform akan menerapkan perbandingan antarauint32_t
danint32_t
sebagaimana ditandatangani dan beberapa tidak ditandatangani, tetapi itu tidak berarti Komite tidak dapat menentukan pengganti C yang kompatibel ke atas yang mencakup jenis yang semantiknya akan konsisten pada semua kompiler.Perbedaan sederhana:
Pada waktu pra-pemrosesan, konstanta diganti dengan nilainya. Jadi Anda tidak bisa menerapkan operator dereference ke definisi, tetapi Anda dapat menerapkan operator dereference ke variabel.
Seperti yang Anda duga, definisikan konstanta statis lebih cepat.
Misalnya, memiliki:
Anda tidak dapat melakukan
printf("address of constant is %p",&mymax);
.Tetapi memiliki
Anda dapat melakukan
printf("address of constant is %p",&mymax_var);
.Untuk lebih jelasnya, define digantikan oleh nilainya pada tahap pra-pemrosesan, jadi kami tidak memiliki variabel yang disimpan dalam program. Kami baru saja kode dari segmen teks dari program di mana define digunakan.
Namun, untuk const statis kami memiliki variabel yang dialokasikan di suatu tempat. Untuk gcc, konstanta statis dialokasikan di segmen teks program.
Di atas, saya ingin memberi tahu tentang operator referensi sehingga ganti dereference dengan referensi.
sumber
const
kualifikasi. C tidak memiliki konstanta simbolika selain konstanta enum . Aconst int
adalah variabel. Anda juga membingungkan bahasa dan implementasi spesifik. Tidak ada persyaratan di mana menempatkan objek. Dan itu bahkan tidak berlaku untuk gcc: biasanya ia menempatkanconst
variabel yang memenuhi syarat di.rodata
bagian ini. Tapi itu tergantung pada platform target. Dan maksud Anda adalah alamat dari operator&
.Kami melihat kode assembler yang dihasilkan pada MBF16X ... Kedua varian menghasilkan kode yang sama untuk operasi aritmatika (ADD Immediate, misalnya).
Jadi
const int
lebih disukai untuk cek tipe sementara#define
gaya lama. Mungkin ini khusus untuk kompiler. Jadi, periksa kode assembler yang Anda hasilkan.sumber
Saya tidak yakin apakah saya benar tetapi menurut pendapat saya memanggil
#define
nilai d jauh lebih cepat daripada memanggil variabel lain yang biasanya dinyatakan (atau nilai const). Itu karena ketika program sedang berjalan dan perlu menggunakan beberapa variabel yang biasanya dinyatakan perlu melompat ke tempat yang tepat di memori untuk mendapatkan variabel itu.Sebaliknya ketika menggunakan
#define
nilai d, program tidak perlu melompat ke memori yang dialokasikan, hanya mengambil nilainya. Jika#define myValue 7
dan panggilan programmyValue
, itu berperilaku persis sama seperti ketika itu hanya panggilan7
.sumber