2009-04-08 5 views
7

Sto leggendo il codice sorgente CRT di Microsoft e posso trovare il seguente codice, in cui la funzione __initstdio1 verrà eseguita prima della routine main().Come eseguire un codice prima di accedere alla routine main() in VC?

La domanda è, come eseguire un codice prima di immettere la routine main() in VC (non il codice 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; 
} 

L'uscita sarà:

Some code before main! 
z = 10 
End! 

Tuttavia, io non sono in grado di capire il codice.

Ho fatto qualche google su .CRT $ XIC ma non ho trovato fortuna. Qualche esperto può spiegarmi sopra il segmento di codice, in particolare i seguenti:

  1. Che cosa significa questa riga _CRTALLOC(".CRT$XIC") static pinit = __initstdio1;? Qual è il significato della variabile pinit?
  2. Durante la compilazione il compilatore (cl.exe) lancia un avvertimento dicendo come di seguito:

Microsoft (R) a 32 bit C/C++ compilatore di ottimizzazione Versione 15.00.30729.01 per 80x86 Copyright (C) Microsoft Corporation . Tutti i diritti riservati.

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 

Qual è l'azione correttiva da eseguire per rimuovere il messaggio di avviso?

Grazie in anticipo.


Aggiunto:

Ho modificato il codice e dare il tipo di Pinit come _PIFV. Ora il messaggio di avviso è sparito.

Il nuovo codice è il seguente:

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

risposta

1

C'è qualche informazione here (ricerca di CRT). Il significato della variabile pinit non è nessuno, è solo un pezzo di dati inserito nell'eseguibile, dove il runtime può trovarlo.Tuttavia, vorrei consigliare di fare un tipo, come questo:

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

L'avvertimento linker probabilmente appena si avverte di avere una funzione che ha int tipo di ritorno, ma non restituisce nulla (si sarebbe probabilmente meglio cambiare il tipo di ritorno a void).

3

In C++, almeno, non occorre tutta quella roba specifica implementazione:

#include <iostream> 

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

A a; 

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

Questa è una buona idea. Ma il tuo codice può passare la compilazione solo in C++; non in C. – yinyueyouge

+0

La domanda è codificata C++ – mouviciel

4

Questo è ciò che _CRTALLOC è definito come:

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 

È una tabella di cose da preinizializzare, di cui un puntatore alla funzioneè posizionato.

Questa pagina descritto CRT inizializzazione:

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

5

Un modo semplice per farlo.

#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

possono sorgere problemi quando before_main() dipende dai dati creati in un'altra funzione eseguita prima di main(). L'ordine in cui tali funzioni vengono eseguite non è definito. Questo, in particolare, significa che i dati globali before_main() creati possono essere sovrascritti dalle routine di runtime che inizializzano i dati globali su 0s – dmityugov

+6

Questo è C++, non C. – RBerteig

+1

sostituire std :: count << con puts() e sarà C – dmityugov

1

Anche in C, v'è la necessità per un po 'di codice da eseguire prima di main() viene inserito, se non altro per trasformare la linea di comando nella convenzione di chiamata C. In pratica, la libreria standard necessita di alcune inizializzazioni e le esigenze esatte possono variare da compilazione a compilazione.

Il vero punto di ingresso del programma è impostato al momento del collegamento ed è solitamente in un modulo denominato qualcosa come crt0 per motivi storici. Come hai scoperto, l'origine di quel modulo è disponibile nelle fonti crt.

Per supportare le inizializzazioni rilevate al momento del collegamento, viene utilizzato un segmento speciale. La sua struttura è un elenco di puntatori di funzione di firma fissa, che verrà iterato all'inizio di crt0 e ogni funzione chiamata. Questo stesso array (o uno molto simile) di puntatori di funzione viene utilizzato in un collegamento C++ per contenere puntatori a costruttori di oggetti globali.

L'array viene compilato dal linker consentendo ad ogni modulo collegato di includere i dati in esso, che sono tutti concatenati insieme per formare il segmento nell'eseguibile finito. L'unico significato della variabile pinit è che viene dichiarato (dalla macro _CRTALLOC()) in tale segmento e che è inizializzato all'indirizzo di una funzione da chiamare durante l'avvio C.

Ovviamente, i dettagli di questo sono estremamente specifici della piattaforma. Per la programmazione generale, si sono probabilmente meglio servita avvolgendo l'inizializzazione e il vostro principale corrente all'interno di un nuovo main():

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

per scopi speciali, modificando il modulo crt0 stesso o fare trucchi specifiche del compilatore per ottenere ulteriori funzioni di inizializzazione presto chiamato può essere la migliore risposta. Ad esempio, quando si creano sistemi incorporati che vengono eseguiti dalla ROM senza un caricatore del sistema operativo, è normale che sia necessario personalizzare il comportamento del modulo per disporre di uno stack su cui spingere i parametri su main(). In tal caso, potrebbe non esserci soluzione migliore rispetto alla modifica di crt0 per inizializzare l'hardware di memoria in base alle proprie esigenze.

2

Ho scritto un premiato article su questo su CodeGuru qualche tempo fa.