C++ 程序真的是从main函数开始/结束的吗?

  • Post author:
  • Post category:其他



目录


起因


经过


exe_common.inl


telemetry.cpp


traceloggingprovider.h


又回到telemetry.cpp里了?


又跳到exe_common.inl里了


utility_desktop.cpp


exe_common.inl他又来了


exit函数——程序的退出


继续探索——main函数的调用者


exe_common.inl


exe_main.cpp


结果


结论


函数/模块调用次序


嗨!别走!还有呢!


起因

今天单步调试时按F11按多了(我更习惯用F11而不是F10),main函数结束后程序忽然跳到了exe_common.inl里面!

于是兴致大起,写了一个比Hello World还简单的程序来做实验:

int main()
{
    return 0;
}


简单不?这个不是重点,我们现在开始调试

经过

exe_common.inl


按4下F11就跳到了exe_common.inl里

telemetry.cpp

又按4下F11,VS提示未找到telemetry.cpp,于是手动创建了一个空的, 看看能不能迷惑住VS,居然真的迷惑住了

||ヽ(* ̄▽ ̄*)ノミ|Ю

traceloggingprovider.h

又按了大约10次F11,VS又提示需要 traceloggingprovider.h,于是又创建了一个(#^.^#)

又回到telemetry.cpp里了?

又按了5下F11,它跳回了telemetry.cpp里面。又按了两下,他又跳到了exe_common.inl中。

又跳到exe_common.inl里了

又按两下,跳到utility_desktop.cpp里了

utility_desktop.cpp

再按11下,又跳回(exe_common.inl)去了。

exe_common.inl他又来了

再次跳到exe_common.inl里面时,我有一种不祥的预感:下一行就是exit函数了o(╥﹏╥)o

exit函数——程序的退出

再按两下,运行到了函数调用exit(main_result);里面,跳到这里程序才算退出了。

继续探索——main函数的调用者

exe_common.inl

但我的实验还没结束,我再次定位到exe_common.inl里,发现main函数是在那里被调用的:

static int __cdecl invoke_main() throw()
{
    return main(__argc, __argv, _get_initial_narrow_environment());
}

然后启动搜索大法(注意是所有的 Visual C++ 目录),发现有4个源文件都导入了这个模块:

  • exe_main.cpp
  • exe_wmain.cpp
  • exe_winmain.cpp
  • exe_wwinmain.cpp

exe_main.cpp

要的当然是exe_main.cpp啊!点进去一看,(不算空行和注释)居然只有6行!

#define _SCRT_STARTUP_MAIN
#include "exe_common.inl"
extern "C" int mainCRTStartup()
{
    return __scrt_common_main();
}

结果

结论

然后再次启动搜索大法,没搜到那里使用exe_main.cpp,于是得出结论:

main函数不是操作系统调用的,是exe_common.inl调用的!

exe_common.inl也不是操作系统调用的,是exe_main.cpp调用的!

exe_main.cpp才是操作系统调用的!

函数/模块调用次序

最后我整理了一下函数/模块使用次序(括号里的是模块,不在括号里的是函数):

  1. (exe_main.cpp)
  2. (exe_common.inl)
  3. (源.cpp)main
  4. (exe_common.inl)invock_main
  5. (exe_common.inl)__telemetry_main_return_trigger
  6. (telemetry.cpp)
  7. (traceloggingprovider.h)
  8. (telemetry.cpp)
  9. (exe_common.inl)
  10. (utility_desktop.cpp)__scrt_is_managed_app
  11. (exe_common.inl)exit

嗨!别走!还有呢!

提示:以下代码有点长,可以配合后面的程序注解看看。

以下是“程序不从main函数开始,不从main函数结束”的另一个证明方法(原谅我英语不太好,如果英文有语法错误,请指出):

#include <cstdio>
#include <iostream>
#include <string>
#include <cstdlib>

using namespace std;

string str("aa");
class my_class
{
public:
    int n;
    my_class()
    {
        printf("default ctor.\n");
    }
    my_class(int n)
    {
        printf("one arg:int n ctor\n");
        this -> n = n;
    }
    ~my_class()
    {
        printf("dtor.\n");
    }
};

my_class a,b(1);

int func()
{
    printf("func() called.\n");
    return 0;
}

int aaa=func();

void func2()
{
	printf("func2() called.\n");
}

int main(int argc,char *argv[])
{
    printf("start of main.\n");
    cout << "Before the main function is called,IO initialization was completed." << endl;
	printf("a and b is right. b.n = %d\n",b.n);
    int *p=new int;
    printf("Heap initialization was completed too.\n");
    delete p; //防止内存泄露
    printf("argc and argv's value is right,too.\nargc = %d,argv[0]=%s,argv[1]=%s,argv[2]=%s\n",argc,argv[0],argv[1],argv[2]);
    int n;
    scanf("%d",&n);
	atexit(&func2);
    printf("Input initialization was completed!\nn=%d!\n",n);
	printf("str + to_string(n) = %s\n",(str+to_string(n)).c_str());
    cout << "end of main.\n";
    return 1;
}

其实这个程序的逻辑还是比较清晰的:

1.定义一个string型变量(调用了string型变量的构造函数)

2.定义一个类my_class,有两个构造函数,一个析构函数和一个成员变量n

3.定义my_class类的两个实例,分别叫a和b,对a调用第一个构造函数,对b调用第二个构造函数

4.定义两个函数,一个叫做func,没有参数,返回值为int,另一个叫做func2,没有参数,没有返回值

5.调用这个函数,返回值赋值到变量aaa中

6.进入主程序

7.输出b.n,argc,argv[0],argv[1],argv[2]

8.定义并输入n

9.用atexit函数注册func2函数

10.输出n和str+to_string(n)

编译:

g++ -std=c++11 xxx.cpp

运行(看好!):

a at 1

输入:

121

输出:

default ctor.
one arg:int n ctor
func() called.
start of main.
Before the main function is called,IO initialization was completed.
a and b is right. b.n = 1
Heap initialization was completed too.
argc and argv's value is right,too.
argc = 3,argv[0]=a,argv[1]=at,argv[2]=1
121
Input initialization was completed!
n=121!
str + to_string(n) = aa121
end of main.
func2() called.
dtor.
dtor.

你看,在start of main之前和end of main之后都有输出,而在start of main之前,其实IO初始化已经完成(可以用printf,scanf,cin和cout),a,b初始化完成(b.n=1),argc和argv被正确传入,str被正确构造。而在main之后,调用了str,a和b的析构函数,还调用了由atexit函数注册的func2函数。

所以,铁证如山,程序真的不是从main函数开始执行的!



版权声明:本文为m0_66248056原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。