¿Qué debería devolver main () en C y C ++?


695

28
Todavía creo que es bastante vago también. Definir "más eficiente" para mí. ¿Eficiente en qué sentido? ¿En el sentido de ocupar menos memoria? ¿En el sentido de correr más rápido? Puedo ver las respuestas útiles, pero sigo pensando que la pregunta está mal formulada.
Onorio Catenacci

77
Pish posh, el contexto de eficiente es obvio aquí, especialmente con los ejemplos (que probablemente estén allí para aclarar la definición de 'eficiente'). Con suerte, el pobre buffer no se metió en un agujero y lamentó la pregunta por completo. Se podría decir, independientemente de void o int, se devuelve un valor, por lo que no tiene impacto en el tamaño del archivo, las operaciones ejecutadas ni la memoria asignada. Y las personas, en la mayoría de los sistemas operativos, tienden a devolver 0 en caso de éxito, y algo más en -otro- éxito o fracaso, pero no hay un estándar. En última instancia, no hay diferencia en la eficiencia de ninguna manera obvia.
Kit10

"correcto (más eficiente)" no tiene sentido. Eficiente es una cosa, correcto es otra. mainse llama una vez (y en C ++ solo se puede llamar una vez: sin recursión). Si no desea que la ejecución pase mucho tiempo main, no invoque el programa muchas veces: haga que el programa implemente la repetición.
Kaz

2
Me parece interesante que ninguna de las respuestas, por lo que yo puedo decir, proporcionar un ejemplo completamente de trabajo, incluyendo las #includedeclaraciones
PUK

3
Los valores de retorno no tienen sentido en una plataforma sin sistema operativo. No vas a volver a nada. Si llega a un returnen main(...)en un dispositivo integrado, el sistema entra en un estado impredecible y la lavadora se convertirá en consciente de sí mismo y tratar de matar. Entonces, usamos void main()en ese caso. Esta es una práctica estándar de la industria en incrustado de metal desnudo.
3Dave

Respuestas:


570

El valor de retorno de mainindica cómo salió el programa. La salida normal está representada por un valor de retorno 0 desde main. La salida anormal se indica mediante un retorno distinto de cero, pero no existe un estándar sobre cómo se interpretan los códigos distintos de cero. Como han señalado otros, void main()está prohibido por el estándar C ++ y no debe usarse. Las mainfirmas válidas de C ++ son:

int main()

y

int main(int argc, char* argv[])

que es equivalente a

int main(int argc, char** argv)

También vale la pena señalar que en C ++, int main()puede dejarse sin una declaración de retorno, en cuyo punto el valor predeterminado es devolver 0. Esto también es cierto con un programa C99. Si return 0;debe omitirse o no, está abierto a debate. El rango de firmas principales del programa C válido es mucho mayor.

La eficiencia no es un problema con la mainfunción. Solo se puede ingresar y dejar una vez (marcando el inicio y la finalización del programa) de acuerdo con el estándar C ++. Para C, el reingreso main()está permitido, pero debe evitarse.


69
main PUEDE ingresarse / salir varias veces, pero ese programa probablemente no ganaría ningún premio de diseño;)
korona

13
C99 también tiene la característica errónea de C ++ de que alcanzar el final de la función main () es equivalente a devolver 0, si main () está definido para devolver un tipo compatible con int (sección 5.1.2.2.3).
Jonathan Leffler

62
volver a ingresar a main no es válido en C ++. Explícitamente en el estándar, 3.6.1.3 establece que 'main no debe usarse dentro de un programa'
workmad3

117
stdlib.h proporciona EXIT_SUCCESS y EXIT_FAILURE para este propósito
Clay

20
0 y no cero son correctos, pero no tienen ningún significado para alguien que lea su código. Esta pregunta es una prueba de que las personas no saben qué son los códigos válidos / inválidos. EXIT_SUCCESS / EXIT_FAILURE son mucho más claros.
JaredPar

169

La respuesta aceptada parece estar dirigida a C ++, por lo que pensé en agregar una respuesta que pertenezca a C, y esto difiere en algunas formas.

ISO / IEC 9899: 1989 (C90):

main() debe declararse como:

int main(void)
int main(int argc, char **argv)

O equivalente. Por ejemplo, int main(int argc, char *argv[])es equivalente al segundo. Además, el inttipo de retorno puede omitirse ya que es un valor predeterminado.

Si una implementación lo permite, main()puede declararse de otras maneras, pero esto hace que la implementación del programa se defina y ya no sea estrictamente conforme.

El estándar define 3 valores para el retorno que son estrictamente conformes (es decir, no se basa en el comportamiento definido por la implementación): 0y EXIT_SUCCESSpara una terminación exitosa y EXIT_FAILUREpara una terminación fallida. Cualquier otro valor no es estándar y su implementación está definida. main()debe tener una returndeclaración explícita al final para evitar un comportamiento indefinido.

Finalmente, no hay nada de malo desde el punto de vista estándar con las llamadas main()desde un programa.

ISO / IEC 9899: 1999 (C99):

Para C99, todo es igual que arriba excepto:

  • El inttipo de retorno no puede omitirse.
  • Puede omitir la declaración de devolución de main(). Si lo hace, y main()terminado, hay un implícito return 0.

1
@Lundin No creo que necesite una cita para decir que a alguien se le permite hacer un compilador que acepte programas que no cumplan con los estándares, o que tenga un compilador que no cumpla con las normas estándar. Eso es conocimiento común y sentido común
KABoissonneault

44
@KABoissonneault El comportamiento definido por la implementación es un término del estándar, en oposición al comportamiento completamente indocumentado. Si implementa algo que aparece como comportamiento definido por la implementación, aún sigue el estándar. En este caso, C89, que fue citado, no enumera tal comportamiento definido por la implementación, de ahí la necesidad de una cita, para demostrar que no solo está inventando cosas de la nada.
Lundin

