sintaxis - Es ''int principal;'' un programa válido de C/C++?
manual completo de c++ pdf (9)
Mi punto, supongo, es que realmente creo que esto debería ser un error en un entorno alojado, ¿eh?
El error es tuyo No especificó una función llamada main
que devuelve un int
y trató de usar su programa en un entorno alojado.
Supongamos 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 queda en manos de la implementación en entornos independientes.
Supongamos que tiene otra unidad de compilación que define una función global llamada main
que devuelve un int
y no toma argumentos. Esto es exactamente lo que necesita un programa en un entorno alojado.
Todo está bien si solo usas la primera unidad de compilación en un entorno independiente y solo usas 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 consistentes; si no lo son, es un comportamiento indefinido. El comportamiento indefinido es un "salir de la cárcel, ¡gratis!" tarjeta a los desarrolladores de una implementación. Cualquier cosa que haga una implementación en respuesta a un comportamiento indefinido es compatible 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 utiliza la incorrecta (que es lo que hizo)? En C, la situación es clara. La falla al 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 que decir nada sobre este error. Que ellos se quejan es una satisfacción en su nombre. Que el programa C compilado y enlazado sin error es su culpa, no del compilador.
Es un poco menos claro en C ++ porque la falta de definición de 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 la resolución de referencias externas, y gracias a la regla de una definición, el vinculador no tiene que saber lo que significan esos símbolos. Usted proporcionó un símbolo llamado main
, el vinculador espera ver un símbolo llamado main
, por lo que todo está bien en lo que respecta al enlazador.
Lo pregunto porque mi compilador parece pensarlo, aunque yo no lo creo.
echo ''int main;'' | cc -xc - -Wall
echo ''int main;'' | c++ -x c++ - -Wall
Clang no emite ninguna advertencia o error con esto, y gcc solo emite una advertencia ''main'' is usually a function [-Wmain]
: ''main'' is usually a function [-Wmain]
, pero solo cuando se compila como C. Especificar a -std=
no parece importar.
De lo contrario, compila y enlaza bien. Pero en la ejecución, termina inmediatamente con SIGBUS
(para mí).
Leyendo las (excelentes) respuestas en ¿Qué debería main () devolver en C y C ++? y un rápido grep a través de las especificaciones del lenguaje, ciertamente me parece que se requiere una función principal. Pero la verborrea de gcc''s -Wmain
(''main'' es usualmente una función) (y la escasez de errores aquí) parece sugerir posiblemente lo contrario.
¿Pero por qué? ¿Hay algún extraño caso de borde o uso "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?
Dado que la pregunta tiene una etiqueta doble como C y C ++, el razonamiento para C ++ y C sería diferente:
- C ++ utiliza el cambio de nombre para ayudar al vinculador a distinguir entre símbolos textualmente idénticos de diferentes tipos, por ejemplo, una variable global
xyz
y una función global autónomaxyz(int)
. Sin embargo, el nombremain
nunca se destroza. - C no utiliza el mapeo, por lo que es posible que un programa confunda al enlazador al proporcionar un símbolo de un tipo en lugar de un símbolo diferente y hacer que el programa se vincule con éxito.
Eso es lo que sucede aquí: el vinculador espera encontrar el símbolo main
, y lo hace. "Alaba" ese símbolo como si fuera una función, porque no lo conoce mejor. La parte de la biblioteca de tiempo de ejecución que pasa el control a main
pregunta al enlazador por main
, por lo que el enlazador le da el símbolo main
, permitiendo que la fase de enlace se complete. Por supuesto, esto falla en tiempo de ejecución, porque main
no es una función.
Aquí hay otra ilustración del mismo problema:
archivo xc:
#include <stdio.h>
int foo(); // <<== main() expects this
int main(){
printf("%p/n", (void*)&foo);
return 0;
}
archivo yc:
int foo; // <<== external definition supplies a symbol of a wrong kind
compilando:
gcc x.c y.c
Esto compila, y probablemente se ejecutará, pero es un comportamiento indefinido, porque el tipo de símbolo prometido al compilador es diferente del símbolo real suministrado al vinculador.
En cuanto a la advertencia, creo que es razonable: C le permite construir bibliotecas que no tienen una función main
, por lo que el compilador libera el nombre main
para otros usos si necesita definir una variable main
por algún motivo desconocido.
Es una advertencia ya que técnicamente no está permitida. 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 es así, 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 cual funciona int main (int argc, char ** argv): el compilador no conoce el argumento envp y simplemente no se usa, y es la limpieza de la persona que llama.
Como broma, podrías hacer algo como
int main = 0xCBCBCBCB;
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 (tipo de) que se ejecuta en múltiples arquitecturas directamente - http://phrack.org/issues/57/17.html#article . También se usó para ganar el IOCCC - http://www.ioccc.org/1984/mullender/mullender.c .
Me gustaría agregar a las respuestas ya dadas citando los estándares de lenguaje actuales.
Es ''int principal;'' un programa C válido?
Respuesta corta (mi opinión): solo si su implementación usa un "entorno de ejecución independiente".
Todas las siguientes citas de C11
5. Medio ambiente
Una implementación traduce archivos fuente C y ejecuta programas C en dos entornos de sistema de procesamiento de datos, que se denominarán entorno de traducción y entorno de ejecución [...]
5.1.2 Entornos de ejecución
Se definen dos entornos de ejecución: independientes y alojados. En ambos casos, el inicio del programa se produce cuando el entorno de ejecución llama a una función C designada.
5.1.2.1 Entorno independiente
En un entorno independiente (en el que la ejecución del programa C puede tener lugar sin ningún beneficio de un sistema operativo), el nombre y el tipo de la función llamada al inicio del programa están definidos por la implementación.
5.1.2.2 Entorno hospedado
No es necesario proporcionar un entorno alojado, pero debe cumplir con las siguientes especificaciones, si están presentes.
5.1.2.2.1 Inicio del programa
La función llamada al inicio del programa se llama main . [...] Se definirá con un tipo de retorno de int y sin parámetros [...] o con dos parámetros [...] o equivalentes o de alguna otra manera definida por la implementación.
De estos, se observa lo siguiente:
- Un programa C11 puede tener un entorno de ejecución autónomo o alojado y ser válido.
- Si tiene una independiente, no necesita existir una función principal.
- De lo contrario, debe haber uno con un valor de retorno de tipo int .
En un entorno de ejecución independiente, yo diría que es un programa válido que no permite 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, aunque su código introduce un objeto llamado main , no puede proporcionar un valor de retorno, por lo que podría argumentar que no es un programa válido en este sentido, aunque también podría argumentarse como antes si el programa no es destinado a ser ejecutado (en caso de que desee proporcionar datos solo por ejemplo), entonces simplemente no permite hacer eso.
Es ''int principal;'' un programa válido de C ++?
Respuesta corta (mi opinión): solo si su implementación usa un "entorno de ejecución independiente".
Cita de C++14
3.6.1 Función principal
Un programa debe contener una función global llamada main, que es el inicio designado del programa. Está definido por la implementación si se requiere un programa en un entorno independiente para definir una función principal. [...] Tendrá un tipo de retorno de tipo int, pero de lo contrario su tipo está definido por la implementación. [...] El nombre principal no está reservado de otro modo.
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.
De nuevo, yo diría que para el caso alojado, su código no es un programa válido de C ++ 14, pero estoy seguro de que es para el caso independiente.
Como mi respuesta solo considera el entorno de ejecución , creo que la respuesta de dasblinkenlicht entra en juego, ya que el cambio de nombre que se produce en el entorno de traducción ocurre de antemano. Aquí, no estoy tan seguro de que las citas anteriores se observen tan estrictamente.
No, este no es un programa válido.
Para C ++ esto fue explícitamente hecho recientemente por el informe de defectos 1886: Enlace de idioma para main () que dice:
No parece haber ninguna restricción para dar a main () un enlace de lenguaje explícito, pero probablemente debería estar mal formado o tener soporte condicional.
y parte de la resolución incluyó el siguiente cambio:
Un programa que declara una variable principal en el ámbito global o que declara el nombre principal con el enlace del lenguaje C (en cualquier espacio de nombres) está mal formado.
Podemos encontrar esta redacción en el último borrador del estándar C ++ N4527 que es el borrador C ++ 1z.
Las últimas versiones de clang y gcc ahora hacen de esto un error ( verlo en vivo ):
error: main cannot be declared as global variable
int main;
^
Antes de este informe de defectos, era un comportamiento indefinido que no requiere un diagnóstico. Por otro lado, un código mal formado requiere un diagnóstico, el compilador puede hacer de esto una advertencia o un error.
Para C hasta ahora, es el comportamiento definido de implementación.
Como dice ISO / IEC9899:
5.1.2.2.1 Inicio del programa
1 La función llamada al inicio del programa se llama main. La implementación no declara ningún prototipo para esta función. Se definirá con un tipo de retorno de int y sin parámetros:
int main(void) { /* ... */ }
o con dos parámetros (a los que se hace referencia aquí como argc y argv, aunque se pueden usar los nombres, ya que son locales para la función en la que se declaran):
int main(int argc, char *argv[]) { /* ... */ }
o equivalente; o de alguna otra manera definida por la implementación.
main
no es una palabra reservada , es solo un identificador predefinido (como cin
, endl
, npos
...), por lo que podría declarar una variable llamada main
, inicializarla e imprimir su valor.
Por supuesto:
- la advertencia es útil ya que es bastante propenso a errores;
- puede tener un archivo fuente sin la función
main()
(bibliotecas).
EDITAR
Algunas referencias:
main
no es una palabra reservada (C ++ 11):La función
main
no se usará dentro de un programa. El enlace (3.5) demain
está definido por la implementación. Un programa que define main como deleted o que declara que main está eninline
,static
oconstexpr
está mal formado. El nombremain
no está reservado de otra manera. [Ejemplo: las funciones miembro, las clases y las enumeraciones se pueden llamarmain
, al igual que las entidades en otros espacios de nombres. - ejemplo final]C ++ 11 - [basic.start.main] 3.6.1.3
[2.11 / 3] [...] algunos identificadores están reservados para ser utilizados por las implementaciones de C ++ y las bibliotecas estándar (17.6.4.3.2) y no deben utilizarse de otro modo; no se requiere diagnóstico.
[17.6.4.3.2 / 1] Ciertos conjuntos de nombres y firmas de funciones siempre se reservan para la implementación:
- Cada nombre que contiene un doble guión bajo __ o comienza con un guión bajo seguido de una letra mayúscula (2.12) está reservado para la implementación para cualquier uso.
- Cada nombre que comienza con un guión bajo está reservado a la implementación para su uso como nombre en el espacio de nombres global.
Palabras reservadas en lenguajes de programación .
Las palabras reservadas no pueden ser redefinidas por el programador, pero las predefinidas a menudo pueden ser anuladas en alguna capacidad. Este es el caso de
main
: hay ámbitos en los que una declaración que usa ese identificador redefine su significado.
Es int main;
un programa válido de C / C ++?
No está del todo claro qué es un programa C / C ++.
Es int main;
un programa C válido?
Sí. Una implementación independiente puede aceptar dicho programa. main
no tiene que tener ningún significado especial en un entorno independiente.
No es válido en un entorno alojado.
Es int main;
un programa válido de C ++?
Ídem.
¿Por qué se cuelga?
El programa no tiene que tener sentido en tu 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é el compilador me advierte?
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 necesita para diagnosticar un programa no conforme. Como 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?
Sí.
gcc
documenta la bandera de compilación -ffreestanding
. Agréguelo y la advertencia desaparecerá. Es posible que desee utilizarlo al construir, por ejemplo, kernels o firmware.
g++
no documenta tal indicador. El suministro parece no tener ningún efecto en este programa. Probablemente sea seguro suponer que el entorno proporcionado por g ++ está alojado. La ausencia de diagnóstico en este caso es un error.
¿Es un programa válido?
No.
No es un programa ya que no tiene partes ejecutables.
¿Es válido compilar?
Sí.
¿Se puede usar con un programa válido?
Sí.
No se requiere que todo el código compilado sea ejecutable para que sea válido. Los ejemplos son bibliotecas estáticas y dinámicas.
Has construido efectivamente un archivo de objeto. No es un ejecutable válido, sin embargo, otro programa podría vincular al objeto main
en el archivo resultante al cargarlo en tiempo de ejecución.
¿Debería ser esto un error?
Tradicionalmente, C ++ le permite al usuario hacer cosas que pueden parecer que no tienen un uso válido, pero que se ajustan a la sintaxis del lenguaje.
Quiero decir que seguro, esto podría ser reclasificado como un error, pero ¿por qué? ¿Qué propósito serviría para que la advertencia no lo haga?
Siempre que haya una posibilidad teórica de que esta funcionalidad se utilice en el código real, es muy poco probable que tener un objeto no funcional llamado main
genere un error de acuerdo con el idioma.