Perché auto a = 1; compilare in C?


125

Il codice:

int main(void)
{
    auto a=1;
    return 0;
}

viene compilato senza errori dal compilatore MS Visual Studio 2012, quando il file ha l'estensione .c. Ho sempre pensato che quando si utilizza l'estensione .c, la compilazione dovrebbe essere in base alla sintassi C e non al C ++. Inoltre, per quanto ne so auto senza un tipo è consentito solo in C ++ da C ++ 11, dove significa che il tipo viene dedotto dall'inizializzatore.

Ciò significa che il mio compilatore non si attacca a C o il codice è effettivamente corretto nel linguaggio C?


8
O si compila con la modalità C ++ (possibile) o MS è ancora bloccato nell'ultimo millennio. Implicito intè stato rimosso dallo standard C nel 1999.
Jens Gustedt,

16
@JensGustedt MSVC ++ supporta solo C89 (e alcune funzionalità di C99). È più un compilatore C ++.
ntoskrnl,

3
@Brandin: con l'opzione / Wall dà un avvertimento C4431, dicendo che manca un identificatore di tipo e che int predefinito non è più supportato in C (vedi il commento di Jens). È un po 'una contraddizione poiché ovviamente questo compilatore lo supporta ...
lee77,

4
@JensGustedt Con questa misura, GCC 4.7, pubblicato nel 2012, (e anche versioni successive, sospetto - non li ho a portata di mano) è anche "bloccato nell'ultimo millennio". Compila il codice OP senza nemmeno preavviso quando non viene assegnato alcun flag.

3
@delnan, almeno stavo supponendo che l'OP avesse attivato i livelli di avviso. Avevo ovviamente torto. E in un certo senso questo è vero, anche gcc è ancora bloccato lì, dal momento che non hanno ancora C99 (o una variante) come impostazione predefinita. clang avverte del costrutto, anche senza bandiere.
Jens Gustedt,

Risposte:


240

autoè una vecchia parola chiave C che significa "ambito locale". auto aè uguale a auto int a, e poiché l'ambito locale è il valore predefinito per una variabile dichiarata all'interno di una funzione, è anche la stessa int adi questo esempio.

Questa parola chiave è in realtà un residuo del predecessore B di C, dove non c'erano tipi di base: tutto era int, puntatore a int, matrice di int. (*) Le dichiarazioni sarebbero o autoo extrn[sic]. C ha ereditato "tutto è int" come regola predefinita, quindi è possibile dichiarare numeri interi

auto a;
extern b;
static c;

ISO C si è sbarazzato di questo, ma molti compilatori lo accettano ancora per compatibilità con le versioni precedenti. Se sembra non avere familiarità, allora dovresti capire che è in atto una regola correlata

unsigned d;  // actually unsigned int

che è ancora comune nel codice moderno.

C ++ 11 ha riutilizzato la parola chiave, che pochi se qualche programmatore C ++ utilizzava con il significato originale, per la sua inferenza di tipo. Questo è per lo più sicuro perché la intregola "tutto è " di C era già stata eliminata in C ++ 98; l'unica cosa che si rompe è auto T ache nessuno usava comunque. (Da qualche parte nei suoi articoli sulla storia della lingua , Stroustrup commenta questo, ma non riesco a trovare il riferimento esatto in questo momento.)

(*) La gestione delle stringhe in B era interessante: avresti usato array di inte impacchettato più caratteri in ciascun membro. B era in realtà BCPL con sintassi diversa.


7
No, questo non è C legale dal 1999. Nessun compilatore C decente moderno lo consente.
Jens Gustedt,

