Disponible la versión 6 de OrganiZATOR
Descubre un nuevo concepto en el manejo de la información.
La mejor ayuda para sobrevivir en la moderna jungla de datos la tienes aquí.

Curso C++

[Home]  [Inicio]  [Índice]


4.4.4  La función main

§1  Sinopsis

Durante la fase de enlazado de la compilación ( 1.4.4 ), el "linker" añade a cualquier programa C++ un módulo especial, de inicio, que es realmente el punto de entrada a la ejecución del programa. Este módulo realiza diversas tareas preparatorias a la ejecución propiamente dicha. Por ejemplo, iniciar todas las variables estáticas o globales y realizar determinadas verificaciones del hardware. Finalmente pasa el control a una función que debe responder al nombre de main y le pasa algunos argumentos en base a datos que ha recibido a su vez del Sistema Operativo; esta es la razón por la que todos los programas C++ deben contener una función con este nombre [4]. Así pues, main representa el punto de la ejecución a partir del cual el programador toma el control de la ejecución, antes de esto ya han sucedido muchas cosas en nuestro programa. A su vez el punto de finalización de esta función, su punto de retorno (return) significa el fin del programa [3], pero recuerde que existe otra forma de terminar el programa, que puede ser utilizada desde cualquier punto (sin necesidad de volver a la función main), consiste en utilizar la función exit ( 1.5.1) de la Librería Estándar.

Nota: algunos compiladores permiten al programador la opción de que se llamen determinadas funciones antes que se realice la llamada a main. Estas funciones representan tareas preparatorias adicionales que queremos realizar antes de que se inicie la ejecución. Esto se consigue con la directiva de inicio #pragma startup. En cualquier caso, las variables estáticas son inicializadas antes que estas funciones iniciales (1.5).

§2 Argumentos

  La función main es imprescindible en cualquier programa C/C++ representa el punto de inicio de su ejecución. Por lo general, su declaración adopta la forma:

int main();

aunque en realidad, el módulo de inicio la invoca con dos parámetros (recibidos a su vez del SO), denominados tradicionalmente argc y argv, contracciones de "argument count" y "argument vector" respectivamente. El primero es un entero que representa el número de comandos que se pasan; el segundo es un puntero a una matriz de cadenas literales de distintas longitudes (es decir: puntero a matriz de punteros); cada una de estas cadenas representa en último extremo los comandos iniciales que se quieren pasar al programa, generalmente para controlar aspectos de su comportamiento. Así pues, la declaración más genérica de main es del tipo:

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

Nota: el Estándar establece que el compilador debe aceptar para main cualquiera de las dos formas anteriores.


Por convención, argv[0] es el nombre con que se ha llamado al programa (normalmente será el nombre del fichero ejecutable incluyendo su dirección completa -path-). Este dato es proporcionado automáticamente por el SO; así pues, el valor mínimo para argc es 1. Después seguirán los que introduzcamos en la línea de comandos, separados por espacios.


§2.1  Como ejemplo, puede usarse el siguiente programa, al que llamaremos prueba.exe:

#include <stdio.h>       // Prueba de parámetros para función main

int main(int argc, char* argv[]) {
  int i;
  printf("Se han pasado %3d argumentos:\n", argc);
  for(i=0; i<argc; i++) printf("%5d- %s\n", i, argv[i]);
  return 0;
}


Si introducimos en la línea de comandos: prueba se pasan seis, parametros, se obtiene la siguiente salida [1]:

Se han pasado  6 argumentos:
   1- D:\ZF\LEARNC\PRUEBA.EXE
   2- prueba
   3- se
   4- pasan
   5- seis,
   6- parametros


Se ha dicho que argv es un puntero a matriz de punteros, y en el ejemplo hemos utilizado la expresión argv[i], algo que a primera vista puede chocar, ya que argv no es una matriz. En realidad la declaración completa: char* argv[] significa puntero-a-matriz-de-caracteres, para distinguirlo de char* argv que sería simplemente puntero-a-carácter.


§2.2  Los argumentos anteriores son recogidos por el sistema en forma de sendas variables globales _argc y _argv ( 4.1.3a) definidas en la cabecera <dos.h>. A continuación se expone una variación del programa anterior que utiliza estas variables:

#include <iostream>
using namespace std;
#include <dos.h>       // necesario para _argc y argv

int main (int argc, char* argv[]) {   // ==============
  cout << "Se han pasado " << _argc << " argumentos:" << endl;
  for (int i = 0; i < _argc; ++i)
    cout << " " << i << "- " << _argv[i] << endl;
  return 0;
}

La salida es la misma que en el caso anterior.

§3  Restricciones

La función main adolece de ciertas limitaciones que la diferencian del resto de funciones C++:

  • No puede ser invocada explícitamente a lo largo del programa, es invocada de forma automática por el módulo de inicio
  • No puede obtenerse su dirección, por lo tanto no pueden declararse punteros a ella:

    int (* pmain)() = &main;   // Error!!

  • No puede ser sobrecargada ( 4.4.1a).
  • No puede ser declarada como inline ( 4.4.6b).
  • main debe estar en el espacio global de una de las unidades de compilación del programa, lo que significa que no puede pertenecer a una clase.
