Comment exécuter du code avant d'entrer dans la routine principale () dans VC?

StackOverflow https://stackoverflow.com/questions/728939

  •  05-09-2019
  •  | 
  •  

Question

Je suis en train de lire le code source CRT de Microsoft, et je peux venir avec le code suivant, où la fonction __initstdio1 sera exécutée avant la routine principale ().

La question est, comment exécuter un code avant d'entrer dans la routine principale () dans VC (non code VC ++)?

#include <stdio.h>

#pragma section(".CRT$XIC",long,read)

int __cdecl __initstdio1(void);

#define _CRTALLOC(x) __declspec(allocate(x))

_CRTALLOC(".CRT$XIC") static pinit = __initstdio1;

int z = 1;

int __cdecl __initstdio1(void) {
    z = 10;
    return 0;
}

int main(void) {
    printf("Some code before main!\n");
    printf("z = %d\n", z);
    printf("End!\n");
    return 0;
}

La sortie sera:

Some code before main!
z = 10
End!

Cependant, je ne suis pas en mesure de comprendre le code.

Je l'ai fait un peu google sur .CRT $ XIC mais pas de chance se trouve. certains experts peut-il expliquer segment de code ci-dessus pour moi, en particulier les points suivants:

  1. Qu'est-ce que cette ligne _CRTALLOC(".CRT$XIC") static pinit = __initstdio1; signifie? Quelle est la signification de la pinit variable?
  2. Lors de la compilation du compilateur (cl.exe) lance un avertissement en disant comme ci-dessous:

Microsoft (R) 32-bit C / C ++ Optimisation de la version du compilateur 15.00.30729.01 pour 80x86 Copyright (C) Microsoft Corporation. Tous droits réservés.

stdmacro.c
stdmacro.c(9) : warning C4047: 'initializing' : 'int' differs in levels of indirection from 'int (__
cdecl *)(void)'
Microsoft (R) Incremental Linker Version 9.00.30729.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:stdmacro.exe
stdmacro.obj

Quelle est l'action corrective doit être fait pour supprimer le message d'avertissement?

Merci d'avance.

Ajout:

Je l'ai modifié le code et donner le type à pinit comme _PIFV. Maintenant, le message d'avertissement disparaît.

Le nouveau code est le suivant:

#include <stdio.h>

#pragma section(".CRT$XIC1",long,read)

int __cdecl __initstdio1(void);

typedef int  (__cdecl *_PIFV)(void);

#define _CRTALLOC(x) __declspec(allocate(x))

_CRTALLOC(".CRT$XIC1") static _PIFV pinit1 = __initstdio1;

int z = 1;

int __cdecl __initstdio1(void) {
    z = 100;

    return 0;
}

int main(void) {
    printf("Some code before main!\n");
    printf("z = %d\n", z);
    printf("End!\n");
    return 0;
}
Était-ce utile?

La solution

Il y a quelques informations (recherche CRT). L'importance de pinit variable est pas, il est juste un morceau de données placées dans le fichier exécutable, où le temps d'exécution peut le trouver. Cependant, je vous conseille de donner un type, comme ceci:

_CRTALLOC(".CRT$XIC") static void (*pinit)()=...

L'avertissement de linker probablement vous avertit que vous avez une fonction qui a int type de retour, mais ne retourne rien (vous auriez sans doute mieux changer le type de retour à void).

Autres conseils

Une façon simple de le faire.

#include <iostream>

int before_main()
{
    std::cout << "before main" << std::endl;
    return 0;
}

static int n = before_main();

void main(int argc, char* argv[])
{
    std::cout << "in main" << std::endl;
}

est ce que _CRTALLOC est défini comme suit:

extern _CRTALLOC(".CRT$XIA") _PVFV __xi_a[];
extern _CRTALLOC(".CRT$XIZ") _PVFV __xi_z[];// C initializers
extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[];
extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[];// C++ initializers

Il est une table de choses à pré-initialisation, duquel est placé un pointeur à votre fonction __initstdio1.

Cette page décrit l'initialisation du CRT:

http://msdn.microsoft.com/en-us/library /bb918180.aspx

En C ++ au moins, vous n'avez pas besoin de toute cette mise en œuvre des choses spécifiques:

#include <iostream>

struct A {
   A() { std::cout << "before main" << std::endl; }
};

A a;

int main() {
   std::cout << "in main" << std::endl;
}

J'ai écrit primé article à ce sujet sur CodeGuru il y a un certain temps.

Même en C, il est nécessaire d'un code à exécuter avant main() est entré, si seulement pour transformer la ligne de commande dans la convention d'appel C. Dans la pratique, la bibliothèque standard nécessite une initialisation, et les besoins exacts peuvent varier d'une compilation à compiler.

Le véritable point d'entrée de programme est réglé au moment de la liaison, et est généralement dans un module nommé quelque chose comme crt0 pour des raisons historiques. Comme vous l'avez trouvé, la source de ce module est disponible dans les sources crt.

Pour soutenir initialisations qui sont découverts au moment de la liaison, un segment spécial est utilisé. Sa structure est une liste de pointeurs de fonction de signature fixe, qui itérer au début de crt0 et chaque fonction appelée. Ce même tableau (ou un très semblable) de pointeurs de fonction est utilisé dans le lien d'un C pour contenir des pointeurs aux constructeurs d'objets globaux.

La matrice est remplie par l'agent de liaison en permettant à chaque module lié à inclure des données qu'il contient, qui sont tous concaténés pour former le segment dans le fichier exécutable fini. La seule signification à la pinit variable est qu'elle est déclarée (par la macro _CRTALLOC()) être situé dans ce segment, et est initialisé à l'adresse d'une fonction à appeler lors du démarrage C.

De toute évidence, les détails de cette sont extrêmement spécifiques à la plate-forme. Pour la programmation générale, vous êtes probablement mieux servi en enveloppant votre initialisation et votre principal courant dans une nouvelle main():

int main(int argc, char **argv) {
    early_init();
    init_that_modifies_argv(&argc, &argv);
    // other pre-main initializations...
    return real_main(argc,argv);
}

Pour des raisons spéciales, la modification du module crt0 lui-même ou en faisant des tours spécifiques au compilateur pour obtenir des fonctions d'initialisation précoce supplémentaires appelées peut être la meilleure réponse. Par exemple, lors de la construction des systèmes embarqués qui fonctionnent à partir de ROM sans chargeur de système d'exploitation, il est courant de devoir personnaliser le comportement du module crt0 afin d'avoir une pile à tout ce sur quoi pour pousser les paramètres à main(). Dans ce cas, il peut y avoir pas de meilleure solution que de modifier crt0 pour initialiser le matériel de mémoire pour répondre à vos besoins.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top