Por que auto a = 1; compilar em C?

125

O código:

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

é compilado sem erros pelo compilador do MS Visual Studio 2012, quando o arquivo tem a extensão .c. Eu sempre pensei que, quando você usa a extensão .c, a compilação deve estar de acordo com a sintaxe C e não com o C ++. Além disso, até onde eu sei, auto sem um tipo é permitido apenas em C ++ desde C ++ 11, onde significa que o tipo é deduzido do inicializador.

Isso significa que meu compilador não está aderindo a C ou o código está realmente correto na linguagem C?

lee77
fonte
8
Você compila com o modo C ++ (possível) ou o MS ainda está parado no último milênio. O implícito intfoi removido do padrão C em 1999.
Jens Gustedt
16
@JensGustedt O MSVC ++ suporta apenas o C89 (e alguns recursos do C99). É mais um compilador C ++.
Ntoskrnl
3
@Brandin: Com a opção / Wall, ele dá o aviso C4431, dizendo que está faltando um especificador de tipo e que o int padrão não é mais suportado em C (consulte o comentário de Jens). É um pouco de uma contradição, pois, obviamente, isso compilador suporta ...
lee77
4
@JensGustedt Por essa medida, o GCC 4.7, lançado em 2012, (e versões posteriores também, eu suspeito - não as tenho em mãos) também está "preso no último milênio". Ele compila o código do OP sem aviso prévio quando não recebe nenhum sinalizador.
3
@ delnan, eu estava supondo pelo menos que o OP havia ativado os níveis de aviso. Eu obviamente estava errado. E, em certo sentido, isso é verdade, o gcc também ainda está preso lá, pois ainda não possui o C99 (ou uma variante) como padrão. clang alerta sobre a construção, mesmo sem bandeiras.
Jens Gustedt

Respostas:

240

autoé uma palavra-chave C antiga que significa "escopo local". auto aé o mesmo que auto int a, e como o escopo local é o padrão para uma variável declarada dentro de uma função, também é o mesmo que int aneste exemplo.

Essa palavra-chave é, na verdade, uma sobra do predecessor de C, B, onde não havia tipos de base: tudo era int, ponteiro para int, matriz de int. (*) As declarações seriam autoou extrn[sic]. C herdou o "tudo é int" como regra padrão, então você pode declarar números inteiros com

auto a;
extern b;
static c;

O ISO C se livrou disso, mas muitos compiladores ainda o aceitam como compatibilidade com versões anteriores. Se não lhe parecer familiar, você deve perceber que uma regra relacionada está em funcionamento em

unsigned d;  // actually unsigned int

o que ainda é comum no código moderno.

O C ++ 11 reutilizou a palavra-chave, que poucos ou nenhum programador C ++ estava usando com o significado original, para sua inferência de tipo. Isso é mais seguro porque a intregra "tudo é " de C já havia sido descartada no C ++ 98; a única coisa que quebra é auto T aque ninguém estava usando de qualquer maneira. (Em algum lugar em seus trabalhos sobre a história da linguagem , Stroustrup comenta isso, mas não consigo encontrar a referência exata no momento.)

(*) O tratamento de strings em B foi interessante: você usaria matrizes inte empacotaria vários caracteres em cada membro. B era na verdade BCPL com sintaxe diferente.

Fred Foo
fonte
7
Não, isso não é C legal desde 1999. Nenhum compilador C decente moderno permite isso.
Jens Gustedt
18
O @JensGustedt VS não pretende fornecer um compilador C moderno. De todas as aparências, o trabalho no compilador C parou muitos anos atrás; eles apenas o fornecem para que as pessoas possam continuar a compilar código legado. (E, claro, qualquer moderna compilador C decente terá opções para apoiar código legado Incluindo uma opção para K & R C..)
James Kanze
23
@JensGustedt: você tem certeza? GCC e Clang alertam sobre isso no modo C99, mas não consideram um erro, exceto com -Werror.
Fred Foo
2
@larsman, sim, em 6.7.2 existe uma restrição explícita para que: Pelo menos um tipo de especificador deve ser dada nos especificadores de declaração em cada declaração ...
Jens Gustedt
40
@JensGustedt - re Não, isso não é C legal desde 1999. Nenhum compilador C decente moderno permite isso. A primeira declaração está correta; é ilegal desde 1999. IMHO, a segunda declaração está incorreta. Qualquer compilador C decente e moderno deve permitir isso. Veja todo o código legado que teria que ser reescrito se não permitisse. Eu escrevi uma resposta que se expande neste comentário.
David Hammen
35

