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

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 here
    return 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 函数链接在一起,确保程序在执行时能正确调用这些例程。
  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值