Pregunto porque mi compilador parece pensar que sí, aunque yo no.
echo 'int main;' | cc -x c - -Wall
echo 'int main;' | c++ -x c++ - -Wall
Clang no emite advertencias ni errores con esto, y gcc solo emite la advertencia mansa:, 'main' is usually a function [-Wmain]
pero solo cuando se compila como C. Especificar a -std=
no parece importar.
De lo contrario, se compila y enlaza bien. Pero en la ejecución, termina inmediatamente con SIGBUS
(para mí).
Leer las (excelentes) respuestas en ¿Qué debería devolver main () en C y C ++? y un rápido grep a través de las especificaciones del idioma, ciertamente me parecería que se requiere una función principal . Pero la verborrea de gcc -Wmain
('main' suele ser una función) (y la escasez de errores aquí) parece sugerir lo contrario.
¿Pero por qué? ¿Hay algún uso extraño o “histórico” para esto? Alguien sabe lo que da?
Mi punto, supongo, es que realmente creo que esto debería ser un error en un entorno alojado, ¿eh?
gcc -std=c99 -pedantic ...
-pedantic
o cualquiera-std
. Mi sistemac99
también compila esto sin advertencia ni error ...main
, que es poco probable que funcione. Si inicializa main con el valor "correcto", en realidad puede devolver ...main
)main=195;
Respuestas:
Dado que la pregunta tiene doble etiqueta como C y C ++, el razonamiento para C ++ y C sería diferente:
xyz
y una función global independientexyz(int)
. Sin embargo, el nombremain
nunca se estropea.Eso es lo que está sucediendo aquí: el enlazador espera encontrar el símbolo
main
, y lo hace. "Conecta" ese símbolo como si fuera una función, porque no conoce nada mejor. La parte de la biblioteca en tiempo de ejecución que pasa el control a la quemain
solicita el vinculadormain
, por lo que el vinculador le da un símbolomain
, dejando que la fase de vínculo se complete. Por supuesto, esto falla en tiempo de ejecución, porquemain
no es una función.Aquí hay otra ilustración del mismo problema:
archivo xc:
archivo yc:
compilando:
Esto se compila, y probablemente se ejecutaría, pero es un comportamiento indefinido, porque el tipo de símbolo prometido al compilador es diferente del símbolo real proporcionado al enlazador.
En lo que respecta a la advertencia, creo que es razonable: C le permite crear bibliotecas que no tienen
main
función, por lo que el compilador libera el nombremain
para otros usos si necesita definir una variablemain
por alguna razón desconocida.fuente
main
no está sujeto a alteración de nombres (eso parece) en C ++, sea o no una función.main
como cualquier otra cosa que no sea una función. La respuesta ofrece una explicación para ambas partes.main
no es una palabra reservada que es sólo un identificador predefinido (comocin
,endl
,npos
...), por lo que podría declarar una variable llamadamain
, inicializar y luego imprimir su valor.Por supuesto:
main()
función (bibliotecas).EDITAR
Algunas referencias:
main
no es una palabra reservada (C ++ 11):C ++ 11 - [basic.start.main] 3.6.1.3
Palabras reservadas en lenguajes de programación .
Es posible que el programador no vuelva a definir las palabras reservadas, pero las predefinidas a menudo se pueden anular de alguna manera. Este es el caso de
main
: hay ámbitos en los que una declaración que utiliza ese identificador redefine su significado.fuente
main()
función, pero no puede vincularla como un programa. Lo que sucede aquí es que un programa "válido" se vincula sin unmain()
, solo unmain
.cin
yendl
no están en el espacio de nombres predeterminado, están en elstd
espacio de nombres.npos
es miembro destd::basic_string
.main
está reservado como nombre global. Ninguna de las otras cosas que mencionaste, nimain
están predefinidas.main
se permite. C ++ dice "Una implementación no predefinirá la función principal" y C dice "La implementación no declara ningún prototipo para esta función".¿Es
int main;
un programa C / C ++ válido?No está del todo claro qué es un programa C / C ++.
¿Es
int main;
un programa C válido?Si. Se permite una implementación independiente para aceptar dicho programa.
main
no tiene por qué tener ningún significado especial en un entorno independiente.Es no válido en un entorno alojado.
¿Es
int main;
un programa C ++ válido?Ídem.
¿Por qué choca?
El programa no tiene por qué tener sentido en su entorno. En un entorno independiente, el inicio y la terminación del programa, y el significado de
main
, están definidos por la implementación.¿Por qué me advierte el compilador?
El compilador puede advertirle sobre lo que le plazca, siempre que no rechace los programas conformes. Por otro lado, la advertencia es todo lo que se requiere para diagnosticar un programa no conforme. Dado que esta unidad de traducción no puede formar parte de un programa alojado válido, se justifica un mensaje de diagnóstico.
¿Es
gcc
un entorno independiente o es un entorno alojado?Si.
gcc
documenta la-ffreestanding
bandera de compilación. Agréguelo y la advertencia desaparecerá. Es posible que desee utilizarlo al compilar, por ejemplo, kernels o firmware.g++
no documenta tal bandera. Suministrarlo parece no tener ningún efecto en este programa. Probablemente sea seguro asumir que el entorno proporcionado por g ++ está alojado. La ausencia de diagnóstico en este caso es un error.fuente
Es una advertencia ya que técnicamente no está prohibida. El código de inicio usará la ubicación del símbolo de "main" y saltará a él con los tres argumentos estándar (argc, argv y envp). No lo hace, y en el momento del enlace no puede verificar que en realidad sea una función, ni siquiera que tenga esos argumentos. Esta es también la razón por la que int main (int argc, char ** argv) funciona: el compilador no conoce el argumento envp y resulta que no se usa, y es una limpieza de llamadas.
Como broma, podrías hacer algo como
en una máquina x86 e, ignorando las advertencias y cosas similares, no solo se compilará, sino que también funcionará.
Alguien usó una técnica similar a esta para escribir un ejecutable (más o menos) que se ejecuta directamente en varias arquitecturas: http://phrack.org/issues/57/17.html#article . También se utilizó para ganar el IOCCC - http://www.ioccc.org/1984/mullender/mullender.c .
fuente
int main __attribute__ ((section (".text")))= 0xC3C3C3C3;
¿Es un programa válido?
No.
No es un programa ya que no tiene partes ejecutables.
¿Es válido compilar?
Si.
¿Se puede utilizar con un programa válido?
Si.
No se requiere que todo el código compilado sea ejecutable para ser válido. Los ejemplos son bibliotecas estáticas y dinámicas.
Ha creado efectivamente un archivo de objeto. No es un ejecutable válido, sin embargo, otro programa podría vincularse al objeto
main
en el archivo resultante cargándolo en tiempo de ejecución.¿Debería ser un error?
Tradicionalmente, C ++ permite al usuario hacer cosas que pueden parecer que no tienen un uso válido pero que encajan con la sintaxis del lenguaje.
Quiero decir que seguro, esto podría reclasificarse como un error, pero ¿por qué? ¿Para qué serviría eso que la advertencia no?
Siempre que exista una posibilidad teórica de que esta funcionalidad se utilice en el código real, es muy poco probable que al llamar a un objeto que no sea una función se produzca
main
un error de acuerdo con el lenguaje.fuente
main
. ¿Cómo puede un programa válido, que debe tener una función visible externamente nombradamain
, vincularse a él?main
función.int main;
definición no será visible.Me gustaría agregar a las respuestas ya dadas citando los estándares reales del idioma.
Es 'int main;' un programa C válido?
Respuesta corta (mi opinión): solo si su implementación utiliza un "entorno de ejecución independiente".
Todas las citas siguientes de C11
5. Medio ambiente
5.1.2 Entornos de ejecución
5.1.2.1 Entorno independiente
5.1.2.2 Entorno alojado
5.1.2.2.1 Inicio del programa
De estos, se observa lo siguiente:
En un entorno de ejecución independiente, yo diría que es un programa válido que no permite que suceda el inicio, porque no hay una función presente para eso como se requiere en 5.1.2. En un entorno de ejecución alojado, mientras su código introduce un objeto llamado main , no puede proporcionar un valor de retorno, por lo que yo diría que no es un programa válido en este sentido, aunque también se podría argumentar como antes que si el programa no es está destinado a ser ejecutado (es posible que on desee proporcionar datos solo, por ejemplo), entonces simplemente no permite hacer eso.
Es 'int main;' un programa C ++ válido?
Respuesta corta (mi opinión): solo si su implementación utiliza un "entorno de ejecución independiente".
Cita de C ++ 14
3.6.1 Función principal
Aquí, a diferencia del estándar C11, se aplican menos restricciones al entorno de ejecución independiente, ya que no se menciona ninguna función de inicio, mientras que para un entorno de ejecución alojado, el caso es prácticamente el mismo que para C11.
Nuevamente, diría que para el caso alojado, su código no es un programa C ++ 14 válido, pero estoy seguro de que es para el caso independiente.
Dado que mi respuesta solo considera el entorno de ejecución , creo que la respuesta de dasblinkenlicht entra en juego, ya que la alteración de nombres que se produce en el entorno de traducción ocurre de antemano. Aquí, no estoy tan seguro de que las citas anteriores se cumplan de manera tan estricta.
fuente
El error es tuyo. No especificó una función llamada
main
que devuelve unint
e intentó utilizar su programa en un entorno alojado.Suponga que tiene una unidad de compilación que define una variable global llamada
main
. Esto bien podría ser legal en un entorno independiente porque lo que constituye un programa se deja a la implementación en entornos independientes.Suponga que tiene otra unidad de compilación que define una función global llamada
main
que devuelve anint
y no toma argumentos. Esto es exactamente lo que necesita un programa en un entorno alojado.Todo está bien si solo usa la primera unidad de compilación en un entorno independiente y solo usa la segunda en un entorno alojado. ¿Qué pasa si usa ambos en un programa? En C ++, ha violado la regla de una definición. Ese es un comportamiento indefinido. En C, ha violado la regla que dicta que todas las referencias a un solo símbolo deben ser coherentes; si no es así, es un comportamiento indefinido. El comportamiento indefinido es un "¡sal de la cárcel, gratis!" tarjeta a los desarrolladores de una implementación. Todo lo que haga una implementación en respuesta a un comportamiento indefinido cumple con el estándar. La implementación no tiene que advertir, y mucho menos detectar, un comportamiento indefinido.
¿Qué pasa si usa solo una de esas unidades de compilación, pero usa la incorrecta (que es lo que hizo)? En C, la situación está clara. No definir la función
main
en una de las dos formas estándar en un entorno alojado es un comportamiento indefinido. Suponga que no definiómain
en absoluto. El compilador / enlazador no tiene nada que decir sobre este error. Que se quejen es una delicadeza en su nombre. Que el programa C compilado y vinculado sin errores es culpa suya, no del compilador.Es un poco menos claro en C ++ porque no definir la función
main
en un entorno alojado es un error en lugar de un comportamiento indefinido (en otras palabras, debe ser diagnosticado). Sin embargo, la regla de una definición en C ++ significa que los enlazadores pueden ser bastante tontos. El trabajo del vinculador es resolver referencias externas y, gracias a la regla de una definición, el vinculador no tiene que saber qué significan esos símbolos. Usted proporcionó un símbolo con nombremain
, el vinculador espera ver un símbolo con nombremain
, por lo que todo está bien en lo que respecta al vinculador.fuente
Para C, hasta ahora es un comportamiento definido por la implementación.
Como dice la ISO / IEC9899:
fuente
No, este no es un programa válido.
Para C ++, esto se hizo recientemente explícitamente mal formado por el informe de defectos 1886: Enlace de idioma para main () que dice:
y parte de la resolución incluyó el siguiente cambio:
Podemos encontrar esta redacción en el último borrador de estándar de C ++ N4527, que es el borrador de C ++ 1z.
Las últimas versiones de clang y gcc ahora hacen que esto sea un error ( véalo en vivo ):
Antes de este informe de defectos, era un comportamiento indefinido que no requiere un diagnóstico. Por otro lado, el código mal formado requiere un diagnóstico, el compilador puede convertir esto en una advertencia o un error.
fuente
main()
). Entiendo la razón para no permitirmain()
tener una especificación de enlace explícita, pero no entiendo que exija quemain()
tenga un enlace C ++ . Por supuesto, la norma no lo hace directamente dirección de cómo manejar ABI vinculación / renombrado de nombres, pero en la práctica (por ejemplo, con Itanium ABI) esto sería destrozarmain()
a_Z4mainv
. ¿Qué me estoy perdiendo?