web-dev-qa-db-fra.com

Est-il possible d'écrire un programme sans utiliser la fonction main ()?

Je n'arrête pas de poser cette question dans les interviews:

Écrire un programme sans utiliser la fonction main()?

Un de mes amis m'a montré du code à l'aide de macros, mais je ne pouvais pas le comprendre.

La question est donc:

Est-il vraiment possible d'écrire et de compiler un programme sans main()?

34
Expert Novice

Dans le C++ standard, une fonction main est requise, donc la question n'a pas de sens pour le C++ standard.

En dehors du C++ standard, vous pouvez par exemple écrire un programme spécifique à Windows et utiliser l'une des fonctions de démarrage personnalisées de Microsoft (wMain, winMain, wWinmain). Sous Windows, vous pouvez également écrire le programme en tant que DLL et utiliser rundll32 pour l'exécuter.

En dehors de cela, vous pouvez créer votre propre petite bibliothèque d'exécution. À une époque, c'était un sport commun.

Enfin, vous pouvez devenir intelligent et rétorquer que selon la règle ODR standard main n'est pas "utilisé", donc tout programme se qualifie. Bah! Bien qu'à moins que les intervieweurs aient un bon sens de l'humour inhabituel (et ils n'auraient pas posé la question s'ils l'avaient fait), ils ne penseront pas que c'est une bonne réponse.

18

Non, vous ne pouvez pas sauf si vous écrivez un programme dans un freestanding environment (Noyau de système d'exploitation intégré, etc.) où le point de départ n'a pas besoin d'être main(). Selon la norme C++, main() est le point de départ de tout programme dans un hosted environment.

Selon le:

Norme C++ 03 .6.1 Fonction principale

1 Un programme doit contenir une fonction globale appelée main, qui est le début désigné du programme. Il est défini par l'implémentation si un programme dans un environnement autonome est nécessaire pour définir une fonction principale. [Remarque: dans un environnement autonome, le démarrage et l'arrêt sont définis par l'implémentation; startup contient l'exécution de constructeurs pour des objets de portée d'espace de noms avec une durée de stockage statique; terminaison contient l'exécution de destructeurs pour les objets avec une durée de stockage statique.


Qu'est-ce que freestanding Environment & Qu'est-ce que Hosted Environment?
Il existe deux types d'implémentations conformes définies dans la norme C++; hosted et freestanding.

Une implémentation freestanding est une implémentation conçue pour des programmes exécutés sans le bénéfice d'un système d'exploitation.
Pour Ex: Un noyau de système d'exploitation ou un environnement intégré serait un environnement autonome.

Un programme utilisant les fonctionnalités d'un système d'exploitation serait normalement dans un hosted implementation.

De la norme C++ 03 Section 1.4/7:

Une implémentation autonome est une implémentation dans laquelle l'exécution peut avoir lieu sans l'avantage d'un système d'exploitation, et possède un ensemble de bibliothèques défini par l'implémentation qui inclut certaines bibliothèques prenant en charge les langues.

Plus loin,
Section: 17.4.1.3.2 Implémentations autonomes citations:

Une implémentation autonome possède un ensemble d'en-têtes défini par l'implémentation. Cet ensemble doit comprendre au moins les en-têtes suivants, comme indiqué dans le tableau:

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>
27
Alok Save

Exemple de programme sans fonction principale 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/

15
miku

main signifie un point d'entrée, un point à partir duquel votre code commencera à s'exécuter. bien que main ne soit pas la première fonction à exécuter. Il y a encore du code qui s'exécute avant main et prépare l'environnement pour faire fonctionner votre code. Ce code appelle ensuite main. Vous pouvez modifier le nom de la fonction main en recompilant le code du fichier de démarrage crt0.c et en changeant le nom de la fonction main. Ou vous pouvez effectuer les opérations suivantes:

#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;
}

Compilez le code avec:

gcc -o no_main no_main.c -nostartfiles

Le -nostartfiles n'inclura pas le fichier de démarrage par défaut. Vous pointez sur le fichier d'entrée principal avec le _start.

main n'est rien d'autre qu'un point d'entrée prédéfini pour le code utilisateur. Par conséquent, vous pouvez le nommer, mais à la fin de la journée, vous avez besoin d'un point d'entrée. En C/C++ et dans d'autres langues, le nom est sélectionné comme main si vous créez une autre langue ou piratez les sources de ces compilateurs de langue, vous pouvez changer le nom de main en pain mais cela causera de la douleur, car il violera les normes.

Mais la manipulation du nom de la fonction d'entrée est utile pour le code du noyau, la première fonction à exécuter dans le noyau ou le code écrit pour les systèmes intégrés.

11
phoxis