Esta é uma resposta e um comentário estendido a Não, isso não é C legal desde 1999. Nenhum compilador C decente moderno permite isso.

Sim, auto a=1;é ilegal em C1999 (e também em C2011). Só porque isso agora é ilegal, não significa que um compilador C moderno deva rejeitar o código que contém essas construções. Eu argumentaria exatamente o contrário, que um compilador C decente e moderno ainda deve permitir isso.

Tanto o clang quanto o gcc fazem exatamente isso ao compilar o código de exemplo na pergunta com relação às versões de 1999 ou 2011 do padrão. Ambos os compiladores emitem um diagnóstico e, em seguida, continuam como se a declaração censurável tivesse sido auto int a=1;.

Na minha opinião, é isso que um compilador decente deve fazer. Ao emitir um diagnóstico, o clang e o gcc são totalmente compatíveis com o padrão. O padrão não diz que um compilador deve rejeitar código ilegal. O padrão diz apenas que uma implementação em conformidade deve produzir pelo menos uma mensagem de diagnóstico se uma unidade de tradução contiver uma violação de qualquer regra ou restrição de sintaxe (5.1.1.3).

Dado o código que contém construções ilegais, qualquer compilador decente tentará entender o código ilegal para que o compilador possa encontrar o próximo erro no código. Um compilador que para no primeiro erro não é um compilador muito bom. Existe uma maneira de entender auto a=1, que é aplicar a regra "int implícita". Esta regra força o compilador a interpretar auto a=1como se fosse auto int a=1quando o compilador é usado no modo C90 ou K&R.

A maioria dos compiladores geralmente rejeita o código (rejeitar: recusar-se a gerar um arquivo de objeto ou um executável) que contém sintaxe ilegal. Este é um caso em que os autores do compilador decidiram que deixar de compilar não é a melhor opção. A melhor coisa a fazer é emitir um diagnóstico, corrigir o código e continuar. Há muito código legado repleto de construções como register a=1;. O compilador deve poder compilar esse código no modo C99 ou C11 (com um diagnóstico, é claro).

David Hammen
fonte
1
@larsmans - Eu posso ver de onde você está vindo. Você deseja uma -ffs-please-stop-allowing-constructs-from-some-previous-millenniumopção de compilador, ou mais sucintamente, uma -fstrict-complianceopção. Resmungando com o compilador: "Quando usei -std = c11, não esperava que o antigo K&R kruft fosse compilado. Na verdade, eu queria que ele não fosse compilado!"
David Hammen
1
Na verdade não, eu quero ter que transformar em uma bandeira para obter o pior cruft a compilação. Mas ter -std=c99mais rigorosa seja seria um passo na direção certa :)
Fred Foo
1
Se você usa gcc -g -O3 -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror(que é o que eu uso rotineiramente, mesmo no código de perguntas sobre SO), você fica bastante próximo do que deseja. Eu gostaria que o GCC tivesse como padrão, pelo menos -std=c99e de preferência -std=c11(ou -std=gnu11,; é mais provável que eles fizessem isso), mas até então, ... Você pode ajustar essas opções; -pedantic, -Wshadow, -Wold-style-declarationE alguns outros pode ser útil, mas este é um bom ponto de partida um conjunto de opções.
Jonathan Leffler
3
@ David David: Nem a complexidade ciclomática, os gerentes de projeto nem as políticas da empresa são elementos do idioma.
Jerry B
3
O sinalizador para obter o comportamento que você deseja no GCC é-pedantic-errors
#
29

autotem um significado Ce C++antes da Norma de 2011. Isso significa que uma variável tem vida útil automática, ou seja, vida útil determinada pelo escopo . Isso se opõe, por exemplo, ao statictempo de vida, em que uma variável dura "para sempre", independentemente do escopo. autoé a vida útil padrão e quase nunca é explicitamente explicitada. É por isso que era seguro alterar o significado em C++.

Agora C, antes do 99 Standard, se você não especificar o tipo de uma variável, o padrão será int.

Assim, auto a = 1;você está declarando (e definindo) uma intvariável, com a vida útil determinada pelo escopo.

