¿Es posible escribir un programa sin usar la función main ()?


Sigo recibiendo esta pregunta en las entrevistas:

Escribir un programa sin usar main() función?

Uno de mis amigos me mostró un código usando Macros, pero no pude entenderlo.

Así que la pregunta es:

¿Es realmente posible escribir y compilar un programa sin main()?

Author: Alok Save, 2011-08-13

20 answers

Dentro de C++ estándar se requiere una función main, por lo que la pregunta no tiene sentido para C++estándar.

Fuera del C++ estándar, por ejemplo, puede escribir un programa específico de Windows y usar una de las funciones de inicio personalizadas de Microsoft (wMain, WinMain, wWinmain). En Windows también puede escribir el programa como DLL y usar rundll32 para ejecutarlo.

Aparte de eso, puede crear su propia biblioteca de tiempo de ejecución. En un tiempo que era un deporte común.

Finalmente, usted puede sé inteligente y responde que, de acuerdo con la regla ODR main del estándar, no se "usa", por lo que cualquier programa califica. Bah! Aunque a menos que los entrevistadores tengan un sentido del humor inusual (y no hubieran hecho la pregunta si lo hubieran hecho) no pensarán que esa es una buena respuesta.

 17
Author: Cheers and hth. - Alf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-14 19:58:01

No, no puede a menos que esté escribiendo un programa en un freestanding environment (kernel de SO de entorno embebido, etc.) donde no es necesario que el punto de partida sea main(). Según el estándar de C++ main() es el punto de partida de cualquier programa en un hosted environment.

Según el:

C++03 standard 3.6.1 Main function

