Éviter le principal (point d'entrée) dans un programme C

Est-il possible d'éviter le point d'entrée (principale) dans un programme C. Dans le code ci-dessous, est-il possible d'invoquer l'appel func() sans appeler via main() dans le programme ci-dessous ? Si Oui, comment le faire et quand est-il nécessaire et pourquoi une telle disposition ?

int func(void)
{
     printf("This is func n");
     return 0;
}

int main(void)
{
     printf("This is main n");
     return 0;
}
16
demandé sur Karthik Balaguru 2010-07-31 21:47:49

7 réponses

si vous utilisez gcc, j'ai trouvé un thread qui dit que vous pouvez utiliser le -e paramètre de ligne de commande pour spécifier un point d'entrée différent; ainsi vous pouvez utiliser func comme votre point d'entrée, ce qui laisserait main inutilisés.

notez que cela ne vous permet pas d'appeler une autre routine au lieu de main . Au lieu de cela , il vous permet d'appeler une autre routine au lieu de _start , qui est la routine de démarrage de libc -- il fait certains setup puis it appelle main . Donc si vous faites cela, vous perdrez du code d'initialisation qui est intégré dans votre bibliothèque runtime, qui pourrait inclure des choses comme l'analyse des arguments en ligne de commande. Lire sur ce paramètre avant de l'utiliser.

si vous utilisez un autre compilateur, il peut y avoir ou non un paramètre pour cela.

22
répondu Joe White 2010-07-31 18:07:30

lors de la construction du firmware des systèmes embarqués à exécuter directement à partir de ROM, je vais souvent éviter de nommer le point d'entrée main() pour souligner à un examinateur de code la nature spéciale du code. Dans ces cas, je fournis une version personnalisée du module C runtime startup, il est donc facile de remplacer son appel à main() par un autre nom tel que BootLoader() .

je (ou mon fournisseur) ai presque toujours à personnaliser le démarrage de L'exécution C dans ces systèmes parce qu'il n'est pas inhabituel que la mémoire vive nécessite un code d'initialisation pour qu'elle commence à fonctionner correctement. Par exemple, les puces DRAM typiques nécessitent une configuration surprenante de leur matériel de contrôle, et nécessitent souvent un délai important (des milliers de cycles d'horloge de bus) avant qu'elles ne soient utiles. Jusqu'à ce que ce soit terminé, il se peut qu'il n'y ait même pas d'endroit où placer la pile d'appels de sorte que le code de démarrage ne puisse pas appeler de fonctions. Même si les dispositifs de RAM sont opérationnels à la puissance, il y a presque toujours une certaine quantité de matériel chip select ou un ou deux FPGA qui nécessitent une initialisation avant qu'il ne soit sûr de laisser l'exécution C démarrer son initialisation.

quand un programme écrit en C charge et démarre, un composant est responsable de rendre l'environnement dans lequel main() est appelé exister. Dans les environnements Unix, linux, Windows et autres environnements interactifs, une grande partie de cet effort est une conséquence naturelle du composant OS qui charge le programme. Cependant, même dans ces environnements, il y a une certaine quantité de travail d'initialisation à faire avant que main() puisse être appelé. Si le code est vraiment C++, alors il peut y avoir une quantité importante de travail qui inclut l'appel des constructeurs pour toutes les instances d'objet global.

les détails de tout cela sont gérés par le linker et ses fichiers de configuration et de contrôle. Le linker ld (1) a un fichier de contrôle très élaboré qui lui dit exactement quels segments inclure dans le les résultats, à quelles adresses et dans quel ordre. Trouver le fichier de contrôle de linker que vous utilisez implicitement pour votre chaîne d'outils et le lire peut être instructif, tout comme le manuel de référence pour le linker lui-même et la norme ABI que vos exécutables doivent suivre pour fonctionner.

Edit: À plus directement répondre à la question posée dans plus d'un contexte commun: "Pouvez-vous appeler foo au lieu de principal?"La réponse est" peut-être, mais seulement en étant délicat".

sous Windows, un exécutable et une DLL sont presque le même format de fichier. Il est possible d'écrire un programme qui charge une DLL arbitraire nommée à l'exécution, et y localise une fonction arbitraire, et l'appelle. Un tel programme est livré dans le cadre d'une distribution Windows standard: rundll32.exe .

