“Static const” vs “#define” vs “enum”

585

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 };
Vijay
sumber
35
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.
MSalters
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:

  1. static const int var = 5;
  2. #define var 5
  3. 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.

Jonathan Leffler
sumber
111
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:

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 :)

Matthieu M.
sumber
2
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).

Semut
sumber
6
"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 ... :-)

bungkusnya
sumber
6
+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 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 ++].

sellibitze
sumber
1
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.

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 ...

Gauthier
sumber
5
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.

David Thornley
sumber
6
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.

Larutan:

const double PI = 3.1416; //or static const...
suren
sumber
6

#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.

Interupsi yang tidak dapat ditutup
sumber
6

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.

Michael Potter
sumber
4

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.

pengguna2229691
sumber
8
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.

Adam Haun
sumber
3

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.

Sempit
sumber
1
"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.

supercat
sumber
1
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

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.

mihaitzateo
sumber
1
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.

tamu
sumber
-1

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.

pajczur
sumber