2009-09-25 15 views
11

gcc si lamenta di questo:Quali sono le implicazioni dell'uso di const statico invece di #define?

#include <stdio.h> 
static const int YY = 1024; 
extern int main(int argc, char*argv[]) 
{ 
    static char x[YY]; 
} 

$ gcc -c test1.c test1.c: In funzione main': test1.c:5: error: storage size of x 'non è costante test1.c: 5: Errore: dimensione della variabile `x' è troppo grande

Rimuovere la "statica" dalla definizione di x e tutto va bene.

Non sono esattamente chiaro cosa sta succedendo qui: sicuramente YY è costante?

Avevo sempre pensato che l'approccio "stat const" fosse preferibile a "#define". C'è un modo per usare "stat const" in questa situazione?

+0

La variabile è in realtà * non * creata nello stack in virtù del 'statico' in' static char x [YY] ' – Dirk

risposta

14

In C, una variabile const non è una costante "reale" in fase di compilazione ... è in realtà solo una variabile normale che non è possibile modificare. Per questo motivo, non è possibile utilizzare una variabile const int per specificare la dimensione di un array.

Ora, gcc ha un'estensione che consente di specificare la dimensione di una matrice in fase di esecuzione se la matrice viene creata nello stack. Questo è il motivo per cui, quando si lascia fuori static dalla definizione di x, il codice viene compilato. Tuttavia, questo non sarebbe ancora legale nello standard C.

La soluzione: utilizzare un #define.

Modifica: notare che questo è un punto in cui C e C++ differiscono. In C++, un const intè una costante reale in fase di compilazione e può essere utilizzato per specificare la dimensione degli array e simili.

+0

Mi piacerebbe commentare che' g ++ 'compila il codice. –

+0

Questo è corretto ... in C++, 'const' produce una costante" reale "in fase di compilazione. Modificherò la risposta per sottolineare questa distinzione. –

+2

C99 consente array di lunghezza variabile (VLA) nello stack. Non è limitato solo a gcc. –

12

È possibile utilizzare 'enum' o 'definire' di dichiarare la dimensione:

#define   XX 1024 
static int const YY = 1024; 
      enum{ ZZ = 1024 }; 

extern int main(void){ 

    static char x[XX]; // no error 
    *(int*)&XX = 123; // error: lvalue required as unary ‘&’ operand 


    static char y[YY]; // error: storage size of ‘y’ isn’t constant 
    *(int*)&YY = 123; // no error, the value of a const may change 


    static char z[ZZ]; // no error 
    *(int*)&ZZ = 123; // error: lvalue required as unary ‘&’ operand 
} 
+0

enum qui è piuttosto brutto, però. Significa completamente il significato di enum. – Metiu

+2

Molto brutto, ma funziona e ha meno effetti collaterali rispetto a #define. È l'equivalente C del C++ "const int foo = 1024;". –

+1

@Metiu: Perché utilizzare enum può essere considerato più brutto di #define? –

0

far seguito la risposta di Martin B, si potrebbe fare questo:

#include <stdio.h> 

#define XSIZE 1024 

static const int YY = XSIZE; 

int main(int argc, char*argv[]) 
{ 
    static char x[XSIZE]; 
} 
0
/* SHOULDN'T THIS WORK IN C++? */ 
// .h 

class C { 
public: 
    static const int kSIZE; 
    int a[kSIZE]; // error: array bound is not an integer constant 
}; 


// .cc 

const int C::kSIZE = 1; 


/* WORKS FINE */  
// .h 

class C { 
public: 
    enum eCONST { kSIZE = 1 }; 
    int a[kSIZE]; 
}; 
+0

Basta chiamarlo 'const int' invece di' static const int', e starai bene. Il problema con 'static const int' è che' C :: kSIZE' è inizializzato nel file '.cc' - quindi come dovrebbe un' .cc' diverso (che vede solo il file di intestazione) sapere cosa 'sizeof (C) 'è? –

+0

aha ... ok, bello. sembra un po 'sciocco avere altri 4 byte per oggetto solo per la costante però ... sembra che il const statico dovrebbe essere inizializzato nell'intestazione. grazie! –

1

Perché hai dichiarato x come 'statico' che lo rende una variabile globale. È noto solo alla funzione main() in cui è dichiarata. Dichiarando YY al di fuori di qualsiasi funzione, l'hai reso globale. 'static' lo rende anche globale, ma noto solo a questo file.

Se hai dichiarato YY come solo "const int YY = 1024", il compilatore potrebbe trattarlo come un #define, ma con un tipo. Questo dipende dal compilatore.

A questo punto, 2 cose potrebbero essere errate.

1:

Tutte le variabili globali vengono inizializzati in fase di esecuzione, prima di main() si chiama.

Poiché sia ​​x che YY sono globali, vengono entrambi inizializzati.

Quindi, l'inizializzazione del runtime di x globale dovrà allocare lo spazio in base al valore in YY. Se il compilatore non considera YY come #define con un tipo, deve fare un giudizio in fase di compilazione sui valori di runtime.Potrebbe assumere il più grande valore possibile per un int, che sarebbe davvero troppo grande. (O forse negativo dato che l'hai lasciato firmato)

Potrebbe essere interessante vedere cosa succede se cambi solo YY in un corto, preferibilmente un corto senza segno. Quindi il suo massimo sarebbe 64K.

2:

La dimensione dello spazio globale può essere limitato nel sistema. Non hai specificato la piattaforma di destinazione e il sistema operativo, ma alcuni hanno solo molto.

Poiché hai dichiarato x come dimensione YY, l'hai impostato per prendere i caratteri YY dallo spazio globale. Ogni elemento in esso sarebbe essenzialmente globale. Se lo spazio globale del tuo sistema è limitato, 1024 caratteri potrebbero essere troppo.

Se si dichiarava x come puntatore al char, sarebbero necessari byte sizeof (char *). (4 byte è la dimensione di un puntatore sulla maggior parte dei sistemi.) Con questo, è necessario impostare il puntatore sull'indirizzo dello spazio correttamente malloc'd.

Dichiarando x senza 'statico', diventa una variabile locale e viene inizializzato solo dopo l'esecuzione della funzione proprietaria. E il suo spazio è preso dalla pila, non dallo spazio globale. (Questo può ancora essere un problema per sistemi o thread con stack molto limitato.) Il valore di YY è stato da tempo impostato da questo punto, quindi non ci sono problemi.

anche:

Non ricordo se c'è alcuna garanzia che globali vengono inizializzati in qualsiasi ordine. In caso contrario, x potrebbe essere inizializzato prima di YY. Se ciò accadesse, allora YY avrebbe semplicemente contenuto casuale della RAM.

Problemi correlati