编译和链接

    1. #include <iostream>
    2. using namespace std;
    3. int mul(int a, int b)
    4. {
    5. return a * b;
    6. }
    7. int main()
    8. {
    9. int a, b;
    10. int result;
    11. cout << "Pick two integers:";
    12. cin >> a;
    13. cin >> b;
    14. result = mul(a, b);
    15. cout << "The result is " << result << endl;
    16. return 0;
    17. }

    以上代码,包含两个函数,一个函数叫做mul,功能:计算两数之积。该函数两个参数 ab,这两个参数从main函数中的标准输入cin传入的,cin >> a表示:从标准输入读取数据转成整数类型,存到变量a中。

    • main(): called by startup code
    • mul() is called in main()

    解释两个知识点,首先第一点:函数的原型,也就是函数的声明。

    1. # 函数声明
    2. # function prototypes normally are put into head files (*.h; *.hpp)
    3. int mul(int a, int b);

    上面代码行含义:有一个名为mul的函数,返回值为int类型,包含两个int类型的参数:a和b。它只是一个声明,表明存在这么一个函数,但是函数在什么位置,并不知道。

    一般的,会把函数的声明放到头文件 .h或者.hpp文件中。 function prototypes normally are put into head files (.h; .hpp)

    1. # 函数的定义
    2. # function definitiones normally are in source files (*.c; *.cpp)
    3. int mul(int a, int b)
    4. {
    5. return a * b;
    6. }

    上面代码行含义:函数的具体实现(函数的定义)。

    一般的,将函数的实现(定义)写到源文件 .c.cpp中 function definitiones normally are in source files (.c; .cpp)

    以上的例子,只实现了一个两数之积的简单功能,如果要实现一个大型系统,系统里会包含很多函数,我们如果将这些函数全部放到源文件中,源文件会非常长,编译时间长,难以管理和维护。我们可以把源代码放到不同的文件中,分别去管理这些函数,例子如下:

    1. // 函数定义 mul.cpp
    2. # include "mul.hpp"
    3. int mul(int a, int b)
    4. {
    5. return a * b;
    6. }
    1. // 函数声明 -> 头文件 mul.hpp
    2. # pragma once
    3. int mul(int a, int b);

    头文件存放函数声明,在main.cppinclude 该头文件,相当于将头文件嵌入到main.cpp中,源文件在编译的时候碰到mul时,就知道这是一个函数,检查参数的数据类型和个数是否符合函数的声明,

    1. // main.cpp
    2. #include <iostream>
    3. #include "mul.hpp"
    4. using namespace std;
    5. int main()
    6. {
    7. int a, b, result;
    8. cout << "Pick two integers: ";
    9. cin >> a;
    10. cin >> b;
    11. result = mul(a, b);
    12. cout << "The result is" << result;
    13. return 0;
    14. }

    image.png
    编译过程:

    1. main.cpp通过g++编译成main.o,o叫做object文件,是一个二进制文件。-c的意思是只编译不链接。
    2. 同样的,将mul.cpp也通过g++编译成二进制文件,此时我们得到两个二进制文件
    3. 从object文件生成可执行程序的步骤叫做链接。link就是将这两个二进制文件合并。

    image.png
    g++ main.cpp mul.cpp -o mul 一次性编译所有源文件并链接。

    既然能够很方便的一起编译,为什么还要分开编译?
    当源文件特别多时候,调试程序的过程中,要经常编译,每次都要全部编译的话,时间消耗长。假设只需要更新其中某些源文件,则只需要单独编译其中修改的文件,其他不需要更新。

    这件事情如果全部手动操作的话,也是比较耗费时间,有一个工具可以帮助管理,不需要每次手动输入编译命令,这个工具叫做Makefile

    如何debug?
    辨认错误类型:

    • 编译错误(一般是语法错误)
      • Normally caused by grammar error, Please check the source code!
      • image.png
    • 链接错误(头文件里头有,但是链接时没有,symbols not foundor undefined symbols
      • image.png
      • “Symbol not found”, Function mul() is misspelled to Mul()
    • 运行时错误(成功编译且链接,也生成了执行程序,但是运行时报错)
      • image.png
      • The source code can be successfully compiled and linked.
      • The floating point exception (divided by 0) will kill the program.
      • It is a typical runtime error.
      • 编程时要考虑周全,尽可能想到程序抛出异常的可能。