1
@Lundin Estás viendo esto de manera incorrecta. De lo que estamos hablando no es de un comportamiento definido por la implementación, estamos hablando de una implementación que se desvía del estándar si así lo eligen. Es más como un niño que desobedece a sus padres: no necesita una cita de los padres para decirle de qué manera un niño puede ir en contra de lo que los padres dijeron. Solo sabes que en el momento en que el niño elige hacerlo, ya no cumple con las líneas de
culpabilidad de

2
@KABoissonneault La parte que cité en mi comentario es definitivamente sobre el comportamiento definido por la implementación (a diferencia de las extensiones del compilador no estándar ). Por lo tanto, estoy hablando del comportamiento definido por la implementación. Si está teniendo un monólogo sobre otra cosa, la mejor de las suertes con eso.
Lundin

1
@Lundin Supongo que la redacción de la cita es confusa (la parte donde dicen "pero esto define la implementación del programa"), pero estoy bastante seguro de que la persona estaba hablando de un comportamiento no estándar (como se dice en "Si una implementación lo permite "y" y ya no se ajusta estrictamente [al estándar] ") en oposición al comportamiento definido de implementación real. La persona definitivamente debería reformular su respuesta, pero todavía no creo que sea necesaria una cita de la norma sobre eso
KABoissonneault

117

Estándar C - Entorno alojado

Para un entorno alojado (ese es el normal), el estándar C11 (ISO / IEC 9899: 2011) dice:

5.1.2.2.1 Inicio del programa

Se nombra la función llamada al inicio del programa main. La implementación no declara ningún prototipo para esta función. Se definirá con un tipo de retorno de inty sin parámetros:

int main(void) { /* ... */ }

o con dos parámetros (referidos aquí como argcy argv, aunque se pueden usar nombres, ya que son locales para la función en la que se declaran):

int main(int argc, char *argv[]) { /* ... */ }

o equivalente; 10) o de alguna otra manera definida por la implementación.

Si se declaran, los parámetros de la función principal obedecerán las siguientes restricciones:

  • El valor de argcserá no negativo.
  • argv[argc] será un puntero nulo.
  • Si el valor de argces mayor que cero, los miembros de la matriz a argv[0]través de argv[argc-1]inclusivo contendrán punteros a las cadenas, a los que el entorno host les da valores definidos por la implementación antes del inicio del programa. La intención es proporcionar al programa información determinada antes del inicio del programa desde cualquier otra parte del entorno alojado. Si el entorno host no es capaz de suministrar cadenas con letras en mayúsculas y minúsculas, la implementación garantizará que las cadenas se reciban en minúsculas.
  • Si el valor de argces mayor que cero, la cadena a la que apunta argv[0] representa el nombre del programa; argv[0][0]será el carácter nulo si el nombre del programa no está disponible en el entorno del host. Si el valor de argces mayor que uno, las cadenas señaladas por argv[1]mediante argv[argc-1] representan los parámetros del programa.
  • Los parámetros argcy argvlas cadenas a las que apunta la argvmatriz serán modificables por el programa y conservarán sus últimos valores almacenados entre el inicio y la finalización del programa.

10) Por lo tanto, intse puede reemplazar por un nombre typedef definido como int, o el tipo de argvse puede escribir como char **argv, y así sucesivamente.

Terminación del programa en C99 o C11

El valor devuelto desde main()se transmite al 'entorno' de una manera definida por la implementación.

5.1.2.2.3 Terminación del programa

1 Si el tipo de retorno de la mainfunción es un tipo compatible con int, un retorno de la llamada inicial a la mainfunción es equivalente a llamar a la exitfunción con el valor devuelto por la mainfunción como argumento; 11) alcanzar el }que finaliza la mainfunción devuelve un valor de 0. Si el tipo de retorno no es compatible int, el estado de terminación devuelto al entorno host no está especificado.

11) De conformidad con 6.2.4, la vida útil de los objetos con una duración de almacenamiento automática declarada main habrá finalizado en el primer caso, incluso cuando no lo hubieran hecho en el segundo.

Tenga en cuenta que 0es obligatorio como 'éxito'. Puede usar EXIT_FAILUREy EXIT_SUCCESSdesde <stdlib.h>si lo prefiere, pero 0 está bien establecido, y también lo es 1. Vea también Códigos de salida mayores que 255 - ¿posible? .

En C89 (y, por lo tanto, en Microsoft C), no hay una declaración sobre lo que sucede si la main()función regresa pero no especifica un valor de retorno; Por lo tanto, conduce a un comportamiento indefinido.

7.22.4.4 La exitfunción

¶5 Finalmente, el control se devuelve al entorno del host. Si el valor de statuses cero o EXIT_SUCCESS, se devuelve una forma definida por la implementación del estado de terminación exitosa . Si el valor de statuses EXIT_FAILURE, se devuelve una forma definida por la implementación del estado de terminación fallida . De lo contrario, el estado devuelto está definido por la implementación.

Standard C ++ - Entorno Hospedado

El estándar C ++ 11 (ISO / IEC 14882: 2011) dice:

3.6.1 Función principal [basic.start.main]

¶1 Un programa contendrá una función global llamada main, que es el inicio designado del programa. [...]

¶2 Una implementación no predefinirá la función principal. Esta función no se sobrecargará. Tendrá un tipo de retorno de tipo int, pero de lo contrario su tipo está definido por la implementación. Todas las implementaciones deberán permitir las dos definiciones siguientes de main:

int main() { /* ... */ }

y

int main(int argc, char* argv[]) { /* ... */ }

En la última forma se argcindicará el número de argumentos pasados ​​al programa desde el entorno en el que se ejecuta el programa. Si argcno es cero, estos argumentos se proporcionarán argv[0] mediante argv[argc-1]punteros a los caracteres iniciales de las cadenas multibyte terminadas en nulo (NTMBS) (17.5.2.1.4.2) y argv[0]serán el puntero al carácter inicial de un NTMBS que representa el nombre utilizado para invocar el programa o "". El valor de argcno será negativo. El valor de argv[argc] será 0. [Nota: Se recomienda agregar más parámetros (opcionales) después argv. —Nota final]

