编译器是怎样连接文件的?

一个可执行程序通常有几个.cpp、.h文件,那么这些文件在编译时是怎么连接的,譬如我在1.cpp定义一个int a,在2.cpp中引用了它,编译器是怎么找到a的定义的?另外,一个可执行程序要用到很多MFC类,这些类定义在不同的文件中,编译器是怎么找到它们的?

改回答引用ChatGPT
C++ 程序通常由多个源文件(.cpp)和头文件(.h)组成,在编译时需要进行预处理、编译、汇编、链接四个步骤。

在编译时,编译器将每个 .cpp 文件编译成一个目标文件(.o),该文件存储了程序代码的机器语言代码和符号表(包括变量、函数等定义)。在链接阶段,编译器将所有的目标文件连接在一起,生成可执行程序。

如果在 1.cpp 中定义了 int a,在 2.cpp 中引用了 a,那么 2.cpp 编译出的目标文件中并不包含 a 的定义,需要在链接阶段通过符号表找到 a 的定义。这就是为什么需要头文件的原因,头文件用于在编译各个 .cpp 文件时提供公共的定义和声明。

对于 MFC 类,编译器通过预编译的头文件(比如 <afxwin.h>)或者自定义的头文件找到类的定义,然后在编译目标文件时使用这些定义。在链接阶段,连接器会找到 MFC 类的实现,把它们加入到可执行程序中。

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 这篇博客: 一个简单程序中的 从一个源文件(.cpp)到可执行程序到底经历了哪几步? 部分也许能够解决你的问题, 你可以仔细阅读以下内容或者直接跳转源博客中阅读:

    编写好以后就需要编译,这取决于用的操作系统和编译器。而编译器一般都具备集成开发环境(IDE),但是是怎么样进行的?

    1.进行预编译:这一步就是把你的头文件都拷贝进你的.cpp里面;把你的#define进行替换;处理条件编译指令,带#的;把注释给删除了;保留#pragma编译器命令,因为编译程序需要。*

    *1:由于笔记本容量较小,下载的是VSCode,利用MingW进行编译。搜索得知MingW是把开源C语言编译器gcc移植到windows下。

    *2:#pragma Para预处理语句(目前只看到#pragma once,只编译一次头文件):作用是设定编译器的状态,或者完成指定动作。比如说让输出窗口输出消息,或者是什么动作做多少次。

    2.编译:高级语言翻译成机器语言,即完成词法分析,语法分析,语义分析然后优化为汇编语言(生成.obj文件)*,再转为二进制。

    *:汇编语言是可以对硬件进行操作的,相较于机器语言,有相应英文缩写,更容易记忆。

    3.链接:将生成的二进制的文件和包含的库(功能?)链接在一起,进行实现。

    如何从.c到实现的过程,参考:

    https://www.cnblogs.com/mhq-martin/p/11898245.html
    (下面是命令行执行示例
    $ g++ -o prog1 prog1.cc

    $是系统提示符,-o prog1是编译器参数,指定了可执行文件的文件名。在不同的系统中,生成一个prog1 或者prog1.exe的可执行文件。

    C:\Users\me\Programs> cl/EHsc prog1.cpp。

    C:\Users\me\Programs>指的是你的目录,你现在在哪儿操作。cl是运行编译器命令。/EHsc是编译器选项,用来打开标准异常处理。

    *:异常:结构化异常和C++异常
    结构化异常:指的是整数/0,访问无效地址 常见代码
    EXCEPTION_ACCESS_VIOLATION, EXCEPTION_STACK_OVERFLOW,EXCEPTION_INT_DIVIDE_BY_ZERO
    C++异常:又称同步异常,一般是调用API导致

    *:/EHsc (EH exception handling异常处理)是 /EHs 和/EHc 的结合
    /EHa可以捕捉异常,让后面的东西继续运行,完成局部函数的析构,但是其他不可。但是假设不发生异常(?)太多了,后面专门学习。
    关于异常处理命令

    **


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^