2009-04-08 6 views
7

Je lis le code source CRT de Microsoft, et je peux trouver le code suivant, où la fonction __initstdio1 sera exécutée avant la routine main().Comment exécuter du code avant d'entrer la routine main() dans VC?

La question est, comment exécuter du code avant d'entrer la routine main() dans VC (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.

J'ai fait du google sur .CRT $ XIC mais pas de chance. Est-ce qu'un expert peut m'expliquer le segment de code ci-dessus, en particulier les suivants:

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

Microsoft (R) 32 bits C/C++ Optimisation du compilateur Version 15.00.30729.01 pour 80x86 Copyright (C) Microsoft Corporation . Tous les droits sont 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 à effectuer pour supprimer le message d'avertissement?

Merci d'avance.


Ajouté:

J'ai modifié le code et donner le type à pinit comme _PIFV. Maintenant, le message d'avertissement est parti.

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

Répondre

1

Il y a quelques informations here (recherche CRT). La signification de la variable pinit est none, c'est juste un morceau de données placé dans l'exécutable, où le runtime peut le trouver.Cependant, je vous conseille de donner un type, comme celui-ci:

_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 (probablement vous aviez mieux changer le type de retour à void).

3

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; 
} 
+0

C'est une bonne idée. Mais votre code peut passer la compilation en C++ seulement; pas en C. – yinyueyouge

+0

La question est étiquetée C++ – mouviciel

4

C'est ce que _CRTALLOC est défini comme:

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 

C'est un tableau de choses à pré-initialiser, dont un pointeur vers votre fonction __initstdio1 est placé.

Cette page décrit Initialisation CRT:

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

5

Un moyen simple de faire cela.

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

Des problèmes peuvent survenir lorsque before_main() dépend de données créées dans une autre fonction qui s'exécute avant main(). L'ordre dans lequel ces fonctions s'exécutent n'est pas défini. Ceci, en particulier, signifie que les données globales before_main() créées peuvent être remplacées par des routines d'exécution qui initialisent les données globales à 0s – dmityugov

+6

Ceci est C++, pas C. – RBerteig

+1

remplacez std :: count << avec puts() et ce sera C – dmityugov

1

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

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

Pour prendre en charge les initialisations détectées au moment de la liaison, un segment spécial est utilisé. Sa structure est une liste de pointeurs de fonction de signature fixe, qui sera itérée au début de crt0 et chaque fonction appelée. Ce même tableau (ou un très similaire) de pointeurs de fonction est utilisé dans un lien C++ pour contenir des pointeurs vers des constructeurs d'objets globaux.

Le tableau est rempli par l'éditeur de liens en permettant à chaque module lié d'inclure des données, qui sont toutes concaténées ensemble pour former le segment dans l'exécutable fini. La seule signification à la variable pinit est qu'il est déclaré (par la macro _CRTALLOC()) d'être situé dans ce segment, et est initialisé à l'adresse d'une fonction à appeler pendant le démarrage C.

De toute évidence, les détails 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 fins spéciales, la modification du module crt0 lui-même ou faire des tours spécifiques au compilateur pour obtenir des fonctions d'initialisation rapide supplémentaires appelé peut être la meilleure réponse. Par exemple, lors de la construction de systèmes embarqués qui s'exécutent à 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 sur laquelle appuyer les paramètres main(). Dans ce cas, il n'y a pas de meilleure solution que de modifier crt0 pour initialiser le matériel de mémoire en fonction de vos besoins.

2

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

Questions connexes