18
@JensGustedt VS non pretende di fornire un moderno compilatore C. Da tutte le apparenze, il lavoro sul compilatore C si è fermato molti anni fa; lo forniscono solo in modo che le persone possano continuare a compilare il codice legacy. (E ovviamente, qualsiasi compilatore C decente moderno avrà opzioni per supportare il codice legacy. Inclusa un'opzione per K&R C.)
James Kanze,

23
@JensGustedt: sei sicuro? GCC e Clang lo avvertono entrambi in modalità C99, ma non lo considerano un errore se non con -Werror.
Fred Foo,

2
@larsman, sì, in 6.7.2 esiste un vincolo esplicito per questo: almeno un
identificatore di

40
@JensGustedt - re No, questo non è C legale dal 1999. Nessun compilatore C decente moderno lo consente. La prima affermazione è corretta; è illegale dal 1999. IMHO, la seconda affermazione non è corretta. Qualsiasi compilatore C decente e moderno deve tener conto di ciò. Guarda tutto il codice legacy che dovrebbe essere riscritto se non lo consentisse. Ho scritto una risposta che si espande su questo commento.
David Hammen,

35

Questa è sia una risposta che un commento esteso a No, questo non è C legale dal 1999. Nessun compilatore C decente moderno lo consente.

Sì, auto a=1;è illegale in C1999 (e anche in C2011). Solo perché questo è ora illegale non significa che un moderno compilatore C dovrebbe rifiutare il codice che contiene tali costrutti. Direi esattamente il contrario, che un moderno compilatore C discreto deve ancora consentire questo.

Sia clang che gcc fanno proprio questo quando compilano il codice di esempio nella domanda rispetto alle versioni 1999 o 2011 dello standard. Entrambi i compilatori rilasciano una diagnosi e poi proseguono come se fosse stata la dichiarazione discutibile auto int a=1;.

Secondo me, questo è ciò che dovrebbe fare un compilatore decente. Emettendo una diagnostica, clang e gcc sono pienamente conformi allo standard. Lo standard non dice che un compilatore deve rifiutare il codice illegale. La norma afferma semplicemente che un'implementazione conforme deve produrre almeno un messaggio diagnostico se un'unità di traduzione contiene una violazione di qualsiasi regola o vincolo di sintassi (5.1.1.3).

Dato il codice che contiene costrutti illegali, qualsiasi compilatore decente cercherà di dare un senso al codice illegale in modo che il compilatore possa trovare il prossimo errore nel codice. Un compilatore che si arresta al primo errore non è un compilatore molto valido. C'è un modo per dare un senso a auto a=1, che è quello di applicare la regola "implicita int". Questa regola impone al compilatore di interpretare auto a=1come se fosse auto int a=1quando il compilatore viene utilizzato in modalità C90 o K&R.

La maggior parte dei compilatori in genere rifiuta il codice (rifiuta: rifiuta di generare un file oggetto o un eseguibile) che contiene sintassi illegale. Questo è un caso in cui gli autori del compilatore hanno deciso che la mancata compilazione non è l'opzione migliore. La cosa migliore da fare è emettere una diagnostica, correggere il codice e proseguire. C'è solo troppo codice legacy che è disseminato di costrutti come register a=1;. Il compilatore dovrebbe essere in grado di compilare quel codice in modalità C99 o C11 (con una diagnostica, ovviamente).


1
@larsmans - Posso vedere da dove vieni. Vuoi -ffs-please-stop-allowing-constructs-from-some-previous-millenniumun'opzione del compilatore, o più succintamente, -fstrict-complianceun'opzione. Brontolando al compilatore: "Quando ho usato -std = c11 non mi aspettavo che l'antica K&R kruft si compilasse. In effetti, volevo che non si compilasse!"
David Hammen,

1
In realtà no, voglio avere a girare su una bandiera per ottenere il peggio cruft alla compilazione. Ma -std=c99essere più severi sarebbe un passo nella giusta direzione :)
Fred Foo,

1
Se usi gcc -g -O3 -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror(che è quello che uso abitualmente, anche sul codice dalle domande su SO), allora ti avvicini abbastanza a quello che vuoi. Vorrei che GCC per impostazione predefinita almeno -std=c99e preferibilmente -std=c11(o -std=gnu11,; più probabilmente lo farebbero), ma fino ad allora, ... Puoi modificare quelle opzioni; -pedantic, -Wshadow, -Wold-style-declarationE alcuni altri possono essere utili, ma questa è una buona partenza fissato di opzioni.
Jonathan Leffler,

3
@DavidHammen: Né la complessità ciclomatica, i project manager, né le politiche aziendali sono elementi del linguaggio.
Jerry B,

3
La bandiera di ottenere il comportamento desiderato in GCC è-pedantic-errors
τεκ

29

autoha un significato Ce C++prima dello standard 2011. Significa che una variabile ha una durata automatica, ovvero una durata determinata dall'ambito . Ciò è contrario, ad esempio, alla staticdurata, in cui una variabile dura "per sempre", indipendentemente dall'ambito. autoè la durata predefinita e non viene quasi mai esplicitata. Questo è il motivo per cui è stato sicuro cambiarne il significato C++.

Ora in C, prima dello Standard 99, se non si specifica il tipo di una variabile, per impostazione predefinita è int.

