C 编程指南

编译和执行

gcc 不是真正的编译器,而是所谓的“编译器驱动程序”,因此它协调了编译的许多步骤【通常有 4~5 个步骤】

  • gcc 将执行 C 预处理器 cpp 来处理某些指令

    程序 cpp 只是一个源到源的转换器,所以它的最终产品仍然只是 源代码

  • 然后真正的编译将开始,通常是一个名为 cc1 的命令,这会将源代码级别的 C 代码转换为 特定主机的低级汇编代码

  • 然后执行汇编程序 as,生成目标代码(机器可以真正理解的数据位和代码位)

  • 最后链接编辑器(或链接器)ld 将它们组合成最终的可执行程序

flowchart LR
src[[源文件]] -- "预处理 cpp" --> cpp[[源代码]] -- "汇编 cc1" --> cc1[[汇编代码]] -- "编译 as" --> as[[机器码]] -- "链接 ld" --> ld[[可执行文件]]

与库链接

有时,你可能想在程序中使用库函数。因为 C 库中有很多函数(可以自动链接到每个程序),所以通常要做的就是找到正确的 #include 预处理命令

有两种类型的库:静态链接库(以.a 结尾)和动态链接库(以.so 结尾)

  • 静态链接库直接组合到可执行文件中

    也就是说,链接器将库的低级代码插入到可执行文件中,从而产生更大的二进制对象,人们有时更喜欢库的静态版本,因为使用动态库有一点点性能开销

  • 动态链接通过在程序可执行文件中包含对库的引用来改进这一点

    程序运行时,操作系统加载程序动态链接库,这种方法优于静态方法,因为它节省了磁盘空间(没有不必要的大型可执行文件),并允许应用程序在内存中共享库代码和静态数据

在 gcc 中 -I 选项应该针对编译,而 -L 选项针对链接,建议你应该总是使用 -Wall,这会提供很多关于可能出错的额外警告

分别编译

一旦程序开始变得足够大,你可能希望将其拆分为单独的文件,分别编译每个文件,然后将它们链接在一起

  • 这个过程最好由另一个程序 make 来管理,我们接下来介绍它

Makefile 基于规则,这些规则决定需要发生的事情。规则的一般形式是:

target: prerequisite1 prerequisite2 ...
    command1
    command2
    ... 
  • target(目标)通常是程序生成的文件的名称
  • prerequisite(先决条件)是用于生成目标的输入文件
  • command(命令)是一个执行的动作
  • 重要提示:必须在每个命令行的开头放一个制表符

调试

在创建了良好的构建环境和正确编译的程序之后,你可能会发现程序有问题

  • 有某种帮助工具:gdb
  • 要为调试会话做好准备,请重新编译程序,并确保将 -g 标志加入每个编译行
  • 另外,不要打开优化 -O,尽管这可能也行,但在调试过程中也可能导致困扰

它是丰富而灵活的调试工具,有许多功能,而不只是这里有限篇幅中描述的功能

结尾

简要介绍了 gcc、make、gdb 作用,它们是 C 编程中最为重要的工具,如果要真正了解它们你应该使用 man 命令