¿Por qué auto a = 1; compilar en C?

125

El código:

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

se compila sin errores por el compilador de MS Visual Studio 2012, cuando el archivo tiene la extensión .c. Siempre he pensado que cuando usas la extensión .c, la compilación debe estar de acuerdo con la sintaxis de C y no con C ++. Además, por lo que sé, auto sin tipo solo está permitido en C ++ desde C ++ 11, donde significa que el tipo se deduce del inicializador.

¿Eso significa que mi compilador no se adhiere a C, o el código es realmente correcto en lenguaje C?

lee77
fuente
8
O compila con el modo C ++ (posible) o MS todavía está atascado en el último milenio. Implícito intse ha eliminado del estándar C en 1999.
Jens Gustedt
16
@JensGustedt MSVC ++ solo es compatible con C89 (y algunas características de C99). Es más un compilador de C ++.
ntoskrnl
3
@Brandin: con la opción / Wall, aparece la advertencia C4431, que indica que falta un especificador de tipo y que int predeterminado ya no es compatible con C (consulte el comentario de Jens). Es un poco una contradicción ya que obviamente este compilador lo admite ...
lee77
44
@JensGustedt Según esa medida, GCC 4.7, lanzado en 2012, (y versiones posteriores también, sospecho, no las tengo a mano) también está "atrapado en el último milenio". Compila el código de OP sin siquiera un aviso cuando no se le dan banderas
3
@delnan, al menos suponía que el OP había activado los niveles de advertencia. Obviamente estaba equivocado. Y en cierto sentido esto es cierto, gcc también está atascado allí, ya que todavía no tienen C99 (o una variante) por defecto. Clang advierte sobre la construcción, incluso sin banderas.
Jens Gustedt

Respuestas:

240

autoes una antigua palabra clave C que significa "ámbito local". auto aes lo mismo que auto int a, y dado que el ámbito local es el valor predeterminado para una variable declarada dentro de una función, también es lo mismo que int aen este ejemplo.

Esta palabra clave es en realidad un remanente del predecesor B de C, donde no había tipos base: todo era int, puntero a int, matriz de int. (*) Las declaraciones serían autoo extrn[sic]. C heredó el "todo es int" como regla predeterminada, por lo que podría declarar enteros con

auto a;
extern b;
static c;

ISO C se deshizo de esto, pero muchos compiladores todavía lo aceptan por compatibilidad con versiones anteriores. Si no le resulta familiar, entonces debe darse cuenta de que una regla relacionada está funcionando en

unsigned d;  // actually unsigned int

que todavía es común en el código moderno.

C ++ 11 reutilizó la palabra clave, que pocos o ningún programador de C ++ estaba usando con el significado original, para su inferencia de tipos. Esto es principalmente seguro porque la intregla "todo es " de C ya se había eliminado en C ++ 98; lo único que se rompe es auto T aque nadie estaba usando de todos modos. (En algún lugar de sus documentos sobre la historia del idioma , Stroustrup comenta sobre esto, pero no puedo encontrar la referencia exacta en este momento).

(*) El manejo de cadenas en B fue interesante: usaría matrices inty empaquetaría varios caracteres en cada miembro. B era en realidad BCPL con diferente sintaxis.

Fred Foo
fuente
77
No, esto no es legal C desde 1999. Ningún compilador moderno decente de C permite esto.
Jens Gustedt
18
@JensGustedt VS no pretende proporcionar un compilador de C moderno. Por lo que parece, el trabajo en el compilador de C se detuvo hace muchos años; solo lo suministran para que las personas puedan continuar compilando código heredado. (Y, por supuesto, cualquier compilador C decente y moderno tendrá opciones para admitir código heredado. Incluyendo una opción para K&R C.)
James Kanze
23
@JensGustedt: ¿estás seguro? GCC y Clang lo advierten en modo C99, pero no lo consideran un error, excepto con -Werror.
Fred Foo
2
@larsman, sí, en 6.7.2 hay una restricción explícita para eso: se debe dar al menos un especificador de tipo en los especificadores de declaración en cada declaración ...
Jens Gustedt
40
@JensGustedt - re No, esto no es C legal desde 1999. Ningún compilador moderno decente de C permite esto. La primera afirmación es correcta; es ilegal desde 1999. En mi humilde opinión, la segunda declaración es incorrecta. Cualquier compilador decente de C moderno debe permitir esto. Mire todo el código heredado que tendría que reescribirse si no lo permitieran. He escrito una respuesta que se expande en este comentario.
David Hammen
35

