Preferisco che le costanti siano definite?

In C, preferisco che le costanti siano definite? Ultimamente ho letto molto codice e tutti gli esempi fanno un uso pesante di define.

No, in generale non si dovrebbero usare oggetti con abilitazione costante in C per creare costanti di nomi. Per creare una costante con nome in C, dovresti usare sia macro ( #define ) che enumerazioni. In effetti, il linguaggio C non ha costanti, nel senso che sembra implicare. (C è significativamente diverso da C ++ in questo senso)

Nel linguaggio C le nozioni di espressione costante e costante sono definite in modo molto diverso dal C ++. In costante C significa un valore letterale , come 123 . Ecco alcuni esempi di costanti in C

 123 34.58 'x' 

Le costanti in C possono essere utilizzate per creare espressioni costanti . Tuttavia, poiché gli oggetti const-qualificati di qualsiasi tipo non sono costanti in C, non possono essere utilizzati in espressioni costanti e, di conseguenza, non è ansible utilizzare oggetti con const-cost dove sono richieste espressioni costanti.

Ad esempio, il seguente non è una costante

 const int C = 123; /* C is not a constant!!! */ 

e poiché la C precedente non è una costante, non può essere utilizzata per dichiarare un tipo di matrice nell’ambito del file

 typedef int TArray[C]; /* ERROR: constant expression required */ 

Non può essere usato come etichetta del caso

 switch (i) { case C: ; /* ERROR: constant expression required */ } 

Non può essere usato come larghezza del campo di bit

 struct S { int f : C; /* ERROR: constant expression required */ }; 

Non può essere utilizzato come inizializzatore per un object con durata di archiviazione statica

 static int i = C; /* ERROR: constant expression required */ 

Non può essere utilizzato come inizializzatore di enum

 enum { E = C /* ERROR: constant expression required */ }; 

cioè non può essere utilizzato ovunque sia richiesta una costante .

Questo potrebbe sembrare contro-intuitivo, ma è così che viene definito il linguaggio C.

Questo è il motivo per cui vedi questi numerosi #define -s nel codice con cui stai lavorando. Ancora una volta, nel linguaggio C l’object qualificato const ha un uso molto limitato. Sono fondamentalmente completamente inutili come “costanti”, ed è per questo che in linguaggio C sei praticamente costretto a usare #define o enumer per dichiarare le costanti vere.

Ovviamente, in situazioni in cui un object con qualifica costante funziona per te, cioè fa ciò che vuoi che faccia, è effettivamente superiore alle macro in molti modi, dato che è ambito e digitato. Probabilmente dovresti preferire tali oggetti laddove applicabile, ma in generale dovrai tener conto delle limitazioni sopra elencate.

Le costanti dovrebbero essere preferite per define s. Ci sono diversi vantaggi:

  • Digitare sicurezza . Mentre C è indebolito tipicamente, usando un define perdo tutto il tipo safety, che permetterà al compilatore di raccogliere problemi per te.

  • Facilità di debug . È ansible modificare il valore delle costanti tramite il debugger, mentre le define s vengono automaticamente modificate nel codice dal pre-processore al valore effettivo , il che significa che se si desidera modificare il valore per scopi di test / debug, è necessario compilare.

Forse li ho usati male ma, almeno in gcc, non è ansible usare le costanti nelle istruzioni case.

 const int A=12; switch (argc) { case A: break; } 

Sebbene questa domanda sia specifica per C, immagino sia bello sapere questo:

 #include int main() { const int CON = 123; int* A = &CON; (*A)++; printf("%d\n", CON); // 124 in C } 

funziona in C , ma non in C ++

Uno dei motivi per usare #define è evitare che tali cose rovinino il tuo codice, specialmente se si tratta di un mix di C e C ++.

Un sacco di gente qui ti sta dando consigli “stile C ++”. Alcuni dicono che gli argomenti del C ++ si applicano a C. Questo potrebbe essere un punto giusto. (Che si tratti o meno di qualcosa di soggettivo.) Le persone che dicono const volte significano qualcosa di diverso nelle due lingue sono anche corrette.

Ma questi sono per lo più punti minori e personalmente, penso che in realtà ci sia una conseguenza relativamente secondaria ad andare in entrambe le direzioni. È una questione di stile e penso che diversi gruppi di persone ti daranno risposte diverse.

In termini di utilizzo comune, utilizzo storico e stile più comune, in C, è molto più tipico vedere #define . Usare gli isistemi C ++ nel codice C può risultare strano per un certo segmento ristretto di codificatori C. (Compreso me, ecco dove sono i miei pregiudizi.)

Ma sono sorpreso che nessuno abbia suggerito una soluzione di medio livello, che “sembra giusto” in entrambe le lingue: se si inserisce in un gruppo di costanti intere, usa un enum .

define può essere usato per molti scopi (molto loose) e dovrebbe essere evitato se è ansible sostituirlo con const, che definisce una variabile e si può fare molto di più con esso.

Nei casi come sotto, definire deve essere usato

  • interruttore di direttiva
  • sostituzione alla tua linea sorgente
  • macro di codice

Un esempio in cui è necessario definire su const è quando il numero di versione dice 3 e si desidera che la versione 4 includa alcuni metodi che non sono disponibili nella versione 3

 #define VERSION 4 ... #if VERSION==4 ................ #endif 

Le definizioni hanno fatto parte della lingua più a lungo delle costanti, quindi un sacco di codice più vecchio le userà perché definisce dove l’unico modo per ottenere il lavoro quando il codice è stato scritto. Per codice più recente potrebbe essere semplicemente una questione di abitudine del programmatore.

Le costanti hanno un tipo e un valore, quindi sarebbero preferibili quando ha senso che il tuo valore abbia un tipo, ma non quando è senza caratteri (o polimorfico).

Se è qualcosa che non è determinato a livello di codice, io uso #define . Ad esempio, se voglio che tutti i miei oggetti UI abbiano lo stesso spazio tra di loro, potrei usare #define kGUISpace 20 .

Oltre alle ottime ragioni fornite da AndreyT per l’uso di DEFINES piuttosto che di costanti nel codice “C”, esiste un altro motivo più pragmatico per l’utilizzo di DEFINES.

Le DEFINI sono facili da definire e utilizzare dai file di intestazione (.h), che è dove ogni codificatore C esperto si aspetterebbe di trovare le costanti definite. Definire le Sali nei file di intestazione non è così facile – è più codice per evitare definizioni duplicate, ecc.

Anche gli argomenti “typesafe” sono discutibili. La maggior parte dei compilatori raccoglierà errori evidenti come asserire una stringa a e int, o “farà la cosa giusta” su una leggera discrepanza come l’assegnazione di un intero a un float.

Le macro (definite) possono essere utilizzate dal pre-processore e al momento della compilazione, le costanti non possono.

Puoi fare controlli in fase di compilazione per assicurarti che una macro sia all’interno di un intervallo valido (e #error o #fatal se non lo è). È ansible utilizzare i valori predefiniti per una macro se non è già stata definita. È ansible utilizzare una macro con le dimensioni di un array.

Un compilatore può ottimizzare con macro meglio di quello che può con le costanti:

 const int SIZE_A = 15; #define SIZE_B 15 for (i = 0; i < SIZE_A + 1; ++i); // if not optimized may load A and add 1 on each pass for (i = 0; i < SIZE_B + 1; ++i); // compiler will replace "SIZE_B + 1" with 16 

La maggior parte del mio lavoro è con processori embedded che non hanno straordinari compilatori di ottimizzazione. Forse gcc tratterà SIZE_A come una macro con qualche livello di ottimizzazione.