¶3 La función mainno se utilizará dentro de un programa. El enlace (3.5) de mainestá definido por la implementación. [...]

¶5 Una declaración de retorno en main tiene el efecto de abandonar la función main (destruir cualquier objeto con duración de almacenamiento automática) y llamar std::exit con el valor return como argumento. Si el control llega al final de main sin encontrar una declaración de retorno, el efecto es el de ejecutar

return 0;

El estándar C ++ dice explícitamente "[la función principal] tendrá un tipo de tipo de retorno int, pero de lo contrario su tipo está definido por la implementación", y requiere las mismas dos firmas que el estándar C para ser admitido como opciones. Por lo tanto, el estándar C ++ no permite directamente un 'void main ()', aunque no hay nada que pueda hacer para detener una implementación no estándar que permita alternativas. Tenga en cuenta que C ++ prohíbe que el usuario llame main(pero el estándar C no).

Hay un párrafo de §18.5 Inicio y terminación en el estándar C ++ 11 que es idéntico al párrafo de §7.22.4.4 La exitfunción en el estándar C11 (citado anteriormente), aparte de una nota al pie (que simplemente documenta queEXIT_SUCCESS y EXIT_FAILUREestá definido en <cstdlib>).

Estándar C - Extensión común

Clásicamente, los sistemas Unix admiten una tercera variante:

int main(int argc, char **argv, char **envp) { ... }

El tercer argumento es una lista de punteros a cadenas con terminación nula, cada una de las cuales es una variable de entorno que tiene un nombre, un signo de igual y un valor (posiblemente vacío). Si no usa esto, aún puede acceder al medio ambiente a través de ' extern char **environ;'. Esta variable global es única entre aquellos en POSIX en que no tiene un encabezado que la declare.

El estándar C reconoce esto como una extensión común, documentada en el Anexo J:

J.5.1 Argumentos del entorno

¶1 En un entorno alojado, la función principal recibe un tercer argumento, char *envp[]que apunta a una matriz de punteros terminados en nulo char, cada uno de los cuales apunta a una cadena que proporciona información sobre el entorno para esta ejecución del programa (5.1. 2.2.1).

Microsoft C

El compilador de Microsoft VS 2010 es interesante. El sitio web dice:

La sintaxis de declaración para main es

 int main();

u opcionalmente

int main(int argc, char *argv[], char *envp[]);

Alternativamente, las funciones mainy wmainse pueden declarar como de retorno void(sin valor de retorno). Si declara maino wmaincomo devolución nula, no puede devolver un código de salida al proceso principal o al sistema operativo utilizando una declaración de devolución. Para devolver un código de salida cuando maino wmainse declara como void, debe usar la exitfunción

No me queda claro qué sucede (qué código de salida se devuelve al padre o al SO) cuando un programa con void main() sale, y el sitio web de MS también permanece en silencio.

Curiosamente, MS no prescribe la versión de dos argumentos main()que requieren los estándares C y C ++. Solo prescribe una forma de tres argumentos donde el tercer argumento eschar **envp , un puntero a una lista de variables de entorno.

La página de Microsoft también enumera algunas otras alternativas: wmain() que requieren cadenas de caracteres anchas y algunas más.

La versión de Microsoft Visual Studio 2005 de esta página no aparece void main()como alternativa. Las versiones de Microsoft Visual Studio 2008 en adelante sí.

Estándar C - Entorno independiente

Como se señaló anteriormente, los requisitos anteriores se aplican a los entornos alojados. Si está trabajando con un entorno independiente (que es la alternativa a un entorno alojado), entonces el estándar tiene mucho menos que decir. Para un entorno independiente, la función llamada al inicio del programa no necesita ser llamada mainy no hay restricciones en su tipo de retorno. El estándar dice:

5.1.2 Entornos de ejecución

Se definen dos entornos de ejecución: independiente y alojado. En ambos casos, el inicio del programa ocurre cuando el entorno de ejecución invoca una función C designada. Todos los objetos con una duración de almacenamiento estático se inicializarán (se establecerán en sus valores iniciales) antes del inicio del programa. La manera y el momento de dicha inicialización no están especificados. La finalización del programa devuelve el control al entorno de ejecución.

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. Cualquier instalación de biblioteca disponible para un programa independiente, que no sea el conjunto mínimo requerido por la cláusula 4, está definida por la implementación.

El efecto de la finalización del programa en un entorno independiente está definido por la implementación.

La referencia cruzada a la cláusula 4 Conformidad se refiere a esto:

¶5 Un programa estrictamente conforme utilizará solo aquellas características del idioma y la biblioteca especificadas en esta Norma Internacional. 3) No producirá resultados dependientes de ningún comportamiento no especificado, indefinido o definido por la implementación, y no deberá exceder ningún límite mínimo de implementación.

¶6 Las dos formas de implementación conforme son alojadas e independientes . Una implementación hospedada conforme deberá aceptar cualquier programa estrictamente conforme. Una aplicación autoportante conforme aceptará cualquier programa estrictamente conforme en la que el uso de las características especificadas en la cláusula de biblioteca (cláusula 7) se limita al contenido de las cabeceras estándar <float.h>, <iso646.h>, <limits.h>, <stdalign.h>, <stdarg.h>, <stdbool.h>, <stddef.h>, <stdint.h>, y <stdnoreturn.h>. Una implementación conforme puede tener extensiones (incluidas funciones de biblioteca adicionales), siempre que no alteren el comportamiento de ningún programa estrictamente conforme. 4)

¶7 Un programa conforme es uno que es aceptable para una implementación conforme. 5)

3) Un programa estrictamente conforme puede usar características condicionales (ver 6.10.8.3) siempre que el uso esté protegido por una directiva de preprocesamiento de inclusión condicional apropiada usando la macro relacionada. Por ejemplo:

#ifdef __STDC_IEC_559__ /* FE_UPWARD defined */
    /* ... */
    fesetround(FE_UPWARD);
    /* ... */
#endif

4) Esto implica que una implementación conforme no reserva identificadores distintos a los reservados explícitamente en esta Norma Internacional.

