Mana yang lebih baik untuk digunakan di antara pernyataan di bawah ini dalam C?
static const int var = 5;
atau
#define var 5
atau
enum { var = 5 };
Mana yang lebih baik untuk digunakan di antara pernyataan di bawah ini dalam C?
static const int var = 5;
atau
#define var 5
atau
enum { var = 5 };
Jawaban:
Tergantung pada apa yang Anda butuhkan nilainya. Anda (dan semua orang sejauh ini) menghilangkan alternatif ketiga:
static const int var = 5;#define var 5enum { 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.
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.
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.
enums (dan static const): mereka tidak dapat diubah. a definebisa di #undefinemana a enumdan static constditetapkan ke nilai yang diberikan.
Secara umum:
static const
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..
static int const var = VAR;
#else
static int const 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.
Biasanya membuat nama panjang :)
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.
#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.
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).
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.)
constberarti hanya baca. const int r = rand();sangat legal.
constexprdibandingkan constdengan stlwadah khusus seperti arrayatau bitset.
switch()pernyataan, bukan dalam casesatu. Saya baru saja terjebak dengan yang ini juga ☺
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 ... :-)
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.
Dalam C #definejauh lebih populer. Anda dapat menggunakan nilai-nilai itu untuk mendeklarasikan ukuran array misalnya:
#define MAXLEN 5
void 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
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
dan bahkan meninggalkan statickarena hubungan internal sudah tersirat const[hanya dalam C ++].
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".
baradalah VLA (array panjang variabel); kompiler cenderung menghasilkan kode seolah-olah panjangnya konstan.
Kelemahan lain dari constdalam C adalah bahwa Anda tidak dapat menggunakan nilai dalam menginisialisasi yang lain const.
static int const NUMBER_OF_FINGERS_PER_HAND = 5;
static int const NUMBER_OF_HANDS = 2;
// initializer element is not constant, this does not work.
static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND
* NUMBER_OF_HANDS;
Bahkan ini tidak bekerja dengan const karena kompilator tidak melihatnya sebagai konstanta:
static uint8_t const ARRAY_SIZE = 16;
static int8_t const 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 ...
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.
const. Ini bisa lebih ditingkatkan!
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.
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.
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).
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.
Larutan:
const double PI = 3.1416; //or static const...
#define var 5akan menyebabkan Anda kesulitan jika Anda memiliki hal-hal seperti mystruct.var.
Sebagai contoh,
struct mystruct {
int var;
};
#define var 5
int main() {
struct mystruct foo;
foo.var = 1;
return 0;
}
Preprocessor akan menggantinya dan kode tidak akan dikompilasi. Untuk alasan ini, gaya pengkodean tradisional menyarankan semua #definehuruf kapital konstan menggunakan untuk menghindari konflik.
Saya menulis program tes cepat untuk menunjukkan satu perbedaan:
#include <stdio.h>
enum {ENUM_DEFINED=16};
enum {ENUM_DEFINED=32};
#define DEFINED_DEFINED 16
#define DEFINED_DEFINED 32
int main(int argc, char *argv[]) {
printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED);
return(0);
}
Ini mengkompilasi dengan kesalahan dan peringatan ini:
main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED'
enum {ENUM_DEFINED=32};
^
main.c:5:7: note: previous definition is here
enum {ENUM_DEFINED=16};
^
main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined]
#define DEFINED_DEFINED 32
^
main.c:8:9: note: previous definition is here
#define DEFINED_DEFINED 16
^
Perhatikan bahwa enum memberikan kesalahan saat define memberikan peringatan.
Definisi
const int const_value = 5;
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:
const int 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.
#definejuga dapat dimodifikasi, dengan mengedit kode mesin.
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.
#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?
#definetidak 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.
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.
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.
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.)
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.
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.
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.
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
const int 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.
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 &.
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.