Ils peuvent faire référence à un programme écrit pour une implémentation autonome. La norme C++ définit deux sortes d'implémentations. L'une est une implémentation hébergée. Les programmes écrits pour ces implémentations doivent avoir une fonction main. Mais sinon, aucune fonction main n'est requise si l'implémentation autonome n'en requiert pas. Ceci est utile pour les noyaux de système d'exploitation ou les programmes système intégrés qui ne s'exécutent pas sous un système d'exploitation.

8

Oui

$ 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!

Le noyau qui exécute vraiment un exécutable ne sait rien des symboles internes, il transfère simplement vers un point d'entrée spécifié en binaire dans l'en-tête de l'image exécutable.

La raison pour laquelle vous avez besoin d'un module principal est que, normalement, votre "programme principal" n'est vraiment qu'un autre module. Le point d'entrée se trouve dans le code de démarrage fourni par la bibliothèque écrit dans une combinaison de C et d'assemblage et ce code de bibliothèque arrive juste à appeler main, vous devez donc normalement en fournir un. Mais exécutez l'éditeur de liens directement et vous ne le faites pas.

Pour inclure un module 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. Et je passe également au x86-64 ici.
6
DigitalRoss

Oui, il est possible de compiler avec notre principal, mais vous ne pouvez pas passer la phase de liaison.

 g++ -c noMain.cpp -o noMain.o
5
Mahesh

"Sans utiliser main" peut également signifier qu'aucune logique n'est autorisée dans main, mais que le main lui-même existe. Je peux imaginer que la question a été clarifiée, mais comme ce n'est pas le cas ici, c'est une autre réponse possible:

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

MainSub mainSub;

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

Ce qui se passera ici, c'est que les éléments du constructeur de MainSub s'exécuteront avant que le main inutilisable soit exécuté, et vous pouvez y placer la logique du programme. Bien sûr, cela nécessite C++, et non C (également pas clair de la question).

3
eran

Tant que vous utilisez g ++, vous pouvez modifier votre point d'entrée avec l'option de l'éditeur de liens -e, De sorte que le code suivant et la commande de compilation peuvent vous permettre de créer un programme sans fonction main():

#import <iostream>

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

J'ai donné le nom de fichier comme noname.cpp, Et l'option de compilation est:

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

Pour dire la vérité, je n'ai pas bien compris pourquoi le code peut fonctionner correctement. Je soupçonne que l'adresse de la variable globale mainClass est la même que celle du constructeur de la classe NoMain. Cependant, j'ai également plusieurs raisons que je pourrais dire que ma supposition peut ne pas corriger.

2
Joohae Kim

Je pense que la référence de macro était de renommer la fonction principale, ce qui suit n'est pas mon code, et le démontre. Le compilateur voit toujours une fonction principale, mais techniquement, il n'y a pas de principal du point de vue source. Je l'ai ici 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
eon