5) Los programas estrictamente conformes están destinados a ser máximamente portátiles entre implementaciones conformes. Los programas conformes pueden depender de características no portátiles de una implementación conforme.

Es notable que el único encabezado requerido de un entorno independiente que realmente define cualquier función es <stdarg.h>(e incluso esas pueden ser, y a menudo son, solo macros).

Estándar C ++ - Entorno independiente

Así como el estándar C reconoce tanto el entorno alojado como el independiente, también lo hace el estándar C ++. (Citas de ISO / IEC 14882: 2011.)

1.4 Cumplimiento de implementación [intro.compliance]

¶7 Se definen dos tipos de implementaciones: una implementación alojada y una implementación independiente . Para una implementación alojada, esta Norma Internacional define el conjunto de bibliotecas disponibles. Una implementación independiente es aquella en la que la ejecución puede tener lugar sin el beneficio de un sistema operativo, y tiene un conjunto de bibliotecas definidas por la implementación que incluye ciertas bibliotecas compatibles con el lenguaje (17.6.1.3).

¶8 Una implementación conforme puede tener extensiones (incluidas funciones de biblioteca adicionales), siempre que no alteren el comportamiento de ningún programa bien formado. Se requieren implementaciones para diagnosticar programas que usan tales extensiones que están mal formadas de acuerdo con esta Norma Internacional. Una vez hecho esto, sin embargo, pueden compilar y ejecutar dichos programas.

¶9 Cada implementación incluirá documentación que identifique todas las construcciones soportadas condicionalmente que no admite y define todas las características específicas de la localidad. 3

3) Esta documentación también define el comportamiento definido por la implementación; ver 1.9.

17.6.1.3 Implementaciones independientes [cumplimiento]

Se definen dos tipos de implementaciones: alojadas e independientes (1.4). Para una implementación alojada, esta Norma Internacional describe el conjunto de encabezados disponibles.

Una implementación independiente tiene un conjunto de encabezados definidos por la implementación. Este conjunto debe incluir al menos los encabezados que se muestran en la Tabla 16.

La versión suministrada de la cabecera <cstdlib>declarará al menos las funciones abort, atexit, at_quick_exit, exit, y quick_exit(18.5). Los otros encabezados enumerados en esta tabla deberán cumplir los mismos requisitos que para una implementación hospedada.

Tabla 16 - Encabezados de C ++ para implementaciones independientes

Subclause                           Header(s)
                                    <ciso646>
18.2  Types                         <cstddef>
18.3  Implementation properties     <cfloat> <limits> <climits>
18.4  Integer types                 <cstdint>
18.5  Start and termination         <cstdlib>
18.6  Dynamic memory management     <new>
18.7  Type identification           <typeinfo>
18.8  Exception handling            <exception>
18.9  Initializer lists             <initializer_list>
18.10 Other runtime support         <cstdalign> <cstdarg> <cstdbool>
20.9  Type traits                   <type_traits>
29    Atomics                       <atomic>

¿Qué pasa con el uso int main()en C?

El estándar §5.1.2.2.1 del estándar C11 muestra la notación preferida  int main(void)- pero también hay dos ejemplos en el estándar que muestran int main(): §6.5.3.4 ¶8 y §6.7.6.3 ¶20 . Ahora, es importante tener en cuenta que los ejemplos no son 'normativos'; son solo ilustrativos. Si hay errores en los ejemplos, no afectan directamente el texto principal del estándar. Dicho esto, son muy indicativos del comportamiento esperado, por lo que si el estándar incluye int main()un ejemplo, sugiere que int main()no está prohibido, incluso si no es la notación preferida.

6.5.3.4 Los operadores sizeofy_Alignof

...

¶8 EJEMPLO 3 En este ejemplo, el tamaño de una matriz de longitud variable se calcula y se devuelve desde una función:

#include <stddef.h>

size_t fsize3(int n)
{
    char b[n+3]; // variable length array
    return sizeof b; // execution time sizeof
}
int main()
{
    size_t size;
    size = fsize3(10); // fsize3 returns 13
    return 0;
}

@DavidBowling: una definición de función como int main(){ … }sí especifica que la función no toma argumentos, pero no proporciona un prototipo de función, AFAICT. Porque main()eso rara vez es un problema; significa que si tiene llamadas recursivas a main(), los argumentos no serán verificados. Para otras funciones, es más un problema: realmente necesita un prototipo de alcance cuando se llama a la función para garantizar que los argumentos sean correctos.
Jonathan Leffler

1
@DavidBowling: No sueles llamar main()recursivamente, fuera de lugares como IOCCC. Tengo un programa de prueba que lo hace, principalmente por novedad. Si tiene int i = 0; int main() { if (i++ < 10) main(i, i * i); return 0; }y compila con GCC y no incluye -Wstrict-prototypes, se compila limpiamente bajo advertencias estrictas. Si es así main(void), no se compila.
Jonathan Leffler

61

Creo que main()debería volver EXIT_SUCCESSo EXIT_FAILURE. Se definen enstdlib.h


20
0 también es estándar.
Chris Young

2
@ChrisYoung Hay EXIT_SUCCESSy EXIT_FAILUREporque algunos sistemas operativos históricos (¿VMS?) Usaron un número diferente que 0 para denotar el éxito. Es 0 en todas partes hoy en día.
fuz

55
@FUZxxl tienes razón, pero eso no está en conflicto con mi comentario. EXIT_SUCCESS puede ser distinto de cero, pero todos los estándares (C89, C99, C11) definen 0 (así como EXIT_SUCCESS) para que también sea una forma definida por la implementación de la terminación exitosa del estado.
Chris Young

2
@FUZxxl: Es cierto que el VMS usó valores impares (como 1) para indicar el éxito y valores pares (como 0) para indicar una falla. Desafortunadamente, el estándar ANSI C original se interpretó en el sentido de que EXIT_SUCCESS tenía que ser 0, por lo que devolver EXIT_SUCCESS desde main obtuvo exactamente el comportamiento incorrecto en VMS. Lo que había que hacer portátil para VMS era usarlo exit(EXIT_SUCCESS), lo que siempre hacía lo correcto.
Adrian McCarthy

