如何在VC中输入main()例程之前执行一些代码?
我正在阅读Microsoft的CRT源代码,我可以提出以下代码,其中函数__initstdio1将在main()例程之前执行。
问题是,如何在VC(而不是VC ++代码)中输入main()例程之前执行一些代码?
#include #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上做了一些谷歌,但没有找到运气。 有些专家可以向我解释上面的代码段,尤其是以下内容:
- 这行是什么
_CRTALLOC(".CRT$XIC") static pinit = __initstdio1;
意思? 变量pinit有什么意义? - 在编译期间,编译器(cl.exe)会发出如下警告:
Microsoft(R)32位C / C ++优化编译器版本15.00.30729.01 for 80×86版权所有(C)Microsoft Corporation。 版权所有。
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 #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; }
这里有一些信息(搜索CRT)。 变量pinit
的意义是none,它只是放在可执行文件中的一段数据,运行时可以在其中找到它。 但是,我建议你给它一个类型,如下所示:
_CRTALLOC(".CRT$XIC") static void (*pinit)()=...
链接器警告可能只是警告您有一个具有int
返回类型的函数,但不返回任何内容(可能您最好将返回类型更改为void
)。
一个简单的方法来做到这一点。
#include 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; }
这就是_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初始化:
至少在C ++中,您不需要所有特定于实现的东西:
#include struct A { A() { std::cout << "before main" << std::endl; } }; A a; int main() { std::cout << "in main" << std::endl; }
我刚刚在CodeGuru上写了一篇关于这个的获奖文章 。
即使在C中,也需要在输入main()
之前运行一些代码,如果只是为了将命令行转换为C调用约定。 实际上,标准库需要一些初始化,确切的需求可能因编译而异。
真正的程序入口点在链接时设置,并且由于历史原因通常位于名为crt0
的模块中。 如您所见,crt源中提供了该模块的源代码。
要支持在链接时发现的初始化,请使用特殊段。 它的结构是一个固定签名的函数指针列表,它将在crt0
尽早迭代并调用每个函数。 函数指针的这个相同的数组(或非常类似的)在C ++链接中用于保存指向全局对象的构造函数的指针。
数组由链接器填充,允许链接的每个模块包含其中的数据,这些数据全部连接在一起以形成完成的可执行文件中的段。 变量pinit
的唯一意义是它(由_CRTALLOC()
宏声明)位于该段中,并初始化为在C启动期间要调用的函数的地址。
显然,这个细节非常特定于平台。 对于一般编程,通过将初始化和当前main包装在新的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
以初始化内存硬件以满足您的需要更好的解决方案。