1 Un programa contendrá una función global llamada main, que es el inicio designado del programa. Se define la implementación si un programa en un se requiere un entorno independiente para definir una función principal. [Nota: En un entorno independiente, puesta en marcha y termination está definida por la implementación; startup contiene la ejecución de constructores para objetos de ámbito de espacio de nombres con duración de almacenamiento estático; termination contiene la ejecución de destructores para objetos con duración de almacenamiento estático.


¿Qué es freestanding Environment y Qué es Hosted Environment?
Hay dos tipos de implementaciones conformes definidas en el C++ estándar; hosted y freestanding.

Una implementación freestanding es aquella que está diseñada para programas que se ejecutan sin el beneficio de un sistema operativo.
Por ejemplo: Un núcleo del sistema operativo o un entorno embebido sería un entorno independiente.

Un programa que utiliza las instalaciones de un sistema operativo normalmente estaría en un hosted implementation.

De la Sección del Estándar C++03 1.4/7:

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 definido por la implementación que incluye ciertas bibliotecas de soporte de idioma.

Además,
Sección: 17.4.1.3.2 Implementaciones independientes cotizaciones:

Una implementación independiente tiene un conjunto de encabezados definido por la implementación. Este conjunto incluirá al menos las siguientes cabeceras, como se muestra en la Tabla:

18.1 Types <cstddef>   
18.2 Implementation properties <limits>   
18.3 Start and termination <cstdlib> 
18.4 Dynamic memory management <new> 
18.5 Type identification <typeinfo> 
18.6 Exception handling <exception> 
18.7 Other runtime support <cstdarg>
 25
Author: Alok Save,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-05 14:31:53

Programa de ejemplo sin una función principal visible.

/* 
    7050925.c 
    $ gcc -o 7050925 7050925.c
*/

#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)

int begin()
{
        printf("How mainless!\n");
}

De: http://learnhacking.in/c-program-without-main-function/

 14
Author: miku,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-13 14:29:01

main significa un punto de entrada, un punto desde el cual su código comenzará a ejecutarse. aunque main no es la primera función en ejecutarse. Hay más código que se ejecuta antes de main y prepara el entorno para hacer que su código se ejecute. Este código luego llama main. Puede cambiar el nombre de la función main recompilando el código del archivo de inicio crt0.c y cambiando el nombre de la función main. O puede hacer lo siguiente:

#include <stdio.h>

extern void _exit (register int code);

_start()
{
  int retval;
  retval = my_main ();
  _exit(retval);
}

int my_main(void)
{
  printf("Hello\n");
  return 0;
}

Compilar el código con:

gcc -o no_main no_main.c -nostartfiles

El -nostartfiles no incluirá el archivo de inicio predeterminado. Apunta al archivo de entrada principal con el _start.

main no es más que un punto de entrada predefinido para el código de usuario. Por lo tanto, puede nombrarlo como sea, pero al final del día necesita un punto de entrada. En C / C++ y otros lenguajes el nombre se selecciona como main si crea otro lenguaje o hackea las fuentes de estos compiladores de lenguaje, puede cambiar el nombre de main a pain pero traerá dolor, ya que violará las normas.

Pero manipular el nombre de la función de entrada es útil para el código del núcleo, la primera función que se ejecuta en el núcleo, o código escrito para sistemas embebidos.

 10
Author: phoxis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-14 04:47:35

Pueden referirse a un programa escrito para una implementación independiente. El estándar C++ define dos tipos de implementaciones. Una es una implementación alojada. Los programas escritos para esas implementaciones deben tener una función main. Pero por lo demás, no se requiere ninguna función main si la implementación independiente no la requiere. Esto es útil para núcleos de sistema operativo o programas de sistema embebidos que no se ejecutan bajo un sistema operativo.

 8
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-13 14:50:01

Sí es posible compilar con out main pero no se puede pasar la fase de enlace.

 g++ -c noMain.cpp -o noMain.o
 5
Author: Mahesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-13 14:14:37

$ cat > hwa.S
write = 0x04
exit  = 0xfc
.text
_start:
        movl    $1, %ebx
        lea     str, %ecx
        movl    $len, %edx
        movl    $write, %eax
        int     $0x80
        xorl    %ebx, %ebx
        movl    $exit, %eax
        int     $0x80
.data
str:    .ascii "Hello, world!\n"
len = . -str
.globl  _start
$ as -o hwa.o hwa.S
$ ld hwa.o
$ ./a.out
Hello, world!

El núcleo que realmente ejecuta un ejecutable no sabe nada de símbolos internos, solo se transfiere a un punto de entrada especificado en binario en el encabezado de la imagen ejecutable.

La razón por la que necesita un main es porque normalmente su "programa principal" es realmente solo otro módulo. El punto de entrada está en el código de inicio proporcionado por la biblioteca escrito en alguna combinación de C y assembly y ese código de biblioteca simplemente llama a main, por lo que normalmente necesita proporcionar uno. Pero corre el enlazador directamente y no lo haces.

Para incluir un módulo C1...

Mac:~/so$ cat > nomain.S
.text
.globl start
start:
        call   _notmain
Mac:~/so$ as -o nomain.o nomain.S
Mac:~/so$ cat > notmain.c
#include <unistd.h>

void notmain(void) {
  write(1, "hi\n", 3);
  _exit(0);
}
Mac:~/so$ cc -c notmain.c
Mac:~/so$ ld -w nomain.o notmain.o -lc
Mac:~/so$ ./a.out
hi


1. Y también estoy cambiando a x86 - 64 aquí.
 5
Author: DigitalRoss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-14 19:42:51

"Sin usar main" también podría significar que no se permite ninguna lógica dentro de main, pero el main en sí existe. Puedo imaginar que la pregunta había aclarado esto, pero como no está aclarado aquí, esta es otra posible respuesta:

struct MainSub
{
   MainSub()
   {
      // do some stuff
   }
};

MainSub mainSub;

int main(int argc, char *argv[]) { return 0; }

Lo que sucederá aquí es que las cosas en el constructor de MainSub se ejecutarán antes de que se ejecute el inservible main, y puede colocar la lógica del programa allí. Esto, por supuesto, requiere C++, y no C (tampoco está claro en la pregunta).

 3
Author: eran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-13 15:20:34

Creo que la referencia macro era cambiar el nombre de la función principal, lo siguiente no es mi código, y demuestra esto. Sin embargo, el compilador todavía ve una función main, pero técnicamente no hay main desde el punto de vista del código fuente. Lo tengo aquí http://www.exforsys.com/forum/c-and-c/96849-without-main-function-how-post412181.html#post412181

#include<stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)

int begin()
{
  printf(" hello ");
}
 2
Author: eon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-13 14:20:39

Mientras esté usando g++ , podría cambiar su punto de entrada con la opción enlazador -e, por lo que el siguiente comando de código y compilación puede permitirle crear un programa sin una función main():

#import <iostream>

class NoMain
{
public:
    NoMain()
    {
        std::cout << "Hello World!" << std::endl;
        exit(0);
    }
} mainClass;

Di el nombre del archivo como noname.cpp, y la opción de compilación es:

g++ nomain.cpp -Wl,-e,_mainClass -v

A decir verdad, no entendí completamente por qué el código puede funciona bien. Sospecho que la dirección de la variable global mainClass es la misma que la del constructor de la clase NoMain. Sin embargo, también tengo varias razones que podría decir que mi suposición puede no ser correcta.

 2
Author: Joohae Kim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-26 19:09:26

Sin tener en cuenta los estándares específicos del lenguaje, la mayoría del cargador de enlaces proporciona algunos medios para declarar un nombre de función (punto de entrada) que debe ejecutarse cuando se carga el binario se carga en la memoria.

Para el lenguaje c de la vieja escuela, el valor predeterminado era algo como 'start' o '_start', definido en el llamado crt (c runtime?), que realiza varios trabajos domésticos necesarios para las funciones estándar de c, como preparar el montón de memoria, inicializar áreas variables estáticas, analizar la línea de comandos en argc / argv, etc.

Posiblemente podría sobrescribir la función de punto de entrada si toma suficiente cuidado de no usar las funciones estándar que requieren esas cosas domésticas (por ejemplo, malloc (), free (), printf (), cualquier definición de clase tiene constructor personalizado,...) Bastante restrictivo pero no imposible si utiliza funciones proporcionadas por o / s, no por el tiempo de ejecución estándar de c.

Por ejemplo, puede hacer un helloworld simple usando la función write() en el descriptor 1.

 1
Author: shr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-13 15:17:25

Cuando el código C o C++ se ejecuta, se ejecuta en una dirección de inicio conocida, el código aquí inicializa el entorno en tiempo de ejecución, inicializa el puntero de la pila, realiza la inicialización de datos, llama a constructores estáticos, luego salta a main().

El código que hace esto está vinculado con su código en el tiempo de compilación por el enlazador. En el CCG suele estar en crt0.s, con un compilador comercial es poco probable que este código esté disponible para usted.

Al final, tiene que empezar en alguna parte y main() es solo un nombre simbólico para esa ubicación. Está especificado por el estándar de lenguaje para que los desarrolladores sepan cómo llamarlo, de lo contrario el código no sería portátil de una cadena de herramientas a otra.

Si está escribiendo código para un sistema' bare-metal ' sin sistema operativo o al menos sin sistema operativo en el sentido de un cargador de procesos (los sistemas embebidos a menudo incluyen un núcleo RTOS que se inicia después de main ()), entonces en teoría puede llamar al punto de entrada de código C por lo general, tienen un control completo sobre el código de inicio en tiempo de ejecución. Pero hacerlo sería tonto y algo perverso.

Algunos entornos RTOS como VxWorks, y la mayoría de los marcos de aplicaciones en general incluyen main() )o su equivalente) dentro de su propio código de biblioteca para que se ejecute antes que el código de la aplicación de usuario. Por ejemplo, las aplicaciones de VxWorks comienzan desde usrAppInit (), y las aplicaciones Win32 comienzan desde WinMain().

 1
