Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/codeigniter/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 编译/链接过程是如何工作的?_C++_Compiler Construction_Linker_C++ Faq - Fatal编程技术网

C++ 编译/链接过程是如何工作的?

C++ 编译/链接过程是如何工作的?,c++,compiler-construction,linker,c++-faq,C++,Compiler Construction,Linker,C++ Faq,编译和链接过程是如何工作的 (注意:这是一个条目。如果您想对在此表单中提供常见问题解答的想法提出批评,则可以在此处进行评论。该问题的答案将在中进行监控,其中常见问题解答的想法是从中开始的,因此您的答案很可能会被提出此想法的人阅读。) C++程序的编译涉及三个步骤: 预处理:预处理器采用C++源代码文件,处理 >包含 s,定义< /code > s和其他预处理器指令。这个步骤的输出是一个没有预处理器指令的“纯”C++文件。 编译:编译器获取预处理器的输出并从中生成一个对象文件 链接:链接器获取

编译和链接过程是如何工作的

(注意:这是一个条目。如果您想对在此表单中提供常见问题解答的想法提出批评,则可以在此处进行评论。该问题的答案将在中进行监控,其中常见问题解答的想法是从中开始的,因此您的答案很可能会被提出此想法的人阅读。)
C++程序的编译涉及三个步骤:

  • 预处理:预处理器采用C++源代码文件,处理<代码> >包含 s,<代码>定义< /code > s和其他预处理器指令。这个步骤的输出是一个没有预处理器指令的“纯”C++文件。

  • 编译:编译器获取预处理器的输出并从中生成一个对象文件

  • 链接:链接器获取编译器生成的对象文件,并生成库或可执行文件

  • 预处理 预处理器处理预处理器指令,如
    #include
    #define
    。C++的语法不可知,这就是为什么必须谨慎使用的原因。

    一次使用一个C++源文件,用文件(通常只是声明)的内容替换<代码>包含< <代码>指令,进行宏替换(<代码>定义> <代码>),根据<代码> >选择< >代码>不同的文本部分,

    \ifdef
    \ifndef
    指令

    预处理器处理预处理令牌流。宏替换被定义为用其他令牌替换令牌(操作符
    ####
    可以在有意义时合并两个令牌)

    在所有这些之后,预处理器生成一个输出,该输出是由上述转换产生的令牌流。它还添加了一些特殊的标记,告诉编译器每一行来自何处,以便编译器可以使用这些标记生成合理的错误消息

    在此阶段,通过巧妙地使用
    #if
    #error
    指令,可能会产生一些错误

    汇编 编译步骤在预处理器的每个输出上执行。编译器解析纯C++源代码(现在没有任何预处理器指令),并将其转换成汇编代码。然后调用底层后端(工具链中的汇编程序),将代码汇编成机器代码,生成某种格式的实际二进制文件(ELF、COFF、a.out等)。此目标文件包含输入中定义的符号的编译代码(二进制形式)。对象文件中的符号按名称引用

    对象文件可以引用未定义的符号。当您使用声明而不为其提供定义时,就是这种情况。编译器并不介意这一点,只要源代码格式正确,编译器就会很乐意生成目标文件

    编译器通常允许您在此时停止编译。这是非常有用的,因为使用它可以分别编译每个源代码文件。这样做的好处是,如果只更改一个文件,则无需重新编译所有内容

    生成的对象文件可以放在称为静态库的特殊归档中,以便以后更容易重用

    在这个阶段,报告“常规”编译器错误,如语法错误或失败的重载解析错误

    连接 链接器从编译器生成的对象文件生成最终编译输出。此输出可以是共享(或动态)库(虽然名称类似,但它们与前面提到的静态库没有太多共同之处)或可执行文件

    它通过用正确的地址替换对未定义符号的引用来链接所有对象文件。这些符号中的每一个都可以在其他对象文件或库中定义。如果它们是在标准库以外的库中定义的,则需要告诉链接器有关它们的信息

    在此阶段,最常见的错误是缺少定义或重复定义。前者意味着要么定义不存在(即未写入),要么它们所在的对象文件或库未提供给链接器。后者很明显:相同的符号定义在两个不同的对象文件或库中。

    在标准正面:

    • 翻译单元是源文件、包含的标题和源文件的组合,减去条件包含预处理器指令跳过的任何源行

    • 该标准定义了翻译的9个阶段。前四个对应预处理,后三个是编译,下一个是模板实例化(生成实例化单元),最后一个是链接


    实际上,第八阶段(模板实例化)通常在编译过程中完成,但有些编译器将其延迟到链接阶段,有些编译器将其分为两个阶段。

    此主题在CProgramming.com上讨论:

    以下是作者所写的:

    编译与创建可执行文件并不完全相同! 相反,创建可执行文件是一个分为以下几个部分的多阶段过程 两个部分:编译和链接。实际上,即使是一个程序 “编译得很好”由于过程中出现错误,它实际上可能无法工作 连接阶段。从源代码文件开始的整个过程 对于可执行文件,最好将其称为构建

    汇编 编译是指处理源代码文件(.c、.cc或 .cpp)和创建“对象”文件。此步骤不会创建 用户可以实际运行的任何内容。相反,编译器只是 生成与 编译的源代码文件。例如,如果您编译(但是 不要链接)三个s
    cpp hello.c > hello.i
    
    gcc -S hello.i
    
    as -o hello.o hello.s
    
    ld -o hello hello.o ...libraries...