todas retorno parametros lenguaje las funciones funcion ejemplos con c++ c

c++ - retorno - ¿Cómo funciona el método main() en C?



funciones en lenguaje c (9)

Sé que hay dos firmas diferentes para escribir el método principal:

int main() { //Code }

o para manejar un argumento de línea de comando, lo escribimos como-

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

En C++ sé que podemos sobrecargar un método, pero en C ¿cómo maneja el compilador estas dos firmas diferentes de la función main ?


Bueno, las dos firmas diferentes de la misma función main () solo aparecen cuando usted las quiere, es decir, si su programa necesita datos antes del procesamiento real de su código, puede pasarlos mediante el uso de -

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

donde la variable argc almacena el recuento de datos que se pasan y argv es una matriz de punteros a char que apunta a los valores pasados ​​desde la consola. De lo contrario, siempre es bueno ir con

int main() { //Code }

Sin embargo, en cualquier caso, puede haber uno y solo un main () en un programa, porque ese es el único punto desde el que un programa inicia su ejecución y, por lo tanto, no puede ser más de uno. (espero que sea digno)


Algunas de las características del lenguaje C comenzaron como hacks que funcionaban.

Múltiples firmas para listas de argumentos principales, así como de longitud variable, es una de esas características.

Los programadores notaron que pueden pasar argumentos adicionales a una función, y nada malo sucede con su compilador dado.

Este es el caso si las convenciones de llamada son tales que:

  1. La función de llamada limpia los argumentos.
  2. Los argumentos de la izquierda están más cerca de la parte superior de la pila, o de la base del marco de la pila, de modo que los argumentos espurios no invaliden el direccionamiento.

Un conjunto de convenciones de llamada que obedece a estas reglas es el paso de parámetros basado en la pila mediante el cual la persona que llama muestra los argumentos y los empuja de derecha a izquierda:

;; pseudo-assembly-language ;; main(argc, argv, envp); call push envp ;; rightmost argument push argv ;; push argc ;; leftmost argument ends up on top of stack call main pop ;; caller cleans up pop pop

En los compiladores donde este tipo de convención de llamadas es el caso, no es necesario hacer nada especial para respaldar los dos tipos de tipos main o incluso adicionales. main puede ser una función de ningún argumento, en cuyo caso es ajeno a los elementos que se insertaron en la pila. Si se trata de una función de dos argumentos, entonces encuentra a argc y argv como los dos elementos de la pila superior. Si se trata de una variante de tres argumentos específica de la plataforma con un puntero de entorno (una extensión común), eso también funcionará: encontrará ese tercer argumento como el tercer elemento de la parte superior de la pila.

Por lo tanto, una llamada fija funciona para todos los casos, lo que permite vincular un único módulo de inicio fijo con el programa. Ese módulo podría escribirse en C, como una función que se parece a esto:

/* I''m adding envp to show that even a popular platform-specific variant can be handled. */ extern int main(int argc, char **argv, char **envp); void __start(void) { /* This is the real startup function for the executable. It performs a bunch of library initialization. */ /* ... */ /* And then: */ exit(main(argc_from_somewhere, argv_from_somewhere, envp_from_somewhere)); }

En otras palabras, este módulo de inicio solo llama a un argumento principal de tres argumentos, siempre. Si main no toma argumentos, o solo int, char ** , resulta que funciona bien, así como si no toma argumentos, debido a las convenciones de llamada.

Si tuviera que hacer este tipo de cosas en su programa, no sería portable y se consideraría un comportamiento indefinido por ISO C: declarar y llamar a una función de una manera, y definirla en otra. Pero el truco inicial de un compilador no tiene que ser portátil; no está guiado por las reglas para programas portátiles.

Pero supongamos que las convenciones de llamada son tales que no puede funcionar de esta manera. En ese caso, el compilador tiene que tratar main especialmente. Cuando se da cuenta de que está compilando la función main , puede generar código que es compatible con, por ejemplo, una llamada de tres argumentos.

Es decir, usted escribe esto:

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

Pero cuando el compilador lo ve, esencialmente realiza una transformación de código para que la función que compila se parezca más a esto:

int main(int __argc_ignore, char **__argv_ignore, char **__envp_ignore) { /* ... */ }

excepto que los nombres __argc_ignore no existen literalmente. No se incluyen dichos nombres en su alcance, y no habrá ninguna advertencia sobre los argumentos no utilizados. La transformación del código hace que el compilador emita código con el enlace correcto que sabe que tiene que limpiar tres argumentos.

Otra estrategia de implementación es para el compilador o quizás el enlazador para generar de forma personalizada la función __start (o como se llame), o al menos seleccionar una de varias alternativas precompiladas. La información podría almacenarse en el archivo objeto sobre cuál de las formas soportadas de main se está utilizando. El vinculador puede ver esta información y seleccionar la versión correcta del módulo de inicio que contiene una llamada a main que es compatible con la definición del programa. Las implementaciones C generalmente tienen solo un pequeño número de formas main admitidas, por lo que este enfoque es factible.

Los compiladores para el lenguaje C99 siempre tienen que tratar main especialmente, hasta cierto punto, para soportar el hack que si la función termina sin una declaración return , el comportamiento es como si se ejecutara return 0 . Esto, de nuevo, puede tratarse mediante una transformación de código. El compilador nota que se está compilando una función llamada main . Luego verifica si el final del cuerpo es potencialmente alcanzable. Si es así, inserta un return 0;


El main es solo un nombre para una dirección de inicio decidida por el vinculador donde main es el nombre predeterminado. Todos los nombres de funciones en un programa son direcciones de inicio donde comienza la función.

Los argumentos de la función son empujados / reventados en / desde la pila, por lo que si no hay argumentos especificados para la función, no hay argumentos activados / desactivados en la pila. Así es como main puede funcionar tanto con argumentos como sin ellos.


Esta es una de las extrañas asimetrías y reglas especiales del lenguaje C y C ++.

En mi opinión, existe solo por razones históricas y no hay una lógica real detrás de esto. Tenga en cuenta que main es especial también por otras razones (por ejemplo, main en C ++ no puede ser recursivo y no puede tomar su dirección y en C99 / C ++ se le permite omitir una declaración de return final).

Tenga en cuenta también que incluso en C ++ no es una sobrecarga ... ya sea un programa tiene la primera forma o tiene la segunda forma; no puede tener ambos.


Lo inusual de main no es que se pueda definir de más de una manera, sino que solo se puede definir de dos maneras diferentes.

main es una función definida por el usuario; la implementación no declara un prototipo para ello.

Lo mismo ocurre con foo o bar , pero puede definir funciones con esos nombres de la forma que desee.

La diferencia es que main es invocado por la implementación (el entorno de tiempo de ejecución), no solo por su propio código. La implementación no se limita a la semántica de llamadas a la función C ordinaria, por lo que puede (y debe) tratar algunas variaciones, pero no es necesario manejar infinitas posibilidades. El formato int main(int argc, char *argv[]) permite argumentos de línea de comandos, e int main(void) en C o int main() en C ++ es solo una conveniencia para programas simples que no necesitan procesar argumentos de línea de comandos.

En cuanto a cómo el compilador maneja esto, depende de la implementación. La mayoría de los sistemas probablemente tienen convenciones de llamadas que hacen que las dos formas sean efectivamente compatibles, y cualquier argumento pasado a una main definida sin parámetros se ignora silenciosamente. De lo contrario, no sería difícil para un compilador o enlazador tratar el tema main especialmente. Si tiene curiosidad sobre cómo funciona en su sistema , puede consultar algunas listas de ensamblaje.

Y como muchas cosas en C y C ++, los detalles son en gran parte el resultado de la historia y las decisiones arbitrarias tomadas por los diseñadores de los idiomas y sus predecesores.

Tenga en cuenta que tanto C como C ++ permiten otras definiciones definidas por la implementación para main , pero rara vez hay una buena razón para usarlas. Y para las implementaciones independientes (como los sistemas integrados sin SO), el punto de entrada del programa está definido por la implementación y ni siquiera se llama necesariamente main .


NO hay sobrecarga de main incluso en C ++. La función principal es el punto de entrada para un programa y solo debe existir una definición única.

Para el estándar C

Para un entorno alojado (que es el normal), el estándar C99 dice:

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 (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; 9) o de alguna otra manera definida por la implementación.

9) Por lo tanto, int puede ser reemplazado por un nombre typedef definido como int , o el tipo de argv puede escribirse como char **argv , y así sucesivamente.

Para C ++ estándar:

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

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

2 Una implementación no debe predefinir la función principal. Esta función no debe estar sobrecargada . 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 ambas de las siguientes definiciones de main:

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

y

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

El estándar de C ++ dice explícitamente que "[la función principal] tendrá un tipo de retorno de tipo int, pero de lo contrario su tipo es implementación definida", y requiere las mismas dos firmas que el estándar C.

En un entorno alojado ( entorno de CA que también admite las bibliotecas C): el sistema operativo llama a main .

En un entorno no alojado (Uno destinado a aplicaciones integradas) siempre puede cambiar el punto de entrada (o salida) de su programa utilizando las directivas del preprocesador como

#pragma startup [priority] #pragma exit [priority]

Donde prioridad es un número integral opcional.

El arranque Pragma ejecuta la función antes de que la salida principal (prioridad-sabia) y pragma ejecute la función después de la función principal. Si hay más de una directiva de inicio, entonces la prioridad decide cuál se ejecutará primero.


No es necesario anular esto. Porque solo se usará uno por vez. Si hay 2 versiones diferentes de la función principal.


No hay necesidad de sobrecargar. Sí, hay 2 versiones, pero solo se puede usar una en ese momento.


Una pregunta similar se hizo antes: ¿Por qué compila una función sin parámetros (en comparación con la definición de la función real)?

Una de las mejores respuestas fue:

En C func() significa que puede pasar cualquier cantidad de argumentos. Si no quiere argumentos, debe declarar como func(void)

Entonces, supongo que es como se declara main (si se puede aplicar el término "declaró" a main ). De hecho, puedes escribir algo como esto:

int main(int only_one_argument) { // code }

y aún se compilará y ejecutará.