2009-04-08 51 views
7

我正在閱讀微軟的CRT源代碼,我可以想出下面的代碼,其中函數__initstdio1將在main()例程之前執行。如何在VC中進入main()例程之前執行一些代碼?

問題是,如何在VC(不是VC++代碼)中輸入main()例程之前執行一些代碼?

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

輸出將是:

Some code before main! 
z = 10 
End! 

不過,我無法理解的代碼。

我已經做了一些谷歌.CRT $ XIC,但沒有找到運氣。一些專家能解釋一下上面的代碼段對我來說,尤其是如下:

  1. 是什麼線_CRTALLOC(".CRT$XIC") static pinit = __initstdio1;是什麼意思?變量pinit的意義是什麼?
  2. 在編譯期間,編譯器(cl.exe時)拋出一個警告說,如下:

微軟(R)32位C/C++優化編譯器版15.00.30729.01爲80x86的 版權(C)微軟公司。版權所有。

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 

需要做什麼樣的糾正措施才能刪除警告消息?

在此先感謝。


補充:

我已經修改了代碼,並作爲PINIT給_PIFV類型。現在警告信息消失了。

新的代碼如下:

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

回答

1

有一些信息here(搜索CRT)。變量pinit的意義不是,它只是放置在可執行文件中的一部分數據,運行時可以找到它。不過,我勸你給它一個類型,像這樣:

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

鏈接器警告可能只是警告你,你有一個具有int返回類型的功能,但不返回任何東西(可能是你更好地將退貨類型更改爲void)。

3

在C++中至少,你並不需要所有的執行具體的東西:

#include <iostream> 

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

A a; 

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

這是一個好主意。 但是你的代碼只能通過C++編譯;不在C. – yinyueyouge 2009-04-08 08:02:03

+0

問題標記爲C++ – mouviciel 2009-04-08 08:16:19

4

這是_CRTALLOC被定義爲:

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 

這是一個預初始化的表格,其中有一個指向你函數的指針__initstdio1被放置。

本頁面描述CRT初始化:

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

5

一個簡單的方法來做到這一點。

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

即使在C,有必要對一些代碼main()之前運行的輸入,如果只在命令行轉變成C調用約定。實際上,標準庫需要一些初始化,確切的需求可能因編譯而異。

真正的程序入口點被設定在鏈接時,通常是在一個名爲模塊的東西像crt0由於歷史的原因。如你所見,該模塊的源代碼在crt源代碼中可用。

爲了支持在鏈接時發現的初始化,一個特殊的段使用。它的結構是一個固定簽名的函數指針列表,它將在crt0早期迭代並調用每個函數。在C++鏈接中使用函數指針的這個相同的數組(或非常像它)來保存指向全局對象構造函數的指針。

陣列由連接器通過允許連接到包括在它的數據,它們都串聯在一起,以形成在最終可執行分段每個模塊填充。 到可變pinit唯一意義在於,它被聲明(由_CRTALLOC()宏)將設在該段,和被初始化爲一個函數的地址C啓動期間被調用。

很明顯,這些細節非常特定於平臺。對於一般的程序,你是通過包裝你的初始化和當前的主要內可能提供更好的服務新main()

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

特殊用途,修改crt0模塊本身或做具體的編譯器技巧來獲得額外的早期初始化功能所謂的可以是最好的答案。例如,當構建從ROM運行而沒有操作系統加載程序的嵌入式系統時,通常需要定製crt0模塊的行爲,以便有一個堆棧來將參數推送到main()。在這種情況下,可能沒有比修改crt0來初始化內存硬件以滿足您的需求更好的解決方案。

2

我寫了一個屢獲殊榮的CodeGuru article這個前一陣子。

相關問題