È ansible utilizzare una variabile const per dichiarare la dimensione di un array in C?

Perché il seguente codice genera un errore?

const int a = 5; int b[a]={1,2,3,4,5}; 

E anche quando ho provato a compilare il codice sopra senza la parola chiave “const”, ho ottenuto lo stesso errore:

 int a = 5; int b[a]={1,2,3,4,5}; 

perché è così? Qual è l’errore che sto facendo qui?

E anche un’altra domanda: quando le costanti vengono sostituite con i loro valori reali in un codice, cioè se dichiaro una variabile dire: const int x = 5; So che nessuna memoria è allocata nella RAM per la variabile x, ma l’area variabile costante nella ROM contiene il valore 5 e che x è semplicemente sostituito dal valore 5 ovunque x appare nel codice. Ma quando succede? Tempo di compilazione? Tempo di avvio? Tempo di pre-elaborazione?

PS: Sto parlando di Embedded C (in esecuzione su un microcontrollore, ecc.), Non di C in esecuzione sul mio desktop. Quindi il sistema embedded è destinato ad avere una ROM (Flash, EEPROM …). Cosa succederebbe allora?

È semplicemente una limitazione della lingua. Le dimensioni degli array con limiti statici devono essere espressioni costanti e, sfortunatamente in C, si tratta solo di una costante letterale o un’espressione sizeof o simile, ma non di una variabile const type.

(Come sottolineato da Simon, dal C99 ci sono anche array con limiti di esecuzione , o “array di lunghezza variabile”, la cui dimensione può essere data dal valore di qualsiasi variabile, ma questo è un altro animale.)

Potresti essere interessato a sapere che le regole sono diverse in C ++, dove un static const int è in effetti un’espressione costante e C ++ 11 aggiunge anche una nuova parola chiave, constexpr , per consentire un uso ancora più generale delle espressioni costanti che comprendono più cose il cui valore “potrebbe essere ragionevolmente determinato al momento della compilazione”.

In C, const è un termine improprio per sola lettura . const variabili const possono cambiare il loro valore, ad esempio è perfettamente giusto dichiarare

 const volatile int timer_tick_register; /* A CPU register. */ 

che puoi leggere e ottenere un valore diverso con ogni lettura, ma non scrivere su. Pertanto, le specifiche del linguaggio considerano gli oggetti qualificati const non come espressioni costanti adatte per le dimensioni degli array.

2 principali alternative a VLA: enum e macro

Con enum :

 enum N { N = 5 }; int is[N]; 

Questo funziona perché i membri di enum sono espressioni costanti: il membro di enum può avere le dimensioni di un array in ANSI-C?

Con macro:

 #define N 5 int is[N]; 

Il vantaggio di enum s è che enum ha scope, e fa parte della fase di compilazione, quindi possono portare a migliori messaggi di errore.

Il vantaggio delle macro è che si ha più controllo sul tipo delle costanti (es. #define N 1 vs #define N 1u ), mentre enum s sono fissi su qualche tipo definito dall’implementazione: Is the sizeof (enum) == sizeof ( int), sempre? Ma non importa molto in questo caso.

Perché evitare VLA

  • non sono in C89 e solo opzionali in C11
  • può comportare un sovraccarico: c’è un sovraccarico per l’utilizzo di array di lunghezza variabile? (probabilmente poco)

EDIT: Basta leggere su wikipedia che C11 ha relegato matrici di lunghezza variabile a una caratteristica opzionale 🙁 Doh! La prima metà del post potrebbe non essere così utile, ma la seconda metà risponde ad alcune delle altre tue domande 🙂

Come supplemento al post di Kerrek SB, C99 (ISO / IEC 9899: 1999) ha il concetto di una matrice di lunghezza variabile . Lo standard fornisce il seguente esempio:

 #include  size_t fsize3(int n) { char b[n+3]; // variable length array return sizeof b; // execution time sizeof } 

L’operatore sizeof è esteso come segue:

L’operatore sizeof produce la dimensione (in byte) del suo operando, che può essere un’espressione o il nome tra parentesi di un tipo. La dimensione è determinata dal tipo di operando. Il risultato è un numero intero. Se il tipo dell’operando è un tipo di array di lunghezza variabile, viene valutato l’operando ; altrimenti, l’operando non viene valutato e il risultato è una costante intera.

Un altro bel esempio può essere trovato su wikipedia .

Si noti che staticamente dichiarato non può essere array di lunghezza variabile.

Per quanto riguarda alcune delle tue altre domande:

D: Quando le costanti vengono sostituite con i loro valori effettivi in ​​un codice?

Se la costante è una variabile const, non può mai essere “sostituita” e potrebbe sempre essere accessibile come area di memoria. Questo perché l’operatore di indirizzo & deve ancora lavorare con la variabile. Tuttavia, se l’indirizzo variabile non viene mai utilizzato, può essere “sostituito” e non avere memoria allocata. Dallo standard C:

L’implementazione può posizionare un object const che non è volatile in una regione di archiviazione di sola lettura. Inoltre, l’implementazione non deve allocare spazio per tale object se il suo indirizzo non viene mai utilizzato.

Prossima domanda…

D: So che nessuna memoria è allocata nella RAM per la variabile x, ma l’area variabile costante nella ROM contiene il valore 5

Questo dipende dal tuo sistema. Se si dispone di ROM e il compilatore sa dove si trova la ROM, potrebbe essere inserito nella ROM. Se non c’è la ROM, l’unica scelta che il compilatore (in effetti un linker davvero) avrà è la RAM.

Q: x è semplicemente sostituito dal valore 5 ovunque x appare nel codice. Ma quando succede? Tempo di compilazione? Tempo di avvio? Tempo di pre-elaborazione?

Come notato, questo dipende piuttosto da come viene usata la costante. Se l’indirizzo della variabile const non viene mai usato e il compilatore è abbastanza intelligente, allora al momento della compilazione. Altrimenti la “sostituzione” non si verifica mai e si tratta di un valore con una posizione in memoria; in questo caso il posizionamento della variabile in memoria avviene al momento del collegamento. Non si verificherà mai durante la pre-elaborazione.

Forse vale la pena menzionare, qui puoi usare

 int b[] = {1, 4, 5}; 

Nel caso abbiate bisogno di un numero di elementi

  size_t sz = sizeof(b)/sizeof(b[0]); 

Credo che dipenda dalla catena di strumenti, per decidere dove memorizzare costanti, flash o RAM