2014-12-29 95 views
5

Dire che ho un'unità di compilazione file1.c, che dichiara una variabile di file-scope in questo modo:È valido trattare un extern globale come const quando la definizione non è const?

int my_variable = 12; 

Poi, in un'altra unità di compilazione file2.c, creo una dichiarazione extern per questo variabile, ma dichiararla come const:

extern const int my_variable; 

Questo compilerà e lavorare bene con gcc, utilizzando -Wall -Wextra -ansi -pedantic. Tuttavia, lo standard C89 dice Per la compatibilità di due tipi qualificati, entrambi devono avere la versione identicamente qualificata di un tipo compatibile. L'aggiunta di const alla dichiarazione aggiunge una restrizione anziché evitarne una. Questa C è sicura e valida? Quale sarebbe la migliore pratica nell'impostazione di questo con i file di intestazione?

+2

Potrebbe essere che il compilatore si lamenterebbe se fosse il contrario, se si scartasse il 'const' quialifier. Ma non ne sono veramente sicuro. –

+2

Sono abbastanza sicuro che [C - Accedere a una dichiarazione const const da non-const] (http://stackoverflow.com/q/8051969/1708801) copre questo caso. –

+0

Probabilmente vale la pena specificare l'esatta versione standard C a cui ti riferisci. – Clifford

risposta

5

È chiaramente indefinito in quanto le dichiarazioni non corrispondono. Come hai notato, const int e int non sono tipi compatibili. È necessaria una diagnostica solo se compaiono nello stesso ambito.

Non è sicuro all'atto pratico sia, non

$ cat test1.c 
#include <stdio.h> 

extern const int n; 
void foo(void); 

int main(void) { 
    printf("%d\n", n); 
    foo(); 
    printf("%d\n", n); 
} 
$ cat test2.c 
int n; 
void foo(void) { ++n; } 
$ gcc -std=c99 -pedantic test1.c test2.c && ./a.out 
0 
1 
$ gcc -O1 -std=c99 -pedantic test1.c test2.c && ./a.out 
0 
0 

Gcc presuppone che n non è stato cambiato con foo() quando l'ottimizzazione, perché può assumere la definizione di n è di tipo compatibile, così const .

Le probabilità sono che si ottiene il comportamento previsto con anche volatile -qualifying n in test1.c, ma per quanto riguarda lo standard C è interessato, questo è ancora definito.

Il modo migliore che posso pensare di impedire all'utente di modificare accidentalmente n è dichiarare un puntatore a const, qualcosa lungo

int my_real_variable; 
const int *const my_variable = &my_real_variable; 

o forse qualche macro

#define my_variable (*(const int *)&my_variable) 

Con C99, my_real_variable può essere evitato tramite un composto letterale:

const int *const my_variable_ptr = &(int){ 12 }; 

Qui è legale gettare via const qui (come l'oggetto int non è const), ma il cast sarebbe necessario, evitando modifiche accidentali.

+0

Questo risponde bene alla domanda; quell'esempio è quello che stavo cercando. – Quackmatic

4

In questo caso la definizione e la dichiarazione vengono visualizzate in unità di traduzione separate, pertanto il compilatore non può eseguire alcun tipo o controllo di qualificatore. I simboli sono risolti dal linker e in questo caso sembra che il linker non stia applicando questo abbinamento di qualificatore.

Se la definizione e la dichiarazione sono state visualizzate nella stessa unità di traduzione; ad esempio se hai inserito la dichiarazione extern in un file di intestazione e incluso in file1.c, allora immagino che il compilatore si lamenti. Inserendoli in unità di traduzione separate, il compilatore non vede mai entrambi, quindi non può eseguire il controllo.

+0

Da questo presuppongo che il comportamento del linker in questa situazione sia specifico dell'implementazione? In questa situazione, tutto quello che so è che la decorazione del nome C non presta attenzione a 'const' (mentre il C++ lo fa, per esempio.) – Quackmatic