Sans tenir compte des normes de langage spécifiques, la plupart des chargeurs de liens fournissent des moyens de déclarer un nom de fonction (point d'entrée) qui doit être exécuté lorsque le binaire est chargé est chargé en mémoire.

Pour le langage c de la vieille école, la valeur par défaut était quelque chose comme 'start' ou '_start', défini dans ce qu'on appelle crt (c runtime?), Qui effectue plusieurs tâches ménagères nécessaires pour les fonctions standard c, telles que la préparation de la mémoire, l'initialisation de la variable statique zones, analyse de la ligne de commande en argc/argv, etc.

Vous pouvez éventuellement remplacer la fonction point d'entrée si vous prenez suffisamment soin de ne pas utiliser les fonctions standard qui nécessitent ces choses domestiques (par exemple malloc (), free (), printf (), toutes les définitions de classe ont un constructeur personnalisé, ...) restrictif mais pas impossible si vous utilisez des fonctions fournies par o/s, et non par le runtime c standard.

Par exemple, vous pouvez créer un helloworld simple en utilisant la fonction write () sur le descripteur 1.

1
shr

Lorsque le code C ou C++ s'exécute, il s'exécute à une adresse de début connue, le code initialise ici l'environnement d'exécution, initialise le pointeur de pile, effectue l'initialisation des données, appelle des constructeurs statiques, puis passe à main ().

Le code qui fait cela est lié à votre code au moment de la construction par l'éditeur de liens. Dans GCC, il est généralement dans crt0.s, avec un compilateur commercial, il est peu probable que ce code soit disponible.

En fin de compte, il doit commencer quelque part et main() n'est qu'un nom symbolique pour cet emplacement. Il est spécifié par la norme de langage afin que les développeurs sachent comment l'appeler, sinon le code ne serait pas portable d'une chaîne d'outils à l'autre.

Si vous écrivez du code pour un système 'bare-metal' sans OS ou au moins sans OS dans le sens d'un chargeur de processus (les systèmes embarqués incluent souvent un noyau RTOS qui est démarré après main ()), vous pouvez en théorie appeler le point d'entrée du code C comme vous le souhaitez, car vous avez généralement un contrôle complet sur le code de démarrage au moment de l'exécution Mais le faire serait stupide et quelque peu pervers.

Certains environnements RTOS tels que VxWorks, et la plupart des cadres d'application en général incluent main ()) ou son équivalent) dans leur propre code de bibliothèque afin qu'il s'exécute avant le code d'application utilisateur. Par exemple, les applications VxWorks démarrent à partir de usrAppInit () et les applications Win32 démarrent à partir de WinMain ().

1
Clifford

Je me rends compte que c'est une vieille question, mais je viens de découvrir cela et j'ai dû partager. Cela ne fonctionnera probablement pas avec tous les éditeurs de liens, mais il est au moins possible de tromper ld (j'utilise la version 2.24.51.20140918) en pensant qu'il y a une principale -fonction en faisant cela :

int main[] {};

ou même juste

int main;

Vous pouvez ensuite appliquer l'une des astuces susmentionnées pour laisser le programme exécuter du code, par exemple grâce à l'utilisation d'un constructeur:

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

La exit(0) sert à empêcher le tableau d'être "appelé". Bon amusement :-)

1
JorenHeit

oui il est possible d'écrire un programme sans main ().

Mais il utilise indirectement main ().

le programme suivant vous aidera à comprendre ..

#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“);
}

L’opérateur ‘##’ est appelé opérateur de collage de jetons ou de fusion de jetons. Autrement dit, nous pouvons fusionner deux ou plusieurs personnages avec elle.

Dans la 2ème ligne du programme-

définir le décodage (s, t, u, m, p, e, d) m ## s ## u ## t

Que fait le préprocesseur ici. Le macro décodage (s, t, u, m, p, e, d) est développé en tant que "msut" (L'opérateur ## fusionne m, s, u & t en msut). La logique est que lorsque vous passez (s, t, u, m, p, e, d) comme argument, il fusionne les 4e, 1er, 3e et 2e caractères (jetons)

Regardez maintenant la troisième ligne du programme -

définir le début du décodage (a, n, i, m, a, r, e)

Ici, le préprocesseur remplace la macro "commence" par le décodage d'extension (a, n, i, m, a, r, e). Selon la définition de la macro dans la ligne précédente, l'argument doit être développé de sorte que les 4e, 1er, 3e et 2e caractères doivent être fusionnés. Dans l'argument (a, n, i, m, a, r, e) les 4e, 1er, 3e et 2e caractères sont "m", "a", "i" et "n".

il remplacera donc begin by main () par le préprocesseur avant que le programme ne soit transmis au compilateur. C'est tout…

0

Écrivez une classe et imprimez votre nom dans le constructeur de cette classe et déclarez un OBJET GLOBAL de cette classe. Ainsi, le constructeur de la classe est exécuté avant main. Vous pouvez donc laisser le principal vide et toujours imprimer votre nom.

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

MyClass gObj; // this will trigger the constructor.

int main()
{
   // nothing here...
}
0
John Gummadi

La fonction main n'est que le libellé par défaut de l'adresse où le programme commencera son exécution. Donc, techniquement, c'est possible, mais vous devez définir le nom de la fonction qui commencera à s'exécuter dans votre environnement.

0
Dawid Królak

Cela dépend de ce qu'ils signifient.

Voulaient-ils:

Écrivez un programme sans fonction main ().

Alors en général non.
Mais il existe des moyens de tricher.

  • Vous pouvez utiliser le pré-processeur pour masquer main () à la vue de tous.
  • La plupart des compilateurs vous permettent de spécifier le point d'entrée dans votre code.
    Par défaut, il est principal (int, char * [])

Ou voulaient-ils dire:

Écrivez un programme qui exécute du code sans utiliser main (pour exécuter votre code).

Il s'agit d'une astuce relativement simple. Tous les objets de l'espace de noms global exécutent leurs constructeurs avant l'entrée de main () et la destruction après la sortie de main (). Ainsi, tout ce que vous devez faire est de définir une classe avec un constructeur qui exécute le code que vous souhaitez, puis créer un objet dans l'espace de noms global.

Remarque: Le compilateur est autorisé à optimiser ces objets pour un chargement retardé (mais ce n'est généralement pas le cas), mais pour être sûr, il suffit de placer le global dans le même fichier que la fonction principale (qui peut être vide).

0
Martin York

Peut-être qu'il est possible de compiler une section .data et de la remplir de code?

0
Gigamegs

1) Utilisation d'une macro qui définit le principal

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

Production:

stackoverflow

2) Utilisation de l’opérateur de collage de jetons La solution ci-dessus contient Word "main". Si nous ne sommes même pas autorisés à écrire main, nous pouvons utiliser l'opérateur de collage de jetons (voir ceci pour plus de détails)

#include<stdio.h>
#define fun m##a##i##n
int fun()
{
printf("stackoverflow");
return 0;
}
0
anshuman singh