1
5.1.2.2.3 "Si el tipo de retorno de la función principal es un tipo compatible con int, un retorno de la llamada inicial a la función principal es equivalente a llamar a la función de salida con el valor devuelto por la función principal como argumento; 11) alcanzar el} que termina la función principal devuelve un valor de 0. "
Lundin

38

Tenga en cuenta que los estándares C y C ++ definen dos tipos de implementaciones: independientes y alojadas.

  • Entorno alojado C90

    Formas permitidas 1 :

    int main (void)
    int main (int argc, char *argv[])
    
    main (void)
    main (int argc, char *argv[])
    /*... etc, similar forms with implicit int */

    Comentarios:

    Los dos primeros se indican explícitamente como las formas permitidas, los otros están implícitamente permitidos porque C90 permitió "int implícito" para el tipo de retorno y los parámetros de función. No se permite ninguna otra forma.

  • Entorno independiente C90

    Se permite cualquier forma o nombre de main 2 .

  • Entorno alojado C99

    Formas permitidas 3 :

    int main (void)
    int main (int argc, char *argv[])
    /* or in some other implementation-defined manner. */

    Comentarios:

    C99 eliminó "implícito int" para main() lo que ya no es válido.

    Se ha introducido una oración extraña y ambigua "o de alguna otra manera definida por la implementación". Esto puede interpretarse como "los parámetros paraint main() pueden variar" o como "main puede tener cualquier forma definida por la implementación".

    Algunos compiladores han elegido interpretar el estándar de la última manera. Podría decirse que no se puede afirmar fácilmente que no se ajustan estrictamente al citar el estándar en sí mismo, ya que es ambiguo.

    Sin embargo, permitir formas completamente salvajes de main()probablemente no era (?) La intención de esta nueva oración. La lógica de C99 (no normativa) implica que la oración se refiere a parámetros adicionales a int main 4 .

    Sin embargo, la sección para la finalización del programa de entorno alojado continúa discutiendo sobre el caso donde main no devuelve int 5 . Aunque esa sección no es normativa sobre cómo debe declararse main, definitivamente implica que main podría declararse de una manera completamente definida por la implementación, incluso en sistemas alojados.

  • Entorno independiente C99

    Cualquier forma o nombre de main está permitido 6 .

  • Entorno alojado C11

    Formularios permitidos 7 :

    int main (void)
    int main (int argc, char *argv[])
    /* or in some other implementation-defined manner. */
  • Entorno independiente C11

    Se permite cualquier forma o nombre de main 8 .


Tenga en cuenta que int main()nunca se incluyó como un formulario válido para ninguna implementación alojada de C en ninguna de las versiones anteriores. En C, a diferencia de C ++, ()y (void)tienen diferentes significados. La primera es una característica obsoleta que puede eliminarse del idioma. Consulte las instrucciones futuras del lenguaje C11:

6.11.6 Declaradores de funciones

El uso de declaradores de funciones con paréntesis vacíos (no declaradores de tipo de parámetro de formato prototipo) es una característica obsoleta.


  • Entorno alojado C ++ 03

    Formularios permitidos 9 :

    int main ()
    int main (int argc, char *argv[])

    Comentarios:

    Tenga en cuenta el paréntesis vacío en la primera forma. C ++ y C son diferentes en este caso, porque en C ++ esto significa que la función no toma parámetros. Pero en C significa que puede tomar cualquier parámetro.

  • C ++ 03 entorno independiente

    El nombre de la función llamada al inicio está definido por la implementación. Si se nombra main()debe seguir los formularios indicados 10 :

    // implementation-defined name, or 
    int main ()
    int main (int argc, char *argv[])
  • Entorno alojado C ++ 11

    Formularios permitidos 11 :

    int main ()
    int main (int argc, char *argv[])

    Comentarios:

    El texto del estándar ha sido cambiado pero tiene el mismo significado.

  • Entorno independiente C ++ 11

    El nombre de la función llamada al inicio está definido por la implementación. Si se nombra main()debe seguir los formularios 12 establecidos :

    // implementation-defined name, or 
    int main ()
    int main (int argc, char *argv[])

