2009-04-08 52 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这个前一阵子。

相关问题