Quindi con auto a = 1;te stai dichiarando (e definendo) una intvariabile, con la durata determinata dall'ambito.

("durata" è più propriamente definita "durata della memoria", ma penso che sia forse meno chiaro).


Ok, quindi in realtà a = 1 è consentito in C e significa una variabile int con durata di memorizzazione automatica.
lee77,

1
Correttamente, "durata della memoria" accetta uno di un elenco di valori, "automatico", "statico", "dinamico", "thread". "Lifetime" è il tempo reale della vita dell'oggetto. Quindi la variabile ha durata di memorizzazione "automatica" e durata "la durata dell'ambito della mainfunzione".
Steve Jessop,

@Steve sì, non intendevo implicarlo autoe staticsono le uniche due possibilità. Stavo cercando di scrivere la mia risposta in un modo mirato al richiedente, che sembra essere abbastanza nuovo C++(e C), quindi ho esaminato un po 'i dettagli. Forse era una cattiva idea; devono essere coperti prima o poi.
BoBTFish

1
@BoBTFish: oh, non me ne sono lamentato. Volevo solo ampliare la differenza semantica tra "durata", che è una durata, e "durata della memoria" che avrebbe potuto essere più precisamente definita "categoria di durata della memoria".
Steve Jessop,

Questa introba implicita viene rimossa da C dal 1999.
Jens Gustedt,

8

In C, e dialetti storici del C ++, autoè una parola chiave che significa amemorizzazione automatica. Dal momento che può essere applicato solo alle variabili locali, che sono automatiche per impostazione predefinita, nessuno lo utilizza; motivo per cui C ++ ha ora riproposto la parola chiave.

Storicamente, C ha consentito dichiarazioni variabili senza specificatore di tipo; il tipo di default è int. Quindi questa dichiarazione è equivalente a

int a=1;

Penso che questo sia deprecato (e forse vietato) nella moderna C; ma alcuni compilatori popolari utilizzano C90 per impostazione predefinita (che, a mio avviso, lo consente) e, fastidiosamente, attivano gli avvisi solo se richiesti specificatamente. Compilando con GCC e specificando C99 con -std=c99o abilitando l'avviso con -Wallo -Wimplicit-int, viene emesso un avviso:

warning: type defaults to int in declaration of a

4
È infatti vietato in C dal 1999.
Jens Gustedt,

5

In C, autosignifica la stessa cosa registerin C ++ 11: significa che una variabile ha una durata di memorizzazione automatica.

E in C prima di C99 (e il compilatore di Microsoft non supporta C99 o C11, sebbene possa supportare parti di esso), il tipo può essere omesso in molti casi, dove sarà impostato per impostazione predefinita int.

Non prende affatto il tipo dall'inizializzatore. Ti è capitato di scegliere un inizializzatore compatibile.


1
La parola chiave register non è deprecata in C ++ 11?
sordido

@sordid Sì, lo è. Prima di C ++ 11, autoe registeraveva lo stesso identico significato (in precedenza avevo commentato che c'erano restrizioni sull'assunzione registerdell'indirizzo di una variabile qualificata, ma che non era corretto per C ++). register, sebbene deprecato, conserva il suo antico significato per ora.

5
@JensGustedt: la risposta non dice che lo siano. Dice che autoin C significa lo stesso che registerin C ++, il che significa (entrambi significano durata della memorizzazione automatica e nient'altro).
Mike Seymour,

3

Il tipo di compilazione di Visual Studio è disponibile all'indirizzo right click on file -> Properties -> C/C++ -> Advanced -> Compile As. Per assicurarsi che sia compilato come /TCopzione di forza C. Quindi, in questo caso, è ciò che ha detto larsmans (vecchia autoparola chiave C ). Potrebbe essere compilato come C ++ a tua insaputa.


3

Una classe di memoria definisce l'ambito (visibilità) e la durata delle variabili e / o funzioni all'interno di un programma C.

Esistono le seguenti classi di archiviazione che possono essere utilizzate in un programma C.

auto
register
static
extern

auto è la classe di archiviazione predefinita per tutte le variabili locali.

{
        int Count;
        auto int Month;
}

L'esempio sopra definisce due variabili con la stessa classe di archiviazione. auto può essere utilizzato solo all'interno di funzioni, ovvero variabili locali.

intè il tipo predefinito autonel seguente codice:

auto Month;
/* Equals to */
int Month;

Anche il codice seguente è legale:

/* Default-int */
main()
{
    reurn 0;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.