Referencias

  1. ANSI X3.159-1989 2.1.2.2 Entorno alojado. "Inicio del programa"

    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 (referidos aquí como argc y argv, aunque se pueden usar nombres, ya que son locales para la función en la que se declaran):

    int main(int argc, char *argv[]) { /* ... */ }
  2. ANSI X3.159-1989 2.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.

  3. ISO 9899: 1999 5.1.2.2 Entorno alojado -> 5.1.2.2.1 Inicio del programa

    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 (referidos aquí como argc y argv, aunque se pueden usar nombres, ya que son locales para la función en la que se declaran):

    int main(int argc, char *argv[]) { /* ... */ }

    o equivalente; 9) o de alguna otra manera definida por la implementación.

  4. Justificación de la norma internacional - Lenguajes de programación - C, Revisión 5.10. 5.1.2.2 Entorno alojado -> 5.1.2.2.1 Inicio del programa

    El comportamiento de los argumentos a main, y de la interacción de exit, main y atexit (ver §7.20.4.2) se ha codificado para frenar alguna variedad no deseada en la representación de cadenas argv y en el significado de los valores devueltos por main.

    La especificación de argc y argv como argumentos para main reconoce una práctica previa extensa. Se requiere que argv [argc] sea un puntero nulo para proporcionar una verificación redundante para el final de la lista, también sobre la base de la práctica común.

    main es la única función que se puede declarar de forma portátil con cero o dos argumentos. (El número de argumentos de otras funciones debe coincidir exactamente entre invocación y definición.) Este caso especial simplemente reconoce la práctica generalizada de dejar los argumentos a main cuando el programa no accede a las cadenas de argumentos del programa. Si bien muchas implementaciones admiten más de dos argumentos, la práctica no es bendecida ni prohibida por el Estándar; un programa que define main con tres argumentos no es estrictamente conforme (ver §J.5.1.).

  5. ISO 9899: 1999 5.1.2.2 Entorno alojado -> 5.1.2.2.3 Finalización del programa

    Si el tipo de retorno de la función principal es un tipo compatible con int, un retorno de la llamada inicial a la función principal es equivalente a llamar a la función de salida con el valor devuelto por la función principal como argumento; 11) llegar al }que termina la función principal devuelve un valor de 0. Si el tipo de retorno no es compatible con int, el estado de terminación devuelto al entorno host no está especificado.

  6. ISO 9899: 1999 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.

  7. ISO 9899: 2011 5.1.2.2 Entorno alojado -> 5.1.2.2.1 Inicio del programa

    Esta sección es idéntica a la C99 citada anteriormente.

  8. ISO 9899: 1999 5.1.2.1 Entorno independiente

    Esta sección es idéntica a la C99 citada anteriormente.

  9. ISO 14882: 2003 3.6.1 Función principal

    Una implementación no debe predefinir la función principal. Esta función no se sobrecargará. Tendrá un tipo de retorno de tipo int, pero de lo contrario su tipo está definido por la implementación. Todas las implementaciones deberán permitir las dos definiciones siguientes de main:

    int main() { /* ... */ }

    y

    int main(int argc, char* argv[]) { /* ... */ }
  10. ISO 14882: 2003 3.6.1 Función principal

    Se define la implementación si se requiere un programa en un entorno independiente para definir una función principal.

  11. ISO 14882: 2011 3.6.1 Función principal

    Una implementación no debe predefinir la función principal. Esta función no se sobrecargará. Tendrá un tipo de retorno de tipo int, pero de lo contrario su tipo está definido por la implementación. Todas las implementaciones permitirán ambos

    - una función de () que devuelve int y

    - una función de (int, puntero a puntero a char) que devuelve int

    como el tipo de main (8.3.5).

  12. ISO 14882: 2011 3.6.1 Función principal

    Esta sección es idéntica a la de C ++ 03 mencionada anteriormente.


Una pregunta: ¿los estándares de C ++ significan que la firma de la función de inicio en entornos independientes también se define como implementación? Por ejemplo, una implementación podría haber definido la función de inicio como: int my_startup_function ()o, ¿ int my_startup_function (int argc, char *argv[])puede tener, por ejemplo, char my_startup_function (long argc, int *argv[])también una función de inicio? Supongo que no, ¿verdad? Además, ¿no es tan ambiguo también?
Utku

@Utku Puede tener cualquier firma, siempre que no tenga nombre main()porque debe usar una de las firmas enumeradas. Me imagino que sería abrumadoramente el más común void my_startup_function (), ya que no tiene sentido regresar del programa en sistemas independientes.
Lundin

1
Veo. Pero si se le permite usar cualquier nombre y firma para la función de inicio, ¿por qué no permitir también usar una firma diferente main? Lo siento si esa no es una pregunta inteligente, pero no podía entender el razonamiento detrás.
Utku

@Utku C y C ++ son diferentes allí. En cuanto a por qué C ++ hace cumplir esto, no tengo idea, no hay justificación. Sospecho que el principal culpable (juego de palabras) es Stroustrup, quien desde el principio declaró que main debe regresar int, punto. Porque cuando hizo la primera versión de C ++, solo estaba acostumbrado a los sistemas alojados. En la publicación vinculada, Stroustrup todavía parece ignorar la existencia de implementaciones independientes: por ejemplo, se está refiriendo ignorantemente al subcapítulo de implementación alojada del estándar C, ignorando la existencia del capítulo 5.1.2.1.
Lundin

1
Lo notable sobre el borrador estándar C11 es que, aunque func()se considera obsoleto, el borrador en sí lo utiliza int main()en sus propios ejemplos.
Antti Haapala

29

Devuelve 0 en caso de éxito y distinto de cero por error. Este es el estándar utilizado por las secuencias de comandos UNIX y DOS para averiguar qué sucedió con su programa.


8

main() en C89 y K&R C, los tipos de retorno no especificados tienen por defecto 'int'.

return 1? return 0?
  1. Si no escribe una declaración de devolución int main(), el cierre {devolverá 0 de forma predeterminada.

  2. return 0o return 1será recibido por el proceso de los padres. En un shell, entra en una variable de shell, y si está ejecutando su programa desde un shell y no usa esa variable, entonces no necesita preocuparse por el valor de retorno de main().

Consulte ¿Cómo puedo obtener lo que ha devuelto mi función principal? .

$ ./a.out
$ echo $?

De esta manera puede ver que es la variable la $?que recibe el byte menos significativo del valor de retorno de main().

En las secuencias de comandos Unix y DOS, return 0generalmente se devuelven en caso de error y no cero para el error. Este es el estándar utilizado por las secuencias de comandos Unix y DOS para averiguar qué sucedió con su programa y controlar todo el flujo.


44
Estrictamente hablando, $?no es una variable de entorno; Es una variable de shell predefinida (o incorporada). La diferencia es difícil de detectar, pero si ejecuta env(sin ningún argumento), imprime el entorno y $?no se mostrará en el entorno.
Jonathan Leffler

1
Devolver 0 automáticamente cuando las principales "caídas del final" son solo en C ++ y C99 en adelante, no en C90.
Kaz

Error tipográfico: "cierre {" debería ser }. SO no me permitirá hacer una edición tan pequeña.
Spencer

7

Tenga en cuenta que, aunque devuelva un int, algunos sistemas operativos (Windows) truncan el valor devuelto a un solo byte (0-255).


44
Unix hace lo mismo, como probablemente la mayoría de los otros sistemas operativos. Sé que VMS hace cosas tan increíbles y extrañas que devolver cualquier cosa que no sea EXIT_SUCCESS o EXIT_FAILURE es un problema.
Leon Timmermans

2
MSDN pide diferencias: cuando se informa a través de mscorlib, un código de salida es un entero de 32 bits con signo . Esto parece implicar que las bibliotecas de tiempo de ejecución C que truncan los códigos de salida son defectuosas.

