Durante la revisione del codice, applico le seguenti regole:
Utilizzare sempre const
per i parametri della funzione passati per riferimento in cui la funzione non modifica (o libera) i dati indicati.
int find(const int *data, size_t size, int value);
Usare sempre const
per costanti che potrebbero altrimenti essere definite usando un #define o un enum. Di conseguenza, il compilatore può individuare i dati nella memoria di sola lettura (ROM) (sebbene il linker sia spesso uno strumento migliore a tale scopo nei sistemi integrati).
const double PI = 3.14;
Non usare mai const in un prototipo di funzione per un parametro passato per
valore . Non ha significato ed è quindi solo "rumore".
// don't add const to 'value' or 'size'
int find(const int *data, size_t size, const int value);
Se del caso, utilizzare const volatile
su posizioni che non possono essere modificate dal programma ma che potrebbero comunque cambiare. I registri hardware sono il tipico caso d'uso qui, ad esempio un registro di stato che riflette uno stato del dispositivo:
const volatile int32_t *DEVICE_STATUS = (int32_t*) 0x100;
Altri usi sono opzionali. Ad esempio, i parametri a una funzione all'interno della funzione attuazione possono essere contrassegnati come const.
// 'value' and 'size can be marked as const here
int find(const int *data, const size_t size, const int value)
{
... etc
o la funzione restituisce valori o calcoli ottenuti e che quindi non cambiano mai:
char *repeat_str(const char *str, size_t n)
{
const size_t len = strlen(str);
const size_t buf_size = 1 + (len * n);
char *buf = malloc(buf_size);
...
Questi usi const
indicano semplicemente che non cambierai la variabile; non cambiano come o dove è memorizzata la variabile. Il compilatore può ovviamente capire che una variabile non è cambiata, ma aggiungendoti gli const
consenti di imporla. Questo può aiutare il lettore e aggiungere un po 'di sicurezza (anche se se le tue funzioni sono abbastanza grandi o abbastanza complicate da fare una grande differenza, probabilmente hai altri problemi). Modifica - ad es. una funzione densamente codificata di 200 righe con loop nidificati e molti nomi di variabili lunghi o simili, sapendo che alcune variabili non cambiano mai potrebbero facilitare la comprensione in modo significativo. Tali funzioni sono state progettate o mantenute male.
Problemi con const
. Probabilmente sentirai il termine "avvelenamento const". Ciò si verifica quando l'aggiunta const
a un parametro di funzione provoca la propagazione di "costanza".
Modifica - avvelenamento const: ad esempio nella funzione:
int function_a(char * str, int n)
{
...
function_b(str);
...
}
se cambiamo str
in const
, dobbiamo quindi assicurarci che fuction_b
anche a const
. E così via se function_b
passa str
a function_c
, ecc. Come puoi immaginare, potrebbe essere doloroso se si propaga in molti file / moduli separati. Se si propaga in una funzione che non può essere modificata (ad esempio una libreria di sistema), diventa necessario un cast. Quindi cospargere
const
nel codice esistente forse chiede problemi. Nel nuovo codice, tuttavia, è meglio const
qualificarsi in modo coerente laddove appropriato.
Il problema più insidioso const
è che non era nella lingua originale. Come componente aggiuntivo non si adatta perfettamente. Per cominciare ha due significati (come nelle regole sopra, che significa "non ho intenzione di cambiare questo" e "questo non può essere modificato"). Ma soprattutto, può essere pericoloso. Ad esempio, compila ed esegui questo codice e (a seconda del compilatore / delle opzioni) potrebbe bloccarsi quando eseguito:
const char str[] = "hello world\n";
char *s = strchr(str, '\n');
*s = '\0';
strchr
restituisce un char*
non a const char*
. Poiché il suo parametro call è
const
, deve trasmettere il parametro call a char*
. E in questo caso ciò elimina la vera proprietà di archiviazione di sola lettura. Modifica: - questo si applica generalmente ai var nella memoria di sola lettura. Per "ROM" intendo non solo la ROM fisica, ma qualsiasi memoria protetta da scrittura, come accade per la sezione di codice dei programmi eseguiti su un sistema operativo tipico.
Molte funzioni di libreria standard si comportano allo stesso modo, quindi attenzione: quando si hanno costanti reali (cioè memorizzate nella ROM) è necessario fare molta attenzione a non perdere la loro costanza.
Specific issues with software development
. Sono abbastanza specifico.