您的位置:首页 > 健康 > 养生 > 【C++】main函数是怎么开始的呢?

【C++】main函数是怎么开始的呢?

2024/10/6 20:34:15 来源:https://blog.csdn.net/2403_86785171/article/details/141934993  浏览:    关键词:【C++】main函数是怎么开始的呢?

文章目录

      • 1. 启动过程概述
      • 2. 启动例程 (`_start`)
      • 3. 全局/静态变量的初始化
      • 4.main 函数的执行
      • 5.程序退出后的清理
      • 6. 与编译器和链接器相关的细节

1. 启动过程概述

当一个 C++ 程序启动时,操作系统负责加载可执行文件并将控制权移交给程序。具体的启动过程大致如下:

  1. 操作系统加载可执行文件:操作系统将可执行文件加载到内存,并设置程序的堆栈指针等。
  2. 调用启动例程(Startup Code):在调用 main 函数之前,程序会首先执行启动代码,这个启动代码由编译器或标准库提供,通常称为 _start 函数。它负责为程序的执行做准备。
  3. 初始化全局/静态变量:C++ 中,全局变量和静态变量的初始化在 main 函数之前进行。编译器生成的启动代码会负责这些变量的初始化。对于构造函数,需要先调用构造函数初始化对象。
  4. 调用 main 函数:完成所有初始化工作后,启动代码会调用用户定义的 main 函数。
  5. 程序执行 main 函数:程序从 main 函数开始执行用户定义的逻辑。
  6. 程序退出:当 main 函数结束后,控制权返回给启动代码,启动代码负责执行清理工作,并向操作系统返回退出状态码。

2. 启动例程 (_start)

_start 函数是由系统库(例如 libclibstdc++)提供的,用来为用户的 main 函数做初始化工作。它的作用包括:

  • 设置程序的堆栈。
  • 初始化全局变量和静态变量。
  • 为动态库加载器提供支持。
  • 调用 C++ 全局对象的构造函数(包括全局和静态对象)。
  • 在完成所有初始化后,调用用户的 main 函数。
// 伪代码说明启动过程
void _start() {// 初始化低级系统资源system_initialization();// 调用全局和静态对象的构造函数initialize_global_objects();// 调用用户定义的 main 函数int result = main();// 处理程序退出后的清理工作,并返回给操作系统exit(result);
}

3. 全局/静态变量的初始化

C++ 中,全局和静态对象在进入 main 函数之前会被初始化,这也是 C++ 和 C 之间的一个重要区别。C++ 支持复杂的对象模型,因此在进入 main 函数之前,编译器会调用全局对象和静态对象的构造函数。这些对象的初始化顺序是:

  1. 已初始化的全局变量会被加载到内存并赋予其初始值。
  2. 未初始化的全局变量会被赋予默认值(通常是零值)。
  3. 全局和静态对象的构造函数会在 main 函数之前执行。

例如:

#include <iostream>class MyClass {
public:MyClass() {std::cout << "MyClass Constructor" << std::endl;}
};MyClass globalObject;  // 在 main 之前执行构造函数int main() {std::cout << "Inside main" << std::endl;return 0;
}

在这段代码中,globalObject 的构造函数会在 main 之前执行。

4.main 函数的执行

当启动代码完成所有的准备工作之后,程序控制权交给 main 函数。main 函数的签名通常有两种形式:

int main()  // 最常见的形式,表示不接收参数
{// Your code herereturn 0;  // 返回给操作系统,表示程序成功结束
}int main(int argc, char* argv[])  // 可接收命令行参数
{// argc 表示参数个数// argv 是参数数组return 0;
}

5.程序退出后的清理

main 函数执行完毕后,控制权会返回给启动例程(如 _start),然后会进行如下清理工作:

  • 调用全局和静态对象的析构函数:如果程序中有全局或静态对象,其析构函数会被调用,以便进行资源释放。
  • 执行 atexit 注册的函数:如果程序使用了 atexit 注册退出时需要执行的函数,它们也会被依次执行。
  • 返回退出状态:启动代码会将 main 函数的返回值作为程序的退出状态码返回给操作系统。

例如:

#include <iostream>class MyClass {
public:~MyClass() {std::cout << "MyClass Destructor" << std::endl;}
};MyClass globalObject;  // 在程序结束后调用析构函数int main() {std::cout << "Inside main" << std::endl;return 0;
}

在这段代码中,程序退出时,globalObject 的析构函数会被自动调用。

6. 与编译器和链接器相关的细节

  • 编译器在编译过程中会生成初始化和清理代码,用来处理全局/静态对象的构造和析构。
  • 链接器将这些初始化和清理代码与用户定义的 main 函数链接在一起,确保程序在执行时能正确调用这些例程。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com