Considere el siguiente programa:
#include <iostream>
int main = ( std::cout << "C++ is excellent!\n", 195 );
Usando g ++ 4.8.1 (mingw64) en el sistema operativo Windows 7, el programa se compila y se ejecuta bien, imprimiendo:
¡C ++ es excelente!
a la consola. main
parece ser una variable global más que una función; ¿Cómo se puede ejecutar este programa sin la función main()
? ¿Este código se ajusta al estándar C ++? ¿Está bien definido el comportamiento del programa? También utilicé la -pedantic-errors
opción, pero el programa aún se compila y se ejecuta.
c++
main
language-lawyer
Incinerador de basuras
fuente
fuente
195
es el código de operación de laRET
instrucción y que en la convención de llamadas de C, el llamador borra la pila.main()
función? De hecho, no tienen ninguna relación)int main = ( std::cout << "C++ is excellent!\n", exit(0),1 );
(e incluido<cstdlib>
), aunque el programa sigue estando legalmente mal formado.Respuestas:
Antes de entrar en el meollo de la pregunta sobre lo que está sucediendo, es importante señalar que el programa está mal formado según el informe de defectos 1886: Enlace de idioma para main () :
Las versiones más recientes de clang y gcc hacen que esto sea un error y el programa no se compilará ( vea el ejemplo en vivo de gcc ):
Entonces, ¿por qué no había ningún diagnóstico en las versiones anteriores de gcc y clang? Este informe de defectos ni siquiera tuvo una propuesta de resolución hasta finales de 2014, por lo que este caso fue muy recientemente explícitamente mal formado, lo que requiere un diagnóstico.
Antes de esto, parece que esto sería un comportamiento indefinido, ya que estamos violando un deberán requisito del proyecto de C ++ estándar de la sección
3.6.1
[basic.start.main] :El comportamiento indefinido es impredecible y no requiere un diagnóstico. La inconsistencia que vemos con la reproducción del comportamiento es un comportamiento indefinido típico.
Entonces, ¿qué está haciendo realmente el código y por qué en algunos casos produce resultados? Veamos lo que tenemos:
Tenemos
main
cuál es un int declarado en el espacio de nombres global y se está inicializando, la variable tiene una duración de almacenamiento estática. La implementación define si la inicialización se llevará a cabo antes de que se realice un intento de llamada,main
pero parece que gcc hace esto antes de llamar.main
.El código usa el operador de coma , el operando de la izquierda es una expresión de valor descartada y se usa aquí únicamente para el efecto secundario de la llamada
std::cout
. El resultado del operador de coma es el operando derecho, que en este caso es el valor pr195
que se asigna a la variablemain
.Podemos ver que Sergej señala el ensamblado generado muestra que
cout
se llama durante la inicialización estática. Aunque el punto más interesante de discusión para ver la sesión de godbolt en vivo sería este:y el siguiente:
El escenario probable es que el programa salte al símbolo
main
esperando que haya un código válido allí y, en algunos casos, se producirá un error de segmentación . Entonces, si ese es el caso, esperaríamos que almacenar código de máquina válido en la variablemain
podría conducir a un programa viable , asumiendo que estamos ubicados en un segmento que permite la ejecución de código. Podemos ver que esta entrada del IOCCC de 1984 hace precisamente eso .Parece que podemos hacer que gcc haga esto en C usando ( verlo en vivo ):
Se produce un error de segmentación si la variable
main
no es constante, presumiblemente porque no está ubicada en una ubicación ejecutable, Hat Tip para este comentario aquí que me dio esta idea.También vea la respuesta FUZxxl aquí a una versión específica de C de esta pregunta.
fuente
main
no es un identificador reservado (3.6.1 / 3). En este caso, creo que el manejo de VS2013 de este caso (ver la respuesta de Francis Cugler) es más correcto en su manejo que gcc & clang.Desde 3.6.1 / 1:
A partir de esto, parece que g ++ permite un programa (presumiblemente como la cláusula "independiente") sin una función principal.
Luego desde 3.6.1 / 3:
Así que aquí aprendemos que está perfectamente bien tener una variable entera nombrada
main
.Finalmente, si se pregunta por qué se imprime la salida, la inicialización del
int main
usa el operador de coma para ejecutarcout
en static init y luego proporcionar un valor integral real para realizar la inicialización.fuente
main
a otra cosa:(.text+0x20): undefined reference to
principal '' 'gcc 4.8.1 genera el siguiente ensamblado x86:
Tenga en cuenta que
cout
se llama durante la inicialización, no en lamain
función..zero 4
declara 4 bytes (inicializados con 0) comenzando en la ubicaciónmain
, dondemain
es el nombre de la variable [!] .El
main
símbolo se interpreta como el inicio del programa. El comportamiento depende de la plataforma.fuente
195
es el código de operación deret
algunas arquitecturas. Por lo tanto, decir cero instrucciones puede no ser exacto.Ese es un programa mal formado. Se bloquea en mi entorno de prueba, cygwin64 / g ++ 4.9.3.
Del estándar:
fuente
La razón por la que creo que esto funciona es que el compilador no sabe que está compilando la
main()
función, por lo que compila un entero global con efectos secundarios de asignación.El formato de objeto en el que se compila esta unidad de traducción no es capaz de diferenciar entre un símbolo de función y un símbolo de variable .
Entonces, el enlazador se vincula felizmente al símbolo principal (variable) y lo trata como una llamada a función. Pero no hasta que el sistema de ejecución haya ejecutado el código de inicialización de la variable global.
Cuando ejecuté la muestra, se imprimió pero luego causó una falla de segmentación . Supongo que fue entonces cuando el sistema de tiempo de ejecución intentó ejecutar una variable int como si fuera una función .
fuente
Probé esto en un sistema operativo Win7 de 64 bits usando VS2013 y se compila correctamente, pero cuando intento compilar la aplicación, aparece este mensaje en la ventana de salida.
fuente
main()
porque es una variable de tipoint
Estás haciendo un trabajo complicado aquí. Como principal (de alguna manera) podría declararse un número entero. Usó el operador de lista para imprimir el mensaje y luego asignarle 195. Como dijo alguien a continuación, que no se siente cómodo con C ++, es cierto. Pero como el compilador no encontró ningún nombre definido por el usuario, main, no se quejó. Recuerde que main no es una función definida por el sistema, su función definida por el usuario y la cosa desde la cual el programa comienza a ejecutarse es el Módulo Principal, no main (), específicamente. De nuevo, main () es llamado por la función de inicio que es ejecutada por el cargador intencionalmente. Luego, todas sus variables se inicializan y, mientras se inicializan, se generan así. Eso es. El programa sin main () está bien, pero no es estándar.
fuente