J'ai appris de mon collègue que l'on peut écrire et exécuter un programme C sans écrire de main()
fonction. Cela peut être fait comme ceci:
my_main.c
/* Compile this with gcc -nostartfiles */
#include <stdlib.h>
void _start() {
int ret = my_main();
exit(ret);
}
int my_main() {
puts("This is a program without a main() function!");
return 0;
}
Compilez-le avec cette commande:
gcc -o my_main my_main.c –nostartfiles
Exécutez-le avec cette commande:
./my_main
Quand faudrait-il faire ce genre de chose? Y a-t-il un scénario du monde réel où cela serait utile?
_start()
et d'autres choses en dehors demain()
._start
, ou sur tout point d'entrée autre quemain
(sauf que le nom du point d'entrée est défini par l'implémentation pour les implémentations autonomes (intégrées)).Réponses:
Le symbole
_start
est le point d'entrée de votre programme. Autrement dit, l'adresse de ce symbole est l'adresse à laquelle saute au démarrage du programme. Normalement, la fonction avec le nom_start
est fournie par un fichier appelécrt0.o
qui contient le code de démarrage de l'environnement d'exécution C. Il configure certaines choses, remplit le tableau d'argumentsargv
, compte le nombre d'arguments présents, puis appellemain
. Après lesmain
retours,exit
est appelé.Si un programme ne souhaite pas utiliser l'environnement d'exécution C, il doit fournir son propre code pour
_start
. Par exemple, l'implémentation de référence du langage de programmation Go le fait car ils ont besoin d'un modèle de thread non standard qui nécessite un peu de magie avec la pile. Il est également utile de fournir le vôtre_start
lorsque vous voulez écrire de très petits programmes ou des programmes qui font des choses non conventionnelles.la source
_start
vient aussi du fichier objetcrt0.o
._start
; en fait, il ne spécifie pas du tout ce qui se passe avant d'main
être appelé, il spécifie simplement quelles conditions doivent être remplies lors de l'main
appel. C'est plus une convention pour le point d'entrée_start
qui remonte à l'ancien temps.Alors que
main
c'est le point d'entrée de votre programme du point de vue des programmeurs,_start
c'est le point d'entrée habituel du point de vue du système d'exploitation (la première instruction exécutée après le démarrage de votre programme à partir du système d'exploitation)Dans un programme typique en C et en particulier en C ++, beaucoup de travail a été effectué avant que l'exécution entre en main.
Surtout des trucs comme l'initialisation des variables globales.Ici vous pouvez trouver une bonne explication de tout ce qui se passe entre_start()
etmain()
aussi après principal est sorti à nouveau (voir commentaire ci - dessous).Le code nécessaire pour cela est généralement fourni par les rédacteurs du compilateur dans un fichier de démarrage, mais avec le drapeau,
–nostartfiles
vous dites essentiellement au compilateur: "Ne me donnez pas le fichier de démarrage standard, donnez-moi un contrôle total sur ce qui se passe directement depuis le début".Ceci est parfois nécessaire et souvent utilisé sur les systèmes embarqués. Par exemple, si vous n'avez pas d'OS et que vous devez activer manuellement certaines parties de votre système de mémoire (par exemple les caches) avant l'initialisation de vos objets globaux.
la source
_start()
(ou en fait une autre fonction appelée par lui) et dans de nombreux programmes Bare Metal, vous copiez explicitement toutes les données globales de la mémoire flash vers la RAM d'abord, ce qui se produit également dans_start()
, mais cette question ne concernait ni le c ++ ni le code bare-metal._start
, la bibliothèque C ne sera pas initialisée à moins que vous ne preniez des mesures spéciales pour le faire vous-même - il peut être dangereux d'utiliser une fonction non sécurisée pour le signal asynchrone d'un tel programme. (Il n'y a aucune garantie officielle que toute fonction de bibliothèque fonctionne, mais les fonctions de sécurité pour les signaux asynchrones ne peut pas se référer à des données globales du tout, donc ils auraient à sortir de leur chemin à un mauvais fonctionnement.)malloc
ne sont pas initialisées.errno
(par exempleread
, elleswrite
sont sûres pour le signal asynchrone et peuvent être définieserrno
) et cela pourrait éventuellement poser un problème en fonction du moment exact où l'errno
emplacement par thread est alloué .Voici un bon aperçu de ce qui se passe lors du démarrage du programme avant
main
. En particulier, cela montre que__start
c'est le point d'entrée réel de votre programme du point de vue du système d'exploitation.C'est la toute première adresse à partir de laquelle le pointeur d'instruction commencera à compter dans votre programme.
Le code là-bas appelle certaines routines de bibliothèque d'exécution C juste pour faire un peu de ménage, puis appelez votre
main
, puis arrêtez les choses et appelezexit
avec le code de sortiemain
renvoyé.Une image vaut mieux que mille mots:
PS: cette réponse est transplantée à partir d' une autre question que SO a utilement clôturée en double de celle-ci.
la source
Lorsque vous voulez votre propre code de démarrage pour votre programme.
main
n'est pas la première entrée pour un programme C,_start
est la première entrée derrière le rideau.Exemple sous Linux:
Si vous voulez dire, implémentez le nôtre
_start
:Oui, dans la plupart des logiciels embarqués commerciaux avec lesquels j'ai travaillé, nous devons implémenter les nôtres
_start
en fonction de nos besoins spécifiques en matière de mémoire et de performances.Si vous voulez dire, supprimez la
main
fonction et changez-la en autre chose:Non, je ne vois aucun avantage à faire cela.
la source