web-dev-qa-db-fra.com

dans la fonction principale c ++ est le point d’entrée pour programmer comment je peux le changer pour une autre fonction?

On m'a posé une question d'entretien pour changer le point d'entrée d'un programme C ou C++ de main() à n'importe quelle autre fonction. Comment est-ce possible?

27
Badr

En C standard (et aussi, je crois, en C++), vous ne pouvez pas, du moins pas pour un environnement hébergé (mais voir ci-dessous). La norme spécifie que le point de départ du code C est main. La norme (c99) ne laisse pas beaucoup de place à l’argumentation:

5.1.2.2.1 Démarrage du programme: (1) La fonction appelée au démarrage du programme s'appelle main.

C'est tout. Ensuite, les paramètres et les valeurs retournées sont abordés, mais il n’ya vraiment aucune marge de manœuvre pour changer le nom.

C'est pour un environnement hébergé. La norme permet également un environnement autonome (c’est-à-dire qu’il n’existe pas de système d’exploitation, par exemple pour les systèmes intégrés). Pour un environnement autonome:

Dans un environnement autonome (dans lequel l'exécution du programme C peut avoir lieu sans aucun avantage d'un système d'exploitation), le nom et le type de la fonction appelée au démarrage du programme sont définis par l'implémentation. Toutes les installations de bibliothèque disponibles pour un programme autonome, autres que l'ensemble minimal requis par l'article 4, sont définies par la mise en œuvre.

Vous pouvez utiliser "ruse" en C implémentations pour pouvoir lui donner l’impression que main n’est pas le point d’entrée. C’est en fait ce que les premiers utilisateurs de Windows ont fait pour marquer WinMain comme point de départ.


Premier moyen: un éditeur de liens peut inclure du code de démarrage pré-principal dans un fichier tel que start.o et c'est ce morceau de code qui s'exécute pour configurer l'environnement C puis appelle main. Rien ne vous empêche de remplacer cela par quelque chose qui appelle plutôt bob.


Deuxième solution: certains éditeurs de liens proposent cette option avec un commutateur de ligne de commande, ce qui vous permet de la modifier sans recompiler le code de démarrage.


Troisième manière: vous pouvez créer un lien avec ce morceau de code:

int main (int c, char *v[]) { return bob (c, v); }

et ensuite, votre point d’entrée pour le code votre est apparemment bob plutôt que main.


Cependant, tout cela, bien que pouvant présenter un intérêt académique, ne change pas le fait que je ne peux pas penser à une seule situation solitaire au cours de plusieurs décennies de code de coupe, où cela serait nécessaire ou souhaitable.

Je demanderais à l'intervieweur: pourquoi vous voudriez faire cela?

43
paxdiablo

Le point d’entrée est en fait la fonction _start (implémentée dans crt1.o ).

La fonction _start prépare les arguments de ligne de commande, puis appelle main(int argc,char* argv[], char* env[]), Vous pouvez modifier le point d'entrée de _start à mystart en définissant un paramètre de l'éditeur de liens:

g++ file.o -Wl,-emystart -o runme

Bien sûr, ceci remplace le point d’entrée _start et vous n’obtiendrez pas les arguments de la ligne de commande: 

void mystart(){

}

Notez que les variables globales/statiques comportant des constructeurs ou des destructeurs doivent être initialisées au début de l'application et détruites à la fin. Gardez cela à l’esprit si vous envisagez de contourner le point d’entrée par défaut qui le fait automatiquement.

9
Lefteris E

A partir de la documentation standard C++ 3.6.1 Fonction principale,

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 requis pour définir une fonction principale.

Donc, il dépend de votre compilateur/éditeur de liens ...

9
liaK

Si vous êtes sur VS2010, ceci pourrait vous donner une idée

Comme il est facile à comprendre, cela n’est pas imposé par le standard C++ et relève du domaine du «comportement spécifique à l’implémentation».

7
Chubsdad

Ceci est hautement spéculatif, mais vous pourriez avoir un initialiseur statique au lieu de main:

comprendre 

int mymain()
{
    std::cout << "mymain";
    exit(0);
}

static int sRetVal = mymain();

int main()
{
    std::cout << "never get here";
}

Vous pourriez même le rendre 'semblable à Java', en plaçant le contenu dans un constructeur:

#include <iostream>

class MyApplication
{
public:
    MyApplication()
    {
        std::cout << "mymain";
        exit(0);
    }
};

static MyApplication sMyApplication;

int main()
{
    std::cout << "never get here";
}

À présent. L'intervieweur aurait peut-être pensé à cela, mais personnellement, je ne l'aurais jamais utilisé. Les raisons sont:

  • C'est non conventionnel. Les gens ne comprendront pas, il n'est pas inutile de trouver le point d'entrée.
  • L'ordre d'initialisation statique est non déterministe. Mettez dans une autre variable statique et vous ne serez jamais maintenant si elle est initialisée.

Cela dit, je l’ai vu être utilisé en production au lieu de init() pour les initialiseurs de bibliothèque. La mise en garde est, sous Windows, (par expérience) que votre statique dans une DLL peut être ou ne pas être initialisée en fonction de l'utilisation.

3
lorro

Avec gcc, déclarez la fonction avec attribut ((constructeur)) et gcc exécutera cette fonction avant tout autre code, y compris principal. 

3
Galaxy

Modifiez l'objet crt qui appelle réellement la fonction main() ou fournissez le vôtre (n'oubliez pas de désactiver la liaison avec l'objet normal).

3

Pour les systèmes basés sur Solaris, j'ai trouvé this . Vous pouvez utiliser la section .init pour toutes les plateformes, je suppose:

   pragma init (function [, function]...)

La source:

Ce pragma provoque l'appel de chaque fonction listée pendant l'initialisation (avant main) ou pendant le chargement de module partagé, en ajoutant un appel à la section .init 

2
Amir Zadeh

C'est très simple:

Comme vous devez le savoir lorsque vous utilisez des constantes dans c, le compilateur exécute une sorte de "macro" en modifiant le nom de la constante pour la valeur correspondante.

incluez simplement un argument #define au début de votre code avec le nom de la fonction de démarrage suivie du nom main:

Exemple:

#define my_start-up_function (main)
2
Paulo Rossi

Je pense qu'il est facile de supprimer le symbole principal () non désiré de l'objet avant la liaison. 

Malheureusement, l'option de point d'entrée pour g ++ ne fonctionne pas pour moi (le binaire se bloque avant d'entrer dans le point d'entrée). Je supprime donc le point d’entrée indésirable du fichier objet. 

Supposons que nous ayons deux sources contenant une fonction de point d’entrée.

  1. target.c contient le main () que nous ne voulons pas.
  2. notre_code.c contient le testmain () que nous voulons être le point d'entrée.

Après la compilation (option g ++ -c), nous pouvons obtenir les fichiers d’objet suivants.

  1. target.o, qui contient le main () que nous ne voulons pas.
  2. our_code.o qui contient le testmain (), nous voulons être le point d'entrée.

Nous pouvons donc utiliser objcopy pour supprimer la fonction main () indésirable.

objcopy --strip-symbol = cible principale.o

Nous pouvons aussi redéfinir testmain () en main () en utilisant objcopy.

objcopy --redefine-sym testmain = main notre_code.o

Et ensuite, nous pouvons relier les deux en binaire.

g ++ target.o notre_code.o -o notre_binaire.bin

Cela fonctionne pour moi. Maintenant, lorsque nous exécutons our_binary.bin, le point d’entrée est le symbole our_code.o:main() qui fait référence à la fonction our_code.c::testmain().

2
shuva

Sur Windows, il existe un autre moyen (plutôt peu orthodoxe) de changer le point d'entrée d'un programme: TLS. Voir ceci pour plus d'explications: http://isc.sans.edu/diary.html?storyid=6655

1
ruslik

Changer une valeur dans les paramètres de l'éditeur de liens remplacera le point d'entrée. En d'autres termes, les applications MFC utilisent la valeur 'Windows (/ SUBSYSTEM: WINDOWS)' pour modifier le point d'entrée de main () à CWinApp :: WinMain ().

Right clicking on solution > Properties > Linker > System > Subsystem > Windows (/SUBSYSTEM:WINDOWS)

...

Avantage très pratique de modifier le point d’entrée:

MFC est un framework dont nous tirons parti pour écrire des applications Windows en C++. Je sais que c'est ancien, mais mon entreprise en maintient un pour des raisons d'héritage! Vous ne trouverez pas main () dans le code MFC. MSDN indique que le point d'entrée est WinMain () à la place. Ainsi, vous pouvez remplacer le WinMain () de votre objet CWinApp de base. Ou bien, la plupart des gens substituent CWinApp :: InitInstance () parce que la base WinMain () l’appellera.

Avertissement: J'utilise des parenthèses vides pour désigner une méthode, sans me préoccuper du nombre d'arguments. 

0
John Doe

Oui, .__ Nous pouvons changer le nom de la fonction principale en un autre nom, par exemple. Démarrer, bob, rem etc.

Comment le compilateur sait-il qu'il doit rechercher le main () dans tout le code?

Rien n’est automatique en programmation ... Quelqu'un a déjà fait quelques efforts pour que cela paraisse automatique.

il a donc été défini dans le fichier de démarrage que le compilateur devrait rechercher main ().

nous pouvons changer le nom principal à quelque chose d'autre, par exemple. Bob, puis le compilateur ne recherchera que Bob ().

0
Gaurav Pal