("tempo de vida" é mais apropriadamente chamado de "duração do armazenamento", mas acho que isso talvez seja menos claro).

BoBTFish
fonte
Ok, então, na verdade, auto a = 1 é permitido em C e significa uma variável int com duração de armazenamento automático.
Lee77
1
Adequadamente, "duração do armazenamento" leva um de uma lista de valores, "automático", "estático", "dinâmico", "encadeamento". "Tempo de vida" é o tempo real de vida do objeto. Portanto, a variável tem duração de armazenamento "automática" e tempo de vida "a duração do escopo da mainfunção".
Steve Jessop
@ Steve sim, eu não quis dizer isso autoe staticsão as duas únicas possibilidades. Eu estava tentando escrever minha resposta de uma maneira direcionada ao solicitante, que parece bastante novo em C++(e C), então encaminhei um pouco os detalhes. Talvez tenha sido uma má ideia; eles precisam ser cobertos mais cedo ou mais tarde.
BoBTFish
1
@BoBTFish: ah, eu não estava reclamando disso. Eu apenas pretendia expandir a diferença semântica entre "tempo de vida", que é uma duração, e "duração de armazenamento", que pode ser mais precisamente chamada de "categoria de duração de armazenamento".
Steve Jessop
Esta implícito intmaterial é removido a partir de C desde 1999.
Jens Gustedt
8

Em C, e dialetos históricos de C ++, autoé uma palavra-chave que significa aarmazenamento automático. Como ele só pode ser aplicado a variáveis ​​locais, que são automáticas por padrão, ninguém as usa; é por isso que o C ++ agora reformulou a palavra-chave.

Historicamente, C permitiu declarações de variáveis ​​sem especificador de tipo; o tipo padrão é int. Portanto, esta declaração é equivalente a

int a=1;

Eu acho que isso foi preterido (e possivelmente proibido) no C moderno; mas alguns compiladores populares assumem o padrão C90 (o que, penso, o permite) e, irritantemente, apenas ativam avisos se você os solicitar especificamente. Compilar com o GCC e especificar C99 com -std=c99ou ativar o aviso com -Wallou -Wimplicit-intdá um aviso:

warning: type defaults to int in declaration of a
Mike Seymour
fonte
4
Na verdade, é proibido em C desde 1999.
Jens Gustedt
5

Em C, autosignifica o mesmo registerque em C ++ 11: significa que uma variável tem duração de armazenamento automática.

E em C anterior ao C99 (e o compilador da Microsoft não suporta C99 ou C11, embora possa suportar partes dele), o tipo pode ser omitido em muitos casos, para o padrão int.

Ele não aceita o tipo do inicializador. Por acaso, você escolheu um inicializador compatível.


fonte
1
A palavra-chave register não está obsoleta no C ++ 11?
Sordid
@ sordid Sim, é. Antes do C ++ 11, autoe registertinha exatamente o mesmo significado (eu comentei anteriormente que havia restrições em usar registero endereço de uma variável qualificada, mas isso estava incorreto para o C ++). register, embora descontinuado, mantém seu antigo significado por enquanto.
5
@JensGustedt: A resposta não diz que são. Ele diz que autoem C significa o mesmo que registerem C ++, o que significa (ambos significam duração automática do armazenamento e nada mais).
Mike Seymour
3

O tipo de compilação do Visual studio está disponível em right click on file -> Properties -> C/C++ -> Advanced -> Compile As. Para garantir que ele seja compilado como opção de força C, /TCentão é o que Larsmans disse (antiga autopalavra-chave C ). Pode ser compilado como C ++ sem você saber.

UmNyobe
fonte
3

Uma classe de armazenamento define o escopo (visibilidade) e o tempo de vida de variáveis ​​e / ou funções dentro de um programa C.

Existem as seguintes classes de armazenamento que podem ser usadas em um programa C

auto
register
static
extern

auto é a classe de armazenamento padrão para todas as variáveis ​​locais.

{
        int Count;
        auto int Month;
}

O exemplo acima define duas variáveis ​​com a mesma classe de armazenamento. auto só pode ser usado dentro de funções, ou seja, variáveis ​​locais.

inté o tipo padrão autono código abaixo:

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

O código abaixo também é legal:

/* Default-int */
main()
{
    reurn 0;
}
Amir Saniyan
fonte