Si, esto es incorrecto. En Windows se devuelve un entero de 32 bits (y se convierte a unsigned). Esto es lo mismo en sistemas UNIX con enteros de 32 bits. Pero los shells de estilo UNIX en cualquiera de los sistemas generalmente solo retendrán un número entero de 8 bits sin signo.
John McFarlane

4

El sistema operativo puede utilizar el valor de retorno para verificar cómo se cerró el programa.

El valor de retorno 0 generalmente significa OK en la mayoría de los sistemas operativos (en los que puedo pensar de todos modos).

También se puede verificar cuando llama a un proceso usted mismo y ver si el programa salió y terminó correctamente.

Es NO sólo una convención de programación.


No hay nada en la pregunta que indique que hay un sistema operativo presente. Devolver un valor no tiene ningún sentido en un sistema independiente.
Lundin

3

El valor de retorno de main()muestra cómo salió el programa. Si el valor de retorno es zero, significa que la ejecución fue exitosa, mientras que cualquier valor distinto de cero representará que algo salió mal en la ejecución.


1
Este es un comentario, no una respuesta a la pregunta.
Lundin

2

Tenía la impresión de que el estándar especifica que main no necesita un valor de retorno ya que un retorno exitoso se basó en el sistema operativo (cero en uno podría ser un éxito o un fracaso en otro), por lo tanto, la ausencia de retorno fue una señal para el compilador para insertar el retorno exitoso en sí.

Sin embargo, generalmente devuelvo 0.


C99 (y C ++ 98) le permiten omitir la declaración de devolución de main; C89 no le permite omitir la declaración de devolución.
Jonathan Leffler

Este es un comentario, no una respuesta.
Lundin

Esto no proporciona una respuesta a la pregunta. Para criticar o solicitar una aclaración de un autor, deje un comentario debajo de su publicación.
Steve Lillis

66
@SteveLillis: en 2008 SO no tenía una sección de comentarios.
graham.reeds

2

Devolver 0 debería decirle al programador que el programa ha finalizado con éxito el trabajo.


Devolver 1 de main()normalmente indica que ocurrió un error; devolver 0 indica éxito. Si sus programas siempre fallan, entonces 1 está bien, pero no es la mejor idea.
Jonathan Leffler

1
@ JonathanLeffler: El significado de regresar 1de mainestá definido por la implementación. Los únicos valores definidos por el lenguaje son 0, EXIT_SUCCESS(a menudo definidos como 0), y EXIT_FAILURE. En OpenVMS, return 1;denota terminación exitosa .
Keith Thompson

VMS no es "normal", en el sentido de lo que dije. ¿No es algo así como 'cualquier valor extraño es el éxito; incluso los valores son fallidos en VMS?
Jonathan Leffler

2

Omitir return 0

Cuando un programa C o C ++ llega al final del maincompilador, generará automáticamente código para devolver 0, por lo que no es necesario ponerlo return 0;explícitamente al final de main.

Nota: cuando hago esta sugerencia, casi siempre es seguida por uno de dos tipos de comentarios: "No lo sabía". o "¡Eso es un mal consejo!" Mi razonamiento es que es seguro y útil confiar en el comportamiento del compilador respaldado explícitamente por el estándar. Para C, desde C99; ver ISO / IEC 9899: 1999 sección 5.1.2.2.3:

[...] un retorno de la llamada inicial a la mainfunción es equivalente a llamar a la exitfunción con el valor devuelto por la mainfunción como argumento; alcanzar el }que termina la mainfunción devuelve un valor de 0.

Para C ++, desde el primer estándar en 1998; ver ISO / IEC 14882: 1998 sección 3.6.1:

Si el control llega al final de main sin encontrar una declaración return, el efecto es el de ejecutar return 0;

Todas las versiones de ambos estándares desde entonces (C99 y C ++ 98) han mantenido la misma idea. Confiamos en funciones miembro generadas automáticamente en C ++, y pocas personas escriben return;declaraciones explícitas al final de una voidfunción. Las razones contra la omisión parecen reducirse a "se ve raro" . Si, como yo, tiene curiosidad sobre la justificación del cambio al estándar C, lea esta pregunta . También tenga en cuenta que a principios de la década de 1990 esto se consideraba "práctica descuidada" porque era un comportamiento indefinido (aunque ampliamente respaldado) en ese momento.

Además, las Pautas principales de C ++ contienen múltiples instancias de omisión return 0;al final demain y ninguna instancia en la que se escriba un retorno explícito. Aunque todavía no hay una directriz específica sobre este tema en particular en ese documento, eso parece al menos un respaldo tácito de la práctica.

Así que abogo por omitirlo; otros no están de acuerdo (¡a menudo con vehemencia!) En cualquier caso, si encuentra código que lo omite, sabrá que el estándar lo respalda explícitamente y sabrá lo que significa.


2
Este es un mal consejo porque los compiladores que implementan solo C89, no un estándar posterior, siguen siendo extremadamente comunes (escribo esto en 2017) y seguirán siendo extremadamente comunes en el futuro previsible. Por ejemplo, la última vez que verifiqué que ninguna versión de los compiladores de Microsoft implementó C99, y tengo entendido que esto también es típico de los compiladores de sistemas integrados que no son GCC.
zwol

44
@zwol: Cualquiera que no tenga más remedio que usar un compilador que esté desactualizado por 28 años probablemente tenga más problemas que decidir si incluirlo explícitamente return 0;, sin embargo, me gustaría señalar que muchos compiladores de esa época también implementaron un implícito return 0;incluso antes de que fuera estandarizado.
Edward

2
En realidad, trabajo mucho en sistemas integrados y no he encontrado un compilador que no sea compatible implícito return 0durante más de una década. También las versiones actuales de Microsoft C también lo admiten . ¿Quizás su información está desactualizada?
Edward

2
Puedo apreciar que esto es controvertido en C, casi (por @zwol). En C ++ cualquier controversia en torno a esto es pura tontería.
Carreras de ligereza en órbita

