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 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.
enum
adalah mereka diimplementasikan sebagai int
([C99] 6.7.2.2/3). A #define
memungkinkan Anda menentukan tanda unsigned dan long with U
dan L
suffix, dan const
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 untuk static const int
, tetapi kompiler mungkin mengoptimalkannya jika Anda tidak mengambil alamat.
enum
s (dan static const
): mereka tidak dapat diubah. a define
bisa di #undefine
mana a enum
dan static const
ditetapkan 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 :)
static
yang alamatnya diambil yang akan tetap; dan jika alamat itu diambil seseorang tidak bisa menggunakan #define
atau 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
.
#if
mungkin lebih lebih #ifdef
untuk bendera boolean, tapi dalam hal ini akan membuat tidak mungkin untuk menentukan var
sebagai 0
dari baris perintah. Jadi dalam hal ini, #ifdef
lebih masuk akal, asalkan 0
merupakan 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 const
objek, pada kenyataannya const
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 sebagai case
label (sementara makro akan berfungsi). Anda tidak bisa menggunakan const int
objek sebagai lebar bidang-bit (selagi makro akan berfungsi). Di C89 / 90 Anda tidak bisa menggunakan const
objek untuk menentukan ukuran array (sementara makro akan bekerja). Bahkan di C99 Anda tidak dapat menggunakan const
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 memilih const
varian (tidak perlu eksplisit static
dalam C ++ sekalipun).
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
dibandingkan const
dengan stl
wadah khusus seperti array
atau bitset
.
switch()
pernyataan, bukan dalam case
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 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 ... :-)
const
tidak menggunakan memori. GCC (diuji dengan 4.5.3 dan beberapa versi yang lebih baru) dengan mudah mengoptimalkan const 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:
#define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
}
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 menulis
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
dan bahkan meninggalkan static
karena 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 const
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 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 const
dalam 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 256
terkubur sepuluh lapisan jauh di web header kusut. Itu semua nama topi ARRAY_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 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 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.
const
di C99 masih belum konstan nyata. Anda dapat mendeklarasikan ukuran array dengan const
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 a const
untuk menyatakan ukuran array anggota di a struct
.
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:
#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 5
akan 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 #define
huruf 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.
#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 tempat CONST_VALUE
yang digunakan, orang harus melakukannya satu per satu.
#define CONST 5
, maka if (CONST == 5) { do_this(); } else { do_that(); }
, dan kompiler menghilangkan else
cabang. Bagaimana Anda mengusulkan untuk mengedit kode mesin untuk mengubah CONST
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.
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.)
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 dari char
ukurannya, harus dapat misalnya menyatakan jenis yang akan membungkus mod 65536, bahkan jika kompiler harus menambahkan banyak AND 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 dengan typedef
for uint16_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 antara uint32_t
dan int32_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:
#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.
const
kualifikasi. C tidak memiliki konstanta simbolika selain konstanta enum . A const 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 menempatkan const
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.
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 program myValue
, itu berperilaku persis sama seperti ketika itu hanya panggilan 7
.