Depuis un .Le fichier EXE peut être chargé et Inspecté par les mêmes API que celles qui gèrent .Fichiers DLL, en principe si le. EXE a une section EXPORTS qui nomme la fonction foo , alors un utilitaire similaire pourrait être écrit pour le charger et l'invoquer. Vous n'avez pas besoin de faire quelque chose de spécial avec main , bien sûr, puisque ce sera le point d'entrée naturel. Bien sûr, L'exécution C initialisée dans votre utilitaire pourrait ne pas être la même exécution C liée à votre exécutable. (Google pour" dll enfer " pour Indice. Dans ce cas, votre utilité pourrait avoir besoin d'être plus intelligent. Par exemple, il pourrait agir comme un débogueur, chargez L'EXE avec un point de rupture à main , courez jusqu'à ce point de rupture, puis changez le PC en point ou en foo et continuez à partir de là.

une sorte de ruse similaire pourrait être possible sur Linux depuis .donc les fichiers sont également similaires à certains égards aux véritables exécutables. Il est certain que l'approche consistant à agir comme un débogueur pourrait fonctionner.

16
répondu RBerteig 2010-07-31 22:17:00

Une règle de pouce serait que le chargeur fourni par le système toujours exécuter en main. Avec suffisamment d'autorité et de compétence vous pourriez théoriquement écrire un chargeur différent qui a fait quelque chose d'autre.

5
répondu dmckee 2010-07-31 17:53:20

renommer main pour être func et func pour être main et appeler func de son nom.

si vous avez accès à la source, vous pouvez le faire et c'est facile.

4
répondu 2011-01-24 15:59:38

si vous utilisez un compilateur source ouvert tel que GCC ou un compilateur ciblé sur les systèmes embarqués, vous pouvez modifier le démarrage de L'exécution C (CRT) pour commencer à n'importe quel point d'entrée dont vous avez besoin. Dans GCC, CE code est dans crt0.S. En général, ce code est partiellement ou entièrement en assembleur, pour la plupart des compilateurs de systèmes embarqués exemple ou le code de démarrage par défaut sera fourni.

cependant une approche plus simple est de simplement "cacher" main() dans une bibliothèque statique que vous liez à votre code. Si cette implémentation de main () ressemble à:

int main(void)
{
    func() ;
}

alors il regardera à toutes fins utiles comme si le point d'entrée de l'utilisateur est func(). C'est le nombre de cadres d'application avec des points d'entrée autres que main() qui fonctionnent. Notez que parce qu'il est dans une bibliothèque statique, toute définition de main() par l'utilisateur supplantera cette version de la bibliothèque statique.

3
répondu Clifford 2010-07-31 18:08:20

la solution dépend du compilateur et du linker que vous utilisez. Toujours est-il que et non main est le point d'entrée réel de la demande. Le point d'entrée réel fait quelques initialisations et appelle par exemple main . Si vous écrivez des programmes pour Windows en utilisant Visual Studio, vous pouvez utiliser / le commutateur D'entrée du linker pour écraser le point d'entrée par défaut mainCRTStartup et appeler func() au lieu de main() :

#ifdef NDEBUG
void mainCRTStartup()
{
    ExitProcess (func());
}
#endif

Si est une pratique standard si vous écrivez la plus petite application. Dans le cas où vous recevrez des restrictions dans l'utilisation des fonctions C-Runtime. Vous devriez utiliser la fonction API de Windows au lieu de la fonction C-Runtime. Par exemple, au lieu de printf("This is func \n") vous devez utiliser OutputString(TEXT("This is func \n"))OutputString sont mis en œuvre uniquement en ce qui concerne WriteFile ou WriteConsole :

static HANDLE g_hStdOutput = INVALID_HANDLE_VALUE;
static BOOL g_bConsoleOutput = TRUE;

BOOL InitializeStdOutput()
{
    g_hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
    if (g_hStdOutput == INVALID_HANDLE_VALUE)
        return FALSE;

    g_bConsoleOutput = (GetFileType (g_hStdOutput) & ~FILE_TYPE_REMOTE) != FILE_TYPE_DISK;
#ifdef UNICODE
    if (!g_bConsoleOutput && GetFileSize (g_hStdOutput, NULL) == 0) {
        DWORD n;

        WriteFile (g_hStdOutput, "\xFF\xFE", 2, &n, NULL);
    }
#endif

    return TRUE;
}

void Output (LPCTSTR pszString, UINT uStringLength)
{
    DWORD n;

    if (g_bConsoleOutput) {
#ifdef UNICODE
        WriteConsole (g_hStdOutput, pszString, uStringLength, &n, NULL);
#else
        CHAR szOemString[MAX_PATH];
        CharToOem (pszString, szOemString);
        WriteConsole (g_hStdOutput, szOemString, uStringLength, &n, NULL);
#endif
    }
    else
#ifdef UNICODE
        WriteFile (g_hStdOutput, pszString, uStringLength * sizeof (TCHAR), &n, NULL);
#else
    {
        //PSTR pszOemString = _alloca ((uStringLength + sizeof(DWORD)));
        CHAR szOemString[MAX_PATH];
        CharToOem (pszString, szOemString);
        WriteFile (g_hStdOutput, szOemString, uStringLength, &n, NULL);
    }
#endif
}

void OutputString (LPCTSTR pszString)
{
    Output (pszString, lstrlen (pszString));
}
1
répondu Oleg 2010-08-01 07:15:53

cela dépend vraiment comment vous invoquez le binaire, et va être raisonnablement plate-forme et l'environnement spécifique. La réponse la plus évidente est simplement de renommer le symbole "main" en quelque chose d'autre et d'appeler "func" "main", mais je soupçonne que ce n'est pas ce que vous essayez de faire.

0
répondu Gian 2010-07-31 17:50:06