Author: Clifford,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-19 19:27:47

Me doy cuenta de que esta es una vieja pregunta, pero acabo de descubrirla y tuve que compartirla. Probablemente no funcionará con todos los enlazadores, pero al menos es posible engañar a ld (estoy ejecutando la versión 2.24.51.20140918) para que piense que hay una función main - haciendo esto:

int main[] {};

O simplemente

int main;

Luego puede aplicar uno de los trucos antes mencionados para permitir que el programa ejecute algún código, por ejemplo, mediante el uso de un constructor:

struct Main
{
    Main()
    {
        cout << "Hello World!\n";
        exit(0);
    }
} main_;

El {[4] } es prevenir la matriz de ser "llamado". Buena diversión :-)

 1
Author: JorenHeit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-29 12:49:18

Tal vez sea posible compilar un .sección de datos y llenarlo con código?

 0
Author: Bytemain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-13 14:15:40

Depende de lo que significan.

¿Querían decir:

Escribe un programa sin función main ().

Entonces, hablando en general no.
Pero hay maneras de hacer trampa.

  • Puede usar el preprocesador para ocultar main() a simple vista.
  • La mayoría del compilador le permite especificar el punto de entrada en su código.
    Por defecto es main (int,char*[])

O querían decir:

Escribir un programa que ejecute código sin usar main (para ejecutar el código).