Esto es tanto una respuesta como un comentario extendido a No, no es C legal desde 1999. Ningún compilador decente de C moderno lo permite.

Sí, auto a=1;es ilegal en C1999 (y también en C2011). El hecho de que esto ahora sea ilegal no significa que un compilador de C moderno deba rechazar el código que contiene tales construcciones. Yo diría exactamente lo contrario, que un compilador C decente y moderno aún debe permitir esto.

Tanto clang como gcc hacen exactamente eso cuando compilan el código de muestra en la pregunta contra las versiones de 1999 o 2011 del estándar. Ambos compiladores emiten un diagnóstico y luego continúan como si la declaración objetable hubiera sido auto int a=1;.

En mi opinión, esto es lo que debe hacer un compilador decente. Al emitir un diagnóstico, clang y gcc cumplen totalmente con el estándar. El estándar no dice que un compilador debe rechazar el código ilegal. El estándar simplemente dice que una implementación conforme debe producir al menos un mensaje de diagnóstico si una unidad de traducción contiene una violación de cualquier regla o restricción de sintaxis (5.1.1.3).

Dado el código que contiene construcciones ilegales, cualquier compilador decente intentará darle sentido al código ilegal para que el compilador pueda encontrar el siguiente error en el código. Un compilador que se detiene en el primer error no es un compilador muy bueno. Hay una manera de darle sentido auto a=1, que es aplicar la regla "implícita int". Esta regla obliga al compilador a interpretar auto a=1como si fuera auto int a=1cuando el compilador se usa en modo C90 o K&R.

La mayoría de los compiladores generalmente rechazan el código (rechazar: rechazar generar un archivo de objeto o un ejecutable) que contenga una sintaxis ilegal. Este es un caso en el que los autores del compilador decidieron que no compilar no es la mejor opción. Lo mejor que puede hacer es emitir un diagnóstico, corregir el código y continuar. Hay demasiado código heredado que está salpicado de construcciones como register a=1;. El compilador debería poder compilar ese código en modo C99 o C11 (con un diagnóstico, por supuesto).

David Hammen
fuente
1
@larsmans - Puedo ver de dónde vienes. Desea una -ffs-please-stop-allowing-constructs-from-some-previous-millenniumopción de compilador, o más sucintamente, una -fstrict-complianceopción. Gruñendo en el compilador: "Cuando usé -std = c11 no esperaba que compilara el antiguo K&R kruft. De hecho, ¡quería que no compilara!"
David Hammen
1
En realidad no, yo quiero tener que convertir en una bandera para conseguir el peor costra de compilación. Pero -std=c99ser más estricto sería un paso en la dirección correcta :)
Fred Foo
1
Si usa gcc -g -O3 -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -Werror(que es lo que uso habitualmente, incluso en el código de las preguntas sobre SO), entonces se acerca bastante a lo que desea. Me gustaría que GCC tenga el valor predeterminado al menos -std=c99y preferiblemente -std=c11(o, -std=gnu11lo más probable es que lo hagan), pero hasta entonces, ... Puede modificar esas opciones; -pedantic, -Wshadow, -Wold-style-declarationY algunos otros pueden ser útiles, pero este es un buen punto de partida un conjunto de opciones.
Jonathan Leffler
3
@DavidHammen: Ni la complejidad ciclomática, los gerentes de proyecto ni las políticas de la empresa son elementos del lenguaje.
Jerry B
3
La bandera para obtener el comportamiento que desea en GCC es-pedantic-errors
τεκ
29

autotiene un significado en Cy C++antes de la Norma 2011. Significa que una variable tiene una vida útil automática, es decir, una vida útil determinada por el alcance . Esto se opone, por ejemplo, a la staticvida útil, donde una variable dura "para siempre", independientemente del alcance. autoes la duración predeterminada y casi nunca se explica explícitamente. Es por eso que era seguro cambiar el significado en C++.

Ahora C, antes del Estándar 99, si no especifica el tipo de una variable, su valor predeterminado es int.