2
@ Edward No dije que la controversia no existía, dije que no tenía sentido: P
Las carreras de ligereza en órbita el

1

Lo que debe devolver depende de lo que desee hacer con el ejecutable. Por ejemplo, si está utilizando su programa con un shell de línea de comando, entonces necesita devolver 0 para un éxito y un no cero para el fracaso. Entonces podrá utilizar el programa en shells con procesamiento condicional dependiendo del resultado de su código. También puede asignar cualquier valor distinto de cero según su interpretación, por ejemplo, para errores críticos, diferentes puntos de salida del programa podrían terminar un programa con diferentes valores de salida, y que está disponible para el shell de llamada que puede decidir qué hacer al inspeccionar el valor devuelto. Si el código no está destinado a usarse con shells y el valor devuelto no molesta a nadie, entonces podría omitirse. Yo personalmente uso la firmaint main (void) { .. return 0; .. }


El formato de main () está determinado por la implementación, lo que significa compilador. El programador no elige qué formulario elegir, excepto cuando un compilador admite varios formularios.
Lundin

@Lundin El tipo de retorno será implementado por la implementación. Pero el valor que se devuelve lo decide el programador. La Sección 5.1.2.2.3 de C99 menciona que el tipo de retorno de maines compatible con int. Por lo tanto, regresar intno será un problema. Aunque se permiten otros tipos de retorno, en ese caso la variable de entorno que tiene el valor de retorno no se especificará. Pero si un programador lo hace return 0;en bash, puede usarse para hacer ramas.
foxis

1

Si realmente tiene problemas relacionados con la eficiencia de devolver un número entero de un proceso, probablemente debería evitar llamar a ese proceso tantas veces que este valor devuelto se convierta en un problema.

Si está haciendo esto (llame a un proceso tantas veces), debe encontrar una manera de poner su lógica directamente dentro de la persona que llama, o en un archivo DLL, sin asignar un proceso específico para cada llamada; Las asignaciones de procesos múltiples le brindan el problema de eficiencia relevante en este caso.

En detalle, si solo desea saber si devolver 0 es más o menos eficiente que devolver 1, podría depender del compilador en algunos casos, pero genéricamente, suponiendo que se lean desde la misma fuente (local, campo, constante, incrustado en el código, resultado de la función, etc.) requiere exactamente el mismo número de ciclos de reloj.


1

Aquí hay una pequeña demostración del uso de códigos de retorno ...

Al usar las diversas herramientas que proporciona el terminal de Linux, se puede usar el código de retorno, por ejemplo, para el manejo de errores después de que se haya completado el proceso. Imagine que el siguiente archivo de texto myfile está presente:

Este es un ejemplo para comprobar cómo funciona grep.

Cuando ejecuta el comando grep, se crea un proceso. Una vez que está terminado (y no se rompió), devuelve un código entre 0 y 255. Por ejemplo:

$ grep order myfile

Si lo haces

$ echo $?
$ 0

obtendrás un 0. ¿Por qué? Porque grep encontró una coincidencia y devolvió un código de salida 0, que es el valor habitual para salir con éxito. Vamos a verlo de nuevo pero con algo que no está dentro de nuestro archivo de texto y, por lo tanto, no se encontrarán coincidencias:

$ grep foo myfile
$ echo $?
$ 1

Dado que grep no pudo hacer coincidir el token "foo" con el contenido de nuestro archivo, el código de retorno es 1 (este es el caso habitual cuando ocurre una falla, pero como se indicó anteriormente, tiene muchos valores para elegir).

Ahora el siguiente script bash (simplemente escríbalo en un terminal de Linux) aunque muy básico debería dar una idea del manejo de errores:

$ grep foo myfile
$ CHECK=$?
$ [ $CHECK -eq 0] && echo 'Match found'
$ [ $CHECK -ne 0] && echo 'No match was found'
$ No match was found

Después de la segunda línea, no se imprime nada en el terminal ya que "foo" hizo que grep devolviera 1 y verificamos si el código de retorno de grep era igual a 0. La segunda instrucción condicional hace eco de su mensaje en la última línea, ya que es verdadero debido a CHECK == 1.

Como puede ver si está llamando a este y a ese proceso, a veces es esencial ver lo que ha devuelto (por el valor de retorno de main ()).


En un script de shell, usaría if grep foo myfile; then echo 'Match found'; else echo 'No match was found'; fi: probar el estado de devolución directamente. Si desea capturar el estado (para informes, etc.), entonces utiliza una asignación. Puede usar if grep foo myfile; CHECK=$?; [ "$CHECK" = 0 ]; then echo 'Match found'; else echo 'No match was found'; fio puede usar tres líneas. También puede usar las opciones -sy -qpara grepevitar que aparezcan las coincidencias o los mensajes de error de rutina. Sin embargo, esto es minucias de shell, el punto clave, que el estado de salida puede ser útil, está bien.
Jonathan Leffler

1

¿Cuál es la forma correcta (más eficiente) de definir la función main () en C y C ++ - int main () o void main () - y por qué?

Esas palabras "(más eficiente)" no cambian la pregunta. A menos que se encuentre en un entorno independiente, hay una forma universalmente correcta de declararmain() , y es como regresar int.

Que debería main() volver en C y C ++?

No es lo que debería main() regresar, es lo que main() regresa. main()es, por supuesto, una función que alguien más llama. No tienes ningún control sobre el código que llama main(). Por lo tanto, debe declarar main()con una firma de tipo correcto para que coincida con la persona que llama. Simplemente no tienes otra opción en el asunto. No tiene que preguntarse qué es más o menos eficiente, o qué es mejor o peor estilo, ni nada de eso, porque la respuesta ya está perfectamente bien definida, para usted, según los estándares C y C +. Solo síguelos.

Si int main () entonces devuelve 1 o devuelve 0?

0 para el éxito, distinto de cero para el fracaso. De nuevo, no es algo que necesite (o que elija): está definido por la interfaz a la que se supone que se está conformando.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.