Hangisi C'de aşağıdaki ifadeler arasında kullanmak daha iyidir?
static const int var = 5;
veya
#define var 5
veya
enum { var = 5 };
Hangisi C'de aşağıdaki ifadeler arasında kullanmak daha iyidir?
static const int var = 5;
veya
#define var 5
veya
enum { var = 5 };
Yanıtlar:
Değer için neye ihtiyacınız olduğuna bağlıdır. Siz (ve şu ana kadar herkes) üçüncü alternatifi atladınız:
static const int var = 5;
#define var 5
enum { var = 5 };
İsim seçimi ile ilgili sorunları göz ardı ederek:
Yani, çoğu bağlamda, alternatiflere göre 'enum'u tercih edin. Aksi takdirde, ilk ve son mermi noktalarının kontrol edici faktörler olması muhtemeldir - ve her ikisini de aynı anda tatmin etmeniz gerekiyorsa daha fazla düşünmeniz gerekir.
C ++ hakkında soru soruyorsanız, (1) seçeneğini - statik sabit - her seferinde kullanırsınız.
enum
olarak uygulanmasıdır int
. A, #define
imzasız ve uzun ekleri U
ve L
sonekleri belirtmenizi const
sağlar ve bir tür vermenizi sağlar. enum
normal tür dönüşümlerde sorunlara neden olabilir.
enum
de #define
kendi başına kullanır. Değer, veri segmentinde veya yığınta veya yığın üzerinde depolama alanı olarak ayrılmak yerine nesne kodunda talimatların bir parçası olarak görünür. İçin yer ayırmış olacaksınız static const int
, ancak bir adres almazsanız derleyici alanı optimize edebilir.
enum
S (ve static const
) için başka bir 'oy' : değiştirilemezler. Bir define
olabilir #undefine
d' burada bir enum
ve static const
belirli bir değere sabitlenir.
Genel konuşma:
static const
Çünkü kapsama saygı duyar ve tip güvenlidir.
Görebildiğim tek uyarı: değişkenin komut satırında tanımlanmasını istiyorsanız. Hala bir alternatif var:
#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
Mümkün olduğunda, makrolar / üç nokta yerine, güvenli bir alternatif kullanın.
Gerçekten bir makroya (örneğin, istediğiniz __FILE__
veya __LINE__
) ihtiyacınız varsa, makronuzu ÇOK dikkatli bir şekilde adlandırmanız daha iyi olur: Adlandırma kuralında Boost , projenin adından başlayarak tüm büyük harfleri önerir (burada BOOST_ ), kitaplığı incelerken bunun (genellikle) ardından belirli bir alanın (kitaplığın) ardından anlamlı bir adla geldiğini göreceksiniz.
Genellikle uzun isimler yapar :)
static
adresi alınmış olanlar kalmalıdır; ve adres alınırsa, bir #define
veya enum
(adres yok) kullanamazdım ... bu yüzden hangi alternatifin kullanılabileceğini göremiyorum. "Derleme süresi değerlendirmesi" ile başa çıkmak, extern const
bunun yerine arıyor olabilir .
#if
over tercih olabilir #ifdef
boolean bayrakları için, ancak bu durumda o imkansız tanımlamak kılacak var
şekilde 0
komut satırından. Bu durumda, yasal bir değer #ifdef
olduğu sürece daha mantıklıdır . 0
var
C olarak, özellikle? C'de doğru cevap: kullanın #define
(veya uygunsa,enum
)
Bir const
nesnenin kapsam ve yazma özelliklerine sahip olmak faydalı olsa da, gerçekteconst
C'deki nesnelerde (C ++ yerine) gerçek sabitler sabit değildir ve bu nedenle çoğu pratik durumda genellikle işe yaramaz.
Bu nedenle, C'de seçim, sabitinizi nasıl kullanmayı planladığınıza göre belirlenmelidir. Örneğin, bir const int
nesneyi case
etiket olarak kullanamazsınız (makro çalışırken). Bir const int
nesneyi bit alanı genişliği olarak kullanamazsınız (makro çalışırken). C89 / 90'da const
bir dizi boyutu belirtmek için bir nesne kullanamazsınız (bir makro çalışırken). C99'da bile, VLAconst
olmayan bir öğeye ihtiyacınız olduğunda bir dizi boyutu belirtmek için bir nesneyi kullanamazsınız .
Bu sizin için önemliyse, seçiminizi belirler. Çoğu zaman, kullanmaktan başka seçeneğiniz olmaz#define
C'de başka seçeneğiniz olmayacaktır. Ve C - 'de gerçek sabitler üreten başka bir alternatifi unutmayın enum
.
C ++ const
nesnelerinde gerçek sabitler vardır, bu yüzden C ++ 'da const
varyantı tercih etmek neredeyse her zaman daha iyidir ( static
C ++' da açıkça gerek yoktur ).
const int
Vaka etiketlerinde nesne kullanmak , C dilinin tüm sürümlerinde yasa dışıdır. (Tabii ki, derleyiciniz standart olmayan C ++ benzeri bir dil uzantısı olarak desteklemekte serbesttir.)
const
salt okunur anlamına gelir. const int r = rand();
tamamen yasaldır.
constexpr
olarak karşılaştırıldığında daha iyidir . const
stl
array
bitset
switch()
değil, ifadede test etmiş olmalısınız case
. Ben de buna yakalandım ☺
Arasındaki fark static const
ve #define
olduğu önceki kullanımları bellek ve depolama için daha sonra kullanmaz bellek. İkinci olarak, bir #define
adresin adresini geçiremezken, a static const
. Aslında, hangi koşul altında olduğumuza bağlı olarak, bu ikisi arasından birini seçmemiz gerekiyor. Her ikisi de farklı koşullar altında en iyi durumdadır. Lütfen birinin diğerinden daha iyi olduğunu varsaymayın ... :-)
Durum böyle olsaydı, Dennis Ritchie en iyisini yalnız tutacaktır ... hahaha ... :-)
const
hafızayı kullandığı doğru değildir (artık) . GCC (4.5.3 ve birkaç yeni sürümle test edilmiştir) const int
-O3 kullanırken kolayca kodunuzdaki doğrudan değişmez değeri optimize eder . Dolayısıyla, düşük RAM katıştırılmış geliştirme (örneğin AVR) yaparsanız, GCC veya başka bir uyumlu derleyici kullanıyorsanız C consts'ı güvenle kullanabilirsiniz. Ben test etmedim ama Clang aynı şeyi btw yapmasını bekliyoruz.
C'de #define
çok daha popüler. Bu değerleri dizi boyutlarını bildirmek için kullanabilirsiniz, örneğin:
#define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
}
ANSI C static const
bildiğim kadarıyla bu bağlamda kullanmanıza izin vermiyor . C ++ 'da bu durumlarda makrolardan kaçınmalısınız. Yazabilirsin
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
ve hatta static
iç bağlantı const
zaten [sadece C ++ ile] ima edildiği için bile dışarıda kalır .
const int MY_CONSTANT = 5;
tek dosyada ve bunu erişmek extern const int MY_CONSTANT;
başka. Standartta (en azından C99) const
varsayılan davranışı değiştirmeyle ilgili hiçbir bilgi bulamadım "6.2.2: 5 Bir nesne için tanımlayıcının bildirimi dosya kapsamına sahipse ve depolama sınıfı belirticisi yoksa, bağlantısı harici" dir.
bar
bir VLA (değişken uzunluk dizisi); derleyici, uzunluğu sabitmiş gibi kod üretebilir.
const
C'nin bir başka dezavantajı , değeri bir başkasını başlatırken kullanamamanızdır 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;
Derleyici sabit olarak görmediğinden bu bile bir const ile çalışmaz:
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!
const
Bu durumlarda yazılan kullanmaktan mutluluk duyarım , aksi takdirde ...
static uint8_t const ARRAY_SIZE = 16;
birdenbire derlenmeyeceğinizi takip etmek biraz zor olabilir, özellikle de #define ARRAY_SIZE 256
karışık bir başlık ağında on kat derinlere gömüldüğünde. Tüm büyük harflerin isminin ARRAY_SIZE
belada olduğunu sorar. ALL_CAPS'u makrolar için ayırın ve hiçbir zaman ALL_CAPS formunda olmayan bir makro tanımlamayın.
const
. Bu daha fazla iptal edilebilir!
Eğer ondan kurtulabilirsen, static const
, bir çok avantajı var. Normal kapsam ilkelerine uyar, bir hata ayıklayıcıda görünür ve genellikle değişkenlerin uyduğu kurallara uyar.
Bununla birlikte, en azından orijinal C standardında, aslında bir sabit değildir. Eğer kullanırsanız #define var 5
, yazabilir int foo[var];
bir deklarasyon olarak, ancak bir derleyici uzantısı" olarak haricinde (bunu yapamaz static const int var = 5;
. Bu ++, C durum böyle değildir static const
versiyonu her yerde kullanılabilir#define
versiyonu kutu ve ben buna inanıyorum C99 için de geçerlidir.
Bununla birlikte, asla bir #define
sabiti küçük harfli isimle adlandırmayın. Çeviri biriminin sonuna kadar bu adın olası kullanımını geçersiz kılacaktır. Makro sabitleri, geleneksel olarak tüm büyük harfler, belki de bir önek ile kendi ad alanı etkili bir şekilde olmalıdır.
const
C99'da hala gerçek bir sabit değil. Dizi boyutunu const
C99'da bir ile bildirebilirsiniz , ancak yalnızca C99 Değişken Uzunluk Dizilerini desteklediğinden. Bu nedenle, sadece VLA'lara izin verilen yerlerde çalışacaktır. Örneğin, C99'da bile, a öğesindeki const
bir üye dizisinin boyutunu bildirmek için yine de kullanamazsınız struct
.
const int
bir C ++ const veya makroymuş gibi bir boyutu olan dizileri mükemmel bir şekilde başlatmanıza izin verecektir . GCC'nin standarttan bu sapmasına bağlı kalmak isteyip istemediğiniz elbette seçiminizdir, GCC veya Clang'dan başka bir derleyici kullanmayı gerçekten düşünmedikçe, kişisel olarak onunla birlikte giderim, ikincisi burada aynı özelliğe sahiptir (Clang ile test edilmiştir) 3.7).
#Define yerine const kullanmak DAİMA tercih edilir. Çünkü const derleyici tarafından işlenir ve #define önişlemci tarafından işlenir. Bu #define kendisi kodun bir parçası değildir (kabaca konuşursak).
Misal:
#define PI 3.1416
PI sembolik ismi derleyiciler tarafından asla görülemez; kaynak kod bir derleyiciye ulaşmadan önce önişlemci tarafından kaldırılabilir. Sonuç olarak, sembol tablosuna PI adı girilemeyebilir. Sabitin kullanımını içeren derleme sırasında bir hata alırsanız bu kafa karıştırıcı olabilir, çünkü hata mesajı PI değil 3.1416'ya işaret edebilir. PI, yazmadığınız bir başlık dosyasında tanımlanmış olsaydı, 3.1416'nın nereden geldiğine dair hiçbir fikriniz olmazdı.
Bu sorun sembolik bir hata ayıklayıcıda da ortaya çıkabilir, çünkü yine programladığınız ad sembol tablosunda olmayabilir.
Çözüm:
const double PI = 3.1416; //or static const...
#define var 5
gibi şeyleriniz varsa size sorun çıkarır mystruct.var
.
Örneğin,
struct mystruct {
int var;
};
#define var 5
int main() {
struct mystruct foo;
foo.var = 1;
return 0;
}
Önişlemci onun yerini alacak ve kod derlenmeyecektir. Bu nedenle, geleneksel kodlama stili, tüm sabit #define
s'lerin çatışmayı önlemek için büyük harfler kullanmasını önerir .
Bir farkı göstermek için hızlı test programı yazdım:
#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);
}
Bu, aşağıdaki hatalar ve uyarılarla derlenir:
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
^
Define bir uyarı verdiğinde enum'un hata verdiğini unutmayın.
Tanım
const int const_value = 5;
her zaman sabit bir değer tanımlamaz. Bazı derleyiciler (örneğin tcc 0.9.26 ) sadece "const_value" adıyla tanımlanan belleği ayırır. "Const_value" tanımlayıcısını kullanarak bu belleği değiştiremezsiniz. Ancak yine de belleği başka bir tanımlayıcı kullanarak değiştirebilirsiniz:
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.
Bu tanım
#define CONST_VALUE 5
hiçbir şekilde değiştirilemeyen sabit bir değer tanımlamanın tek yoludur.
#define
, makine kodunu düzenleyerek de değiştirebilirsiniz.
5
. Ancak #define
bir önişlemci makrosu olduğu için değişiklik yapılamaz . İkili programda mevcut değildir. Eğer CONST_VALUE
kullanılan tüm yerleri değiştirmek isterse, bunu tek tek yapmak zorundaydı.
#define CONST 5
sonra, if (CONST == 5) { do_this(); } else { do_that(); }
ve derleyici ortadan kaldırır else
dalı. Makine kodunu CONST
6 olarak değiştirmek için düzenlemeyi nasıl öneriyorsunuz ?
#define
kurşun geçirmez değil.
#define
. Bunu yapmanın tek gerçek yolu kaynak kodunu düzenlemek ve yeniden derlemektir.
Her ne kadar soru tamsayılarla ilgili olsa da, sabit bir yapıya veya dizgiye ihtiyacınız varsa #define ve enums'un işe yaramaz olduğunu belirtmek gerekir. Bunların her ikisi de genellikle işaretçi olarak işlevlere geçirilir. (Dizelerle gereklidir; yapılarla çok daha verimlidir.)
Tamsayılara gelince, çok sınırlı belleğe sahip gömülü bir ortamdaysanız, sabitin nerede depolandığı ve ona erişimin nasıl derlendiği konusunda endişelenmeniz gerekebilir. Derleyici çalışma zamanında iki const ekleyebilir, ancak derleme zamanında iki #define ekleyebilir. Bir #define sabiti bir veya daha fazla MOV [acil] komutuna dönüştürülebilir, bu sabitin program belleğinde etkili bir şekilde saklandığı anlamına gelir. Bir const sabiti, veri belleğindeki .const bölümünde saklanır. Harvard mimarisine sahip sistemlerde, küçük olmalarına rağmen performans ve bellek kullanımında farklılıklar olabilir. İç döngülerin sert çekirdek optimizasyonu için önemli olabilirler.
"Her zaman en iyisi" için bir cevap olduğunu düşünmeyin, ancak Matthieu'nun dediği gibi
static const
güvenli tiptir. #define
Bununla birlikte , en büyük evcil hayvanım , Visual Studio'da hata ayıklama sırasında değişkeni izleyememeniz. Sembolün bulunamaması hatası verir.
Bu arada, #define
uygun kapsam belirleme sağlayan ancak "gerçek" sabit gibi davranan bir alternatif "enum" dur. Örneğin:
enum {number_ten = 10;}
Çoğu durumda, numaralandırılmış türleri tanımlamak ve bu türlerden değişkenler oluşturmak yararlıdır; bu yapılırsa, hata ayıklayıcılar numaralandırma adlarına göre değişkenleri görüntüleyebilir.
Bununla birlikte, bununla ilgili önemli bir uyarı: C ++ 'da, numaralandırılmış türlerin tamsayılarla sınırlı uyumluluğu vardır. Örneğin, varsayılan olarak, bunlarda aritmetik yapılamaz. Ben enums için meraklı bir varsayılan davranış olduğunu düşünüyorum; "sıkı enum" türü olması güzel olurdu, C ++ genellikle C ile uyumlu olma arzusu göz önüne alındığında, bir "enum" türü varsayılan davranış tamsayı ile değiştirilebilir olması gerektiğini düşünüyorum.
int
türdedir, bu nedenle "enum hack" diğer tamsayı türleriyle kullanılamaz. (Numaralandırma türü , zorunlu olarak değil, bazı uygulama tanımlı tamsayı türüyle uyumludur int
, ancak bu durumda tür anonimdir, bu nedenle önemli değildir.)
int
numaralandırma türünde bir değişkenden başka bir tür atadığında (derleyicilerin yapmasına izin verilir) MISRA-C'nin squawk okuyacağını ve böyle bir değişkene atanmaya çalıştığını okudum kendi numaralandırmasının bir üyesi. Standart komitelerinin belirtilen semantiklerle tamsayı türlerini bildirmenin taşınabilir yollarını eklemesini isterdim. HERHANGİ BİR platform, char
boyutuna bakılmaksızın , derleyici çok sayıda AND R0,#0xFFFF
veya eşdeğer talimat eklemek zorunda olsa bile, örneğin 65536'yı saran bir tür bildirebilmelidir .
uint16_t
bir numaralandırma türü değil elbette olsa. Kullanıcının belirli bir numaralandırma türünü temsil etmek için kullanılan tamsayı türünü belirtmesine izin vermek güzel olurdu, ancak tek tek değerler için bir typedef
for uint16_t
ve #define
s serisi ile aynı etkinin çoğunu elde edebilirsiniz .
2U < -1L
doğru ve diğerlerini yanlış olarak değerlendireceğinden ve şu anda bazı platformların imzalanmış uint32_t
ve int32_t
imzalanmış olarak bir karşılaştırma yapmasıyla şaşırdık. bazıları imzasızdır, ancak bu Komite, anlambilimi tüm derleyicilerde tutarlı olacak türleri içeren C'ye yukarı doğru uyumlu bir halef tanımlayamayacağı anlamına gelmez.
Basit bir fark:
Ön işleme zamanında sabit, değeri ile değiştirilir. Bu nedenle, bir tanımlamaya dereference operatörünü uygulayamazsınız, ancak dereference operatörünü bir değişkene uygulayabilirsiniz.
Tahmin edeceğiniz gibi, tanım statik sabitten daha hızlıdır.
Örneğin:
#define mymax 100
yapamazsın printf("address of constant is %p",&mymax);
.
Ama sahip olmak
const int mymax_var=100
yapabilirsin printf("address of constant is %p",&mymax_var);
.
Daha açık olmak gerekirse, tanım, ön işleme aşamasında değeri ile değiştirilir, bu nedenle programda depolanan herhangi bir değişkenimiz yoktur. Sadece tanımın kullanıldığı programın metin bölümündeki koda sahibiz.
Ancak, statik sabit için bir yere tahsis edilen bir değişkenimiz var. Gcc için, statik sabit programın metin bölümüne ayrılır.
Yukarıda, referans operatörü hakkında bilgi vermek istedim, bu yüzden referansı referansla değiştirin.
const
niteleyici için çok farklı semantiğe sahip C ++ ile ilgilidir . C, enum-sabitleri dışında sembolik sabitlere sahip değildir . A const int
bir değişkendir. Dil ve belirli uygulamaları da karıştırıyorsunuz. Nesnenin nereye yerleştirileceğine gerek yoktur. Ve gcc için bile doğru değildir: tipik olarak bölüme const
nitelikli değişkenler yerleştirir .rodata
. Ancak bu, hedef platforma bağlıdır. Ve operatörün adresi demek istiyorsun &
.
MBF16X üzerinde üretilen montajcı koduna baktık ... Her iki varyant da aritmetik işlemler için aynı kodla sonuçlanır (örneğin, ADD Immediate).
Yani const int
iken tip kontrolü için tercih edilmektedir #define
eski tarzıdır. Belki derleyiciye özgüdür. Ürettiğiniz montajcı kodunu kontrol edin.
Ben haklı olup olmadığından emin değilim ama benim görüşüme göre #define
d değeri çağırmak herhangi bir normalde bildirilen değişken (veya const değeri) çağırmak çok daha hızlıdır. Program çalışırken ve normal olarak bildirilen bir değişken kullanması gerektiğinde, bu değişkeni almak için bellekte tam bir yere atlaması gerekir.
Tersine, #define
d değerini kullandığında , programın ayrılmış bir belleğe atlaması gerekmez, sadece değeri alır. Eğer #define myValue 7
ve program çağırıyorsa myValue
, sadece aradığı zamanki gibi davranır 7
.