如何在VC中输入main()例程之前执行一些代码?

时间:2009-04-08 07:52:32

标签: c msvcrt crt

我正在阅读微软的CRT源代码,我可以提出以下代码,其中函数__initstdio1将在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)会发出如下警告:
  3. Microsoft(R)32位C / C ++优化编译器版本15.00.30729.01 for 80x86 版权所有(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 <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;
    }
    

6 个答案:

答案 0 :(得分: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 :(得分: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

答案 2 :(得分:3)

至少在C ++中,您不需要所有特定于实现的东西:

#include <iostream>

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

A a;

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

答案 3 :(得分:2)

我刚刚在CodeGuru上写了一篇关于这个的获奖作品article

答案 4 :(得分:1)

有一些信息here(搜索CRT)。变量pinit的意义是无,它只是放在可执行文件中的一段数据,运行时可以在其中找到它。但是,我建议你给它一个类型,如:

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

链接器警告可能只是警告您有一个具有int返回类型的函数,但不返回任何内容(可能您最好将返回类型更改为void)。

答案 5 :(得分: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模块的行为,以便有一个堆栈可以将参数推送到{ {1}}。在这种情况下,可能没有比修改main()以初始化内存硬件以满足您的需要更好的解决方案。