Quale è meglio usare tra le seguenti affermazioni in C?
static const int var = 5;
o
#define var 5
o
enum { var = 5 };
Quale è meglio usare tra le seguenti affermazioni in C?
static const int var = 5;
o
#define var 5
o
enum { var = 5 };
Risposte:
Dipende da cosa ti serve il valore. Tu (e tutti gli altri finora) hai omesso la terza alternativa:
static const int var = 5;
#define var 5
enum { var = 5 };
Ignorando i problemi relativi alla scelta del nome, quindi:
Quindi, nella maggior parte dei contesti, preferisce l '"enum" rispetto alle alternative. Altrimenti, è probabile che il primo e l'ultimo punto elenco siano i fattori di controllo - e devi pensare di più se devi soddisfare entrambi contemporaneamente.
Se chiedessi informazioni su C ++, useresti sempre l'opzione (1) - la const statica.
enum
è che sono implementati come int
([C99] 6.7.2.2/3). A #define
consente di specificare unsigned, long with U
e L
suffixes e const
consente di specificare un tipo. enum
può causare problemi con le normali conversioni di tipo.
enum
né #define
utilizza spazio extra, di per sé. Il valore verrà visualizzato nel codice oggetto come parte delle istruzioni anziché essere allocato nella memoria nel segmento di dati o nell'heap o nello stack. Avrai dello spazio assegnato per il static const int
, ma il compilatore potrebbe ottimizzarlo se non prendi un indirizzo.
enum
s (e static const
): non possono essere cambiati. a define
può essere #undefine
'd dove an enum
e static const
sono fissi sul valore dato.
Parlando in generale:
static const
Perché rispetta l'ambito ed è sicuro per i tipi.
L'unica avvertenza che ho potuto vedere: se si desidera che la variabile possa essere definita sulla riga di comando. C'è ancora un'alternativa:
#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
Quando possibile, anziché macro / puntini di sospensione, utilizzare un'alternativa sicura per tipo.
Se hai davvero BISOGNO di andare con una macro (ad esempio, vuoi __FILE__
o __LINE__
), allora faresti meglio a nominare la tua macro MOLTO attentamente: nella sua convenzione di denominazione Boost raccomanda tutte le lettere maiuscole, a partire dal nome del progetto (qui BOOST_ ), mentre sfogli la libreria noterai che questo è (generalmente) seguito dal nome della particolare area (libreria), quindi con un nome significativo.
In genere crea nomi lunghi :)
static
dovrebbero rimanere solo quelli il cui indirizzo è preso; e se l'indirizzo è stato preso uno non avrebbe potuto usare un #define
o enum
(nessun indirizzo) ... quindi non riesco davvero a vedere quale alternativa avrebbe potuto essere usato. Se riesci a eliminare la "valutazione del tempo di compilazione", potresti invece cercarla extern const
.
#if
potrebbe essere preferibile rispetto #ifdef
ai flag booleani, ma in questo caso renderebbe impossibile definire var
come 0
dalla riga di comando. Quindi, in questo caso, #ifdef
ha più senso, purché 0
sia un valore legale per var
.
In C, in particolare? In C la risposta corretta è: usare #define
(o, se appropriato, enum
)
Mentre è utile avere le proprietà di scoping e typing di un const
oggetto, in realtà gli const
oggetti in C (in contrapposizione a C ++) non sono costanti vere e quindi sono generalmente inutili nella maggior parte dei casi pratici.
Quindi, in C la scelta dovrebbe essere determinata da come pensi di usare la tua costante. Ad esempio, non è possibile utilizzare un const int
oggetto come case
etichetta (mentre una macro funzionerà). Non è possibile utilizzare un const int
oggetto come larghezza del campo bit (mentre una macro funzionerà). In C89 / 90 non è possibile utilizzare un const
oggetto per specificare una dimensione dell'array (mentre una macro funzionerà). Anche in C99 non è possibile utilizzare un const
oggetto per specificare una dimensione dell'array quando è necessario un array non VLA .
Se questo è importante per te, determinerà la tua scelta. Il più delle volte, non avrai altra scelta che usare #define
in C. E non dimenticare un'altra alternativa, che produce vere costanti in C - enum
.
In C ++ gli const
oggetti sono costanti vere, quindi in C ++ è quasi sempre meglio preferire la const
variante (non c'è bisogno di esplicito static
in C ++).
const int
oggetti nelle etichette dei casi è illegale in tutte le versioni del linguaggio C. (Ovviamente, il tuo compilatore è libero di supportarlo come estensione non standard del linguaggio C ++.)
const
significa sola lettura. const int r = rand();
è perfettamente legale.
constexpr
rispetto a const
specialmente con i stl
contenitori come array
o bitset
.
switch()
dichiarazione, non in case
una. Anch'io sono stato catturato da questo ☺
La differenza tra static const
e #define
è che il primo utilizza la memoria e il successivo non utilizza la memoria per l'archiviazione. In secondo luogo, non è possibile passare l'indirizzo di un #define
mentre è possibile passare l'indirizzo di un static const
. In realtà dipende dalle circostanze in cui ci troviamo, dobbiamo selezionarne una tra queste due. Entrambi sono al meglio in circostanze diverse. Per favore, non dare per scontato che uno sia migliore dell'altro ... :-)
Se così fosse, Dennis Ritchie avrebbe tenuto da solo il migliore ... hahaha ... :-)
const
usi la memoria. GCC (testato con 4.5.3 e alcune versioni più recenti) ottimizza facilmente il valore const int
letterale diretto nel codice quando si utilizza -O3. Pertanto, se si esegue uno sviluppo integrato con RAM ridotta (ad es. AVR), è possibile utilizzare in modo sicuro i costi C se si utilizza GCC o un altro compilatore compatibile. Non l'ho provato ma mi aspetto che Clang faccia la stessa cosa tra l'altro.
In C #define
è molto più popolare. Ad esempio, è possibile utilizzare questi valori per dichiarare le dimensioni dell'array:
#define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
}
ANSI C non ti consente di utilizzare static const
s in questo contesto per quanto ne so. In C ++ dovresti evitare le macro in questi casi. Tu puoi scrivere
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
e addirittura tralasciamo static
perché il collegamento interno è const
già implicito [solo in C ++].
const int MY_CONSTANT = 5;
in un file e accedervi con extern const int MY_CONSTANT;
in un altro. Non sono riuscito a trovare alcuna informazione nello standard (almeno C99) sulla const
modifica del comportamento predefinito "6.2.2: 5 Se la dichiarazione di un identificatore per un oggetto ha un ambito file e nessuno speci fi catore di classe di archiviazione, il suo collegamento è esterno".
bar
è un VLA (array di lunghezza variabile); è probabile che il compilatore generi codice come se la sua lunghezza fosse costante.
Un altro svantaggio di const
in C è che non è possibile utilizzare il valore per inizializzare un altro 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;
Anche questo non funziona con una const poiché il compilatore non la vede come una costante:
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!
Sarei felice di usare dattiloscritto const
in questi casi, altrimenti ...
static uint8_t const ARRAY_SIZE = 16;
all'improvviso non si compila più può essere un po 'impegnativo, in particolare quando #define ARRAY_SIZE 256
sono sepolti dieci strati in profondità in una trama intricata di intestazioni. Che il nome di tutti i tappi ARRAY_SIZE
richiede problemi. Prenota ALL_CAPS per le macro e non definisci mai una macro che non è nel modulo ALL_CAPS.
const
. Questo potrebbe essere più votato!
Se riesci a cavartela, static const
ha molti vantaggi. Obbedisce ai normali principi dell'ambito, è visibile in un debugger e generalmente obbedisce alle regole alle quali le variabili obbediscono.
Tuttavia, almeno nello standard C originale, in realtà non è una costante. Se lo usi #define var 5
, puoi scrivere int foo[var];
come una dichiarazione, ma non puoi farlo (tranne che come estensione del compilatore "con static const int var = 5;
. Questo non è il caso in C ++, dove la static const
versione può essere usata ovunque la #define
versione possa, e credo che questo è anche il caso di C99.
Tuttavia, non nominare mai una #define
costante con un nome minuscolo. Sostituirà qualsiasi possibile uso di quel nome fino alla fine dell'unità di traduzione. Le costanti macro devono trovarsi in quello che è effettivamente il loro spazio dei nomi, che è tradizionalmente tutte le lettere maiuscole, forse con un prefisso.
const
in C99 non è ancora una vera costante. È possibile dichiarare la dimensione dell'array con a const
in C99, ma solo perché C99 supporta array a lunghezza variabile. Per questo motivo, funzionerà solo dove sono consentiti i VLA. Ad esempio, anche in C99, non è ancora possibile utilizzare a const
per dichiarare la dimensione di un array di membri in a struct
.
const int
dimensione come se fosse una const C ++ o una macro. Indipendentemente dal fatto che tu voglia dipendere da questa deviazione di GCC dallo standard, è una tua scelta, personalmente lo farei a meno che tu non possa davvero rinunciare a usare un altro compilatore di GCC o Clang, quest'ultimo ha la stessa funzionalità qui (testato con Clang 3.7).
È SEMPRE preferibile usare const, invece di #define. Questo perché const viene trattata dal compilatore e #define dal preprocessore. È come se #define non facesse parte del codice (in termini approssimativi).
Esempio:
#define PI 3.1416
Il nome simbolico PI non può mai essere visto dai compilatori; può essere rimosso dal preprocessore prima che il codice sorgente arrivi anche a un compilatore. Di conseguenza, il nome PI potrebbe non essere inserito nella tabella dei simboli. Questo può essere fonte di confusione se durante la compilazione viene visualizzato un errore che comporta l'uso della costante, poiché il messaggio di errore può fare riferimento a 3.1416, non a PI. Se il PI fosse definito in un file di intestazione che non hai scritto, non avresti idea da dove provenisse quel 3.1416.
Questo problema può anche insorgere in un debugger simbolico, perché, di nuovo, il nome con cui stai programmando potrebbe non essere nella tabella dei simboli.
Soluzione:
const double PI = 3.1416; //or static const...
#define var 5
ti causerà problemi se hai cose del genere mystruct.var
.
Per esempio,
struct mystruct {
int var;
};
#define var 5
int main() {
struct mystruct foo;
foo.var = 1;
return 0;
}
Il preprocessore lo sostituirà e il codice non verrà compilato. Per questo motivo, lo stile di codifica tradizionale suggerisce che tutte le costanti #define
usano lettere maiuscole per evitare conflitti.
Ho scritto un programma di test rapido per dimostrare una differenza:
#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);
}
Questo compila con questi errori e avvertenze:
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
^
Si noti che enum genera un errore quando define dà un avviso.
La definizione
const int const_value = 5;
non definisce sempre un valore costante. Alcuni compilatori (ad esempio tcc 0.9.26 ) allocano solo la memoria identificata con il nome "const_value". Utilizzando l'identificatore "const_value" non è possibile modificare questa memoria. Ma potresti comunque modificare la memoria usando un altro identificatore:
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.
Questo significa la definizione
#define CONST_VALUE 5
è l'unico modo per definire un valore costante che non può essere modificato in alcun modo.
#define
può anche essere modificato, modificando il codice macchina.
5
. Ma non si può modificare #define
perché è una macro preprocessore. Non esiste nel programma binario. Se si voleva modificare tutti i luoghi in cui CONST_VALUE
veniva utilizzato, si doveva farlo uno per uno.
#define CONST 5
, quindi if (CONST == 5) { do_this(); } else { do_that(); }
, e il compilatore elimini il else
ramo. Come proponete di modificare il codice macchina per passare CONST
a 6?
#define
non è a prova di proiettile.
#define
. L'unico vero modo per farlo è modificare il codice sorgente e ricompilare.
Sebbene la domanda riguardasse numeri interi, vale la pena notare che #define ed enum sono inutili se hai bisogno di una struttura o stringa costante. Entrambi vengono in genere passati a funzioni come puntatori. (Con le stringhe è richiesto; con le strutture è molto più efficiente.)
Per quanto riguarda gli interi, se ti trovi in un ambiente incorporato con una memoria molto limitata, potresti doverti preoccupare di dove è memorizzata la costante e di come vengono compilati gli accessi ad essa. Il compilatore potrebbe aggiungere due contro in fase di esecuzione, ma aggiungere due #define in fase di compilazione. Una costante #define può essere convertita in una o più istruzioni MOV [immediate], il che significa che la costante è effettivamente memorizzata nella memoria del programma. Una costante const verrà memorizzata nella sezione .const nella memoria dati. Nei sistemi con architettura Harvard, potrebbero esserci differenze nelle prestazioni e nell'utilizzo della memoria, anche se probabilmente sarebbero piccole. Possono essere importanti per l'ottimizzazione del core degli anelli interni.
Non pensare che ci sia una risposta per "che è sempre la cosa migliore" ma, come ha detto Matthieu
static const
è sicuro. Il mio più grande problema con gli animali domestici #define
, tuttavia, è quando il debug in Visual Studio non è possibile guardare la variabile. Dà un errore che il simbolo non può essere trovato.
Per inciso, un'alternativa a #define
, che fornisce l'ambito corretto ma si comporta come una costante "reale", è "enum". Per esempio:
enum {number_ten = 10;}
In molti casi, è utile definire tipi enumerati e creare variabili di tali tipi; in tal caso, i debugger potrebbero essere in grado di visualizzare le variabili in base al nome dell'enumerazione.
Un avvertimento importante nel farlo, tuttavia: in C ++, i tipi enumerati hanno una compatibilità limitata con numeri interi. Ad esempio, per impostazione predefinita, non è possibile eseguire l'aritmetica su di essi. Trovo che sia un curioso comportamento predefinito per gli enum; mentre sarebbe stato bello avere un tipo "enum rigoroso", dato il desiderio di avere C ++ generalmente compatibile con C, penso che il comportamento predefinito di un tipo "enum" dovrebbe essere intercambiabile con numeri interi.
int
, quindi "enum hack" non può essere usato con altri tipi di numeri interi. (Il tipo di enumerazione è compatibile con un tipo intero definito dall'implementazione, non necessariamente int
, ma in questo caso il tipo è anonimo quindi non ha importanza.)
int
una variabile tipizzata da enumerazione (che i compilatori sono autorizzati a fare) e si tenta di assegnare a tale variabile un membro della propria enumerazione. Vorrei che i comitati standard aggiungessero modi portatili per dichiarare i tipi interi con la semantica specificata. QUALSIASI piattaforma, indipendentemente dalle char
dimensioni, dovrebbe essere in grado di dichiarare, ad esempio, un tipo che avvolgerà la mod 65536, anche se il compilatore deve aggiungere molte AND R0,#0xFFFF
istruzioni equivalenti.
uint16_t
, anche se ovviamente non è un tipo di enumerazione. Sarebbe bello consentire all'utente di specificare il tipo intero utilizzato per rappresentare un determinato tipo di enumerazione, ma è possibile ottenere quasi lo stesso effetto con un typedef
for uint16_t
e una serie di #define
s per i singoli valori.
2U < -1L
come vere e altre come false, e ora siamo bloccati dal fatto che alcune piattaforme implementeranno un confronto tra uint32_t
e int32_t
come firmato e alcuni come non firmati, ma ciò non significa che il Comitato non sia stato in grado di definire un successore verso l'alto compatibile con C che includa tipi la cui semantica sarebbe coerente su tutti i compilatori.
Una semplice differenza:
Al momento della pre-elaborazione, la costante viene sostituita con il suo valore. Quindi non è possibile applicare l'operatore di dereference a una definizione, ma è possibile applicare l'operatore di dereference a una variabile.
Come si suppone, definire è più veloce della const statica.
Ad esempio, avendo:
#define mymax 100
non puoi fare printf("address of constant is %p",&mymax);
.
Ma avendo
const int mymax_var=100
puoi fare printf("address of constant is %p",&mymax_var);
.
Per essere più chiari, la definizione viene sostituita dal suo valore nella fase di pre-elaborazione, quindi non abbiamo alcuna variabile memorizzata nel programma. Abbiamo solo il codice dal segmento di testo del programma in cui è stato utilizzato il parametro di definizione.
Tuttavia, per const statici abbiamo una variabile che è allocata da qualche parte. Per gcc, const statici sono allocati nel segmento di testo del programma.
Sopra, volevo parlare dell'operatore di riferimento, quindi sostituisci la dereferenza con il riferimento.
const
qualificatore. C non ha costanti simboliche diverse dalle enum-costanti . A const int
è una variabile. Confondi anche il linguaggio e le implementazioni specifiche. Non è richiesto dove posizionare l'oggetto. E non è nemmeno vero per gcc: in genere inserisce const
variabili qualificate nella .rodata
sezione. Ma ciò dipende dalla piattaforma di destinazione. E intendi l'indirizzo dell'operatore &
.
Abbiamo esaminato il codice assemblatore prodotto sull'MBF16X ... Entrambe le varianti danno lo stesso codice per le operazioni aritmetiche (ad esempio ADD Immediate).
Quindi const int
è preferito per il controllo del tipo mentre #define
è vecchio stile. Forse è specifico del compilatore. Quindi controlla il tuo codice assemblatore prodotto.
Non sono sicuro di avere ragione, ma secondo me chiamare il #define
valore d è molto più veloce che chiamare qualsiasi altra variabile (o valore const) normalmente dichiarata. È perché quando il programma è in esecuzione e deve usare alcune variabili normalmente dichiarate, deve saltare al posto esatto in memoria per ottenere quella variabile.
Al contrario, quando usa il #define
valore d, il programma non ha bisogno di saltare a nessuna memoria allocata, prende semplicemente il valore. Se #define myValue 7
e il programma chiama myValue
, si comporta esattamente come quando chiama 7
.