Este es un truco relativamente simple. Todos los objetos en el espacio de nombres global ejecutan sus constructores antes de que se ingrese main() y la destrucción después de que main () salga. Por lo tanto, todo lo que necesita hacer es definir una clase con un constructor que ejecute el código que desea, y luego crear un objeto en el espacio de nombres global.

Nota: Al compilador se le permite optimizar estos objetos para la carga retardada (pero generalmente no lo hace), pero para estar seguro, simplemente coloque el global en el mismo archivo que la función principal (que puede estar vacía).

 0
Author: Martin York,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-13 16:19:33

La función principal es solo la etiqueta predeterminada para la dirección donde el programa comenzará la ejecución. Así que técnicamente sí es posible, pero tienes que establecer el nombre de la función que comenzará la ejecución en tu entorno.

 0
Author: Dawid Królak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-13 22:26:43

Escriba una clase e imprima su nombre en el constructor de esa clase y declare un OBJETO GLOBAL de esa clase. Así que el constructor de la clase se ejecuta antes de main. Así que puede dejar el principal vacío y aún imprimir su nombre.

class MyClass
{
   myClass()
   {
       cout << "printing my name..." <<endl;
   }
};

MyClass gObj; // this will trigger the constructor.

int main()
{
   // nothing here...
}
 0
Author: John Gummadi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-19 17:53:56

1) Usando una macro que define main

#include<stdio.h>
#define fun main
int fun(void)
{
printf("stackoverfow");
return 0;
}

Salida:

Stackoverflow

2) Usando el Operador de Pegado de Tokens La solución anterior tiene la palabra 'principal' en ella. Si no se nos permite incluso escribir main, podemos usar el operador de pegado de tokens (vea esto para más detalles)

#include<stdio.h>
#define fun m##a##i##n
int fun()
{
printf("stackoverflow");
return 0;
}
 0
Author: anshuman singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-09 15:26:52

Sí es posible escribir un programa sin main().

Pero usa main() indirectamente.

Siguiente programa le ayudará a entender..

#include<stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,r,e)
int begin()
{
printf(” you are inside main() which is hidden“);
}

El operador '# # ' se llama el operador de pegado o fusión de tokens. Es decir, podemos combinar dos o más caracteres con él.

En la segunda línea del programa-

Definir decodificar (s,t,u,m,p,e, d) m##s##u##t

¿Qué está haciendo el preprocesador aquí? La macro decodificación (s, t, u,m,p,e, d) está siendo expandido como "msut" (El operador # # fusiona m,s, u & t en msut). La lógica es cuando se pasa (s, t, u,m,p,e,d) como argumento se fusiona el 4º,1º, 3º y el 2º caracteres(tokens)

Ahora mira la tercera línea del programa –

Definir comenzar decodificar (a,n,i,m,a,r, e)

Aquí el preprocesador reemplaza la macro "begin" con la decodificación de expansión(a,n,i,m,a,r,e). De acuerdo con la definición de macro en la línea anterior, el argumento debe expandirse de modo que la 4a,1a, 3a y la 2a los caracteres deben combinarse. En el argumento (a,n,i,m,a,r,e) 4th,1st, 3rd & the 2nd characters are 'm','a','i' & 'n'.

Así que reemplazará begin por main() por el preprocesador antes de que el programa sea pasado al compilador. Eso es todo {

 0
Author: Saraswati Katakdhond,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-16 05:59:05

De acuerdo con los estándares, se requiere main() y el punto de partida de los entornos alojados. Es por eso que tienes que usar trucos para ocultar el aspecto obvio principal, como el truco publicado anteriormente.

#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)

int begin()
{
    printf(" hello ");
}

Aquí, main está escrito por los trucos de macro. Puede que no esté claro a la vez, pero finalmente conduce a main. Si esta es la respuesta válida para su pregunta, esto podría hacerse con mucha facilidad, así.

# include <stdio.h>
# define m main

int m()
{
    printf("Hell0");
}
 -4
Author: Bharat Kul Ratan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-13 21:59:07