Entonces, con auto a = 1;usted está declarando (y definiendo) una intvariable, cuya duración está determinada por el alcance.

("vida útil" se denomina más correctamente "duración de almacenamiento", pero creo que quizás sea menos claro).

BoBTFish
fuente
Bien, entonces en realidad auto a = 1 está permitido en C y significa una variable int con duración de almacenamiento automática.
lee77
1
Correctamente, la "duración del almacenamiento" toma uno de una lista de valores, "automático", "estático", "dinámico", "hilo". "Lifetime" es el tiempo real de vida del objeto. Por lo tanto, la variable tiene una duración de almacenamiento "automática" y una vida útil "la duración del alcance de la mainfunción".
Steve Jessop
@ Steve, no quise decir eso autoy staticson las dos únicas posibilidades. Estaba tratando de escribir mi respuesta de una manera dirigida al autor de la pregunta, que parece ser bastante nuevo para C++(y C), así que pasé por alto un poco los detalles. Tal vez esa fue una mala idea; necesitan ser cubiertos tarde o temprano.
BoBTFish
1
@BoBTFish: oh, no me estaba quejando de eso. Solo pretendía ampliar la diferencia semántica entre "vida útil", que es una duración, y "duración de almacenamiento", que podría haberse llamado con más precisión "categoría de duración de almacenamiento".
Steve Jessop
Este intmaterial implícito se elimina de C desde 1999.
Jens Gustedt
8

En C, y dialectos históricos de C ++, autoes una palabra clave que significa que atiene almacenamiento automático. Como solo se puede aplicar a las variables locales, que son automáticas por defecto, nadie lo usa; Es por eso que C ++ ahora ha reutilizado la palabra clave.

Históricamente, C ha permitido declaraciones variables sin especificador de tipo; el tipo predeterminado es int. Entonces esta declaración es equivalente a

int a=1;

Creo que esto está en desuso (y posiblemente prohibido) en la C moderna; pero algunos compiladores populares tienen el valor predeterminado C90 (que, creo, lo permite) y, molestamente, solo habilitan advertencias si las solicita específicamente. Compilar con GCC y especificar C99 con -std=c99, o habilitar la advertencia con -Wallo -Wimplicit-int, da una advertencia:

warning: type defaults to int in declaration of a
Mike Seymour
fuente
44
De hecho, está prohibido en C desde 1999.
Jens Gustedt
5

En C, autosignifica lo mismo que registeren C ++ 11: significa que una variable tiene una duración de almacenamiento automática.

Y en C anterior a C99 (y el compilador de Microsoft no admite C99 ni C11, aunque puede admitir partes de él), el tipo puede omitirse en muchos casos, donde el valor predeterminado será int .

No toma el tipo del inicializador en absoluto. Acabas de elegir un inicializador que sea compatible.


fuente
1
¿No está en desuso la palabra clave de registro en C ++ 11?
sórdido
@ sórdido Sí, lo es. Antes de C ++ 11, autoy registertenía exactamente el mismo significado (anteriormente comenté que había restricciones para tomar registerla dirección de una variable calificada, pero eso era incorrecto para C ++). register, aunque está en desuso, conserva su antiguo significado por ahora.
55
@JensGustedt: La respuesta no dice que sí. Dice que autoen C significa lo mismo que registeren C ++, lo que significa (ambos significan la duración del almacenamiento automático y nada más).
Mike Seymour
3

El tipo de compilación de Visual Studio está disponible en right click on file -> Properties -> C/C++ -> Advanced -> Compile As . Para asegurarse de que se compila como /TCopción de fuerza C. Entonces, en este caso, es lo que dijo larsmans (antigua autopalabra clave C ). Podría compilarse como C ++ sin que usted lo sepa.

UmNyobe
fuente
3

Una clase de almacenamiento define el alcance (visibilidad) y el tiempo de vida de las variables y / o funciones dentro de un Programa C.

Existen las siguientes clases de almacenamiento que se pueden usar en un programa C

auto
register
static
extern

auto es la clase de almacenamiento predeterminada para todas las variables locales.

{
        int Count;
        auto int Month;
}

El ejemplo anterior define dos variables con la misma clase de almacenamiento. auto solo puede usarse dentro de funciones, es decir, variables locales.

intes el tipo predeterminado para el autosiguiente código:

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

El siguiente código también es legal:

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