§4  Valor devuelto por main

En muchos sistemas operativos el valor devuelto por la función main es utilizado como control de estado para el entorno desde el que se ha ejecutado el programa (este valor es considerado el estado de retorno del programa). En UNIX, MS-DOS y MS Windows, los 8 bits más bajos del valor devuelto son pasados al programa invocante o al intérprete de comandos. Este "estado de retorno" se utiliza en ocasiones para cambiar el curso de un programa, un proceso por lotes o un guión para el ejecutor de comandos.

Algunos compiladores pueden generar un aviso de error si se intenta compilar un programa cuya función main no devuelva un int. Por contra, algunas plataformas pueden provocar fallos cuando estos programas arrancan o a su terminación, ya que esperan un int de retorno.

En los programas C++ no es necesario devolver nada desde la función main, aunque en realidad la definición de esta sea int main(). La razón es que el Estándar garantiza que si main llega a su final alcanzando el corchete de cierre "}" sin haber encontrado una sentencia return, el compilador debe devolver automáticamente un 0 indicando un final correcto, de forma que en ausencia de ningún retorno el compilador proporciona automáticamente un return 0; [2].

Nota: no obstante lo anterior, el comportamiento concreto varía de un compilador a otro. Por ejemplo, un programa en el que se alcanza el corchete de cierre de la función main sin ninguna sentencia de retorno compila sin dificultad ni aviso de ningún tipo en Borland C++ 5.5, mientras que MS Visual C++ 6.0 avisa: warning C4508: 'main' : function should return a value; 'void' return type assumed.

En cualquier caso, es práctica de buena programación incluir un valor de retorno para la función main. Tradicionalmente 0 significa una terminación correcta del programa, cualquier otro valor es señal de terminación anormal (error o excepción).

Si el programa termina por una invocación a la función exit, el valor devuelto por main es el argumento pasado a exit ( 1.5.1). Por ejemplo, si el programa contiene la llamada exit(1), el valor devuelto es 1.


§4.1  Existen solamente tres valores completamente estándar y portables que puedan utilizarse como retorno para la función main, o como argumento para la función exit:

  • El clásico entero de valor 0.
  • La constante simbólica ( 1.4.1a) EXIT_SUCCESS definida en <stdlib.h>
  • La constante EXIT_FAILURE definida en <stdlib.h>

Si se utiliza cualquiera de los dos valores 0 o EXIT_SUCCESS se garantiza que el compilador los trasladará a un código que sea considerado terminación correcta por el Sistema Operativo. Si se utiliza EXIT_FAILURE el compilador lo trasladará a un código que será considerado como terminación errónea por el Sistema Operativo.


§4.2  Algunos sistemas, como Unix, MS-DOS y Windows, truncan el entero pasado a exit o devuelto por main, a un unsigned char, con objeto de utilizarlos en el fichero de órdenes del intérprete de comandos, en un fichero de ejecución por lotes, o en el proceso que invocó el programa. Algunos programadores utilizan valores positivos para indicar diferentes razones de fallo, excepción o finalización del programa, pero estos usos no son necesariamente portables ni funcionan en todas las implementaciones. Sin embargo C++ proporciona la posibilidad de devolver valores que son totalmente portables mediante la función atexit ( 1.5.1).

  Inicio.


[1]  Naturalmente, el "path" de la segunda línea: "D:\ZF\LEARNC\" depende del entorno de trabajo.

[2]  Con excepción de main, C++ no proporciona un valor de retorno automático para ninguna otra función. Si la función invocante espera un valor y la función llamada llega a su fin sin haber encontrado un return adecuado, se devuelve un valor que es basura.

[3]  En los programas C++ destinados a correr en una plataforma convencional, el módulo de inicio invoca a una función que debe responder al nombre main. Es por ejemplo, el caso de los programas C++ compilados para correr en modo texto en plataformas Windows (utilizando una ventana DOS). Responden al estándar, y el módulo de inicio invoca a la función main. En los días en que se desarrolló MS-DOS, Microsoft Corporation no era una empresa tan importante y debía seguir el estándar. Sin embargo, en los que deben correr bajo sistemas Windows utilizando su interfaz gráfica (GUI), el compilador establece que el módulo de inicio invoque una función de nombre WinMain. Esta función representa el punto de entrada a este tipo de aplicaciones, y acepta un cierto número de parámetros (que son los mismos para aplicaciones Windows 3.x y Win-32) y que no se corresponden con los usuales. Por ejemplo, el primer argumento no es "argument count", ni el segundo una matriz de punteros a carácter . Naturalmente esto se aparta del Estándar C++, y representa otra de las peculiaridades establecidas por el gigante de Redmond para su Sistema Operativo ( 1.7.5.1).

[4]  Esto es lo usual en programas que corren bajo control de un Sistema Operativo. Sin embargo, existen sistemas en los que el programa no corre bajo un SO; son sistemas especiales "Freestanding environments", en los que el programa controla todo el entorno de la máquina que los alberga. En estos casos es potestad de la aplicación la existencia de una función main.