2.1.GCC编译器

linux系统下主要的编译器为GCC(GNU Complier Collection),最初GCC只是一个C语言编译器(GNU C Complier),现在GCC已经是一个包含众多语言的编译器了,也就是GNU编译器套件的意思。
GCC能将C、C++语言源程序和目标程序编译链接为可执行文件,如果没有给出可执行文件的名字,GCC将生成名为“a.out”文件。

2.1.1 基本工作流程

使用GCC将C语言源程序生成可执行文件的过程需要经过四个相互关联的步骤:预处理,编译,汇编,链接

  1. 调用“cpp”进行预处理生成.i文件。处理所有”#include”预编译指令,#if #endif条件预编译指令,“#define”宏定义,删除注释。

命令:gcc -E -o hello.i hello.c

  1. 调用“ccl”进行编译。生成.s汇编代码文件。

命令:gcc -S -o hello.s hello.i

  1. 调用“as”指令进行汇编,则是一个针对汇编语言的步骤。生成机器唯一识别的.o二进制目标文件。

命令:gcc -c -o hello.o hello.s

  1. 调用“ld”完成最后的链接工作,将汇编形成的obj文件,系统库的obj文件库文件链接起来,形成可执行文件。

命令: gcc -o hello hello.c

2.2.2 GCC编译器基本用法

1.生成带有调试信息的可执行文件(-g)

gcc -g -o outfile infile
此时将在编译时加上调试信息,以便供GDB调试器调试。

2. 设置自定义头文件路径(-I dirname)

gcc -I dirname infile
将dirname中所指的目录加入程序头文件目录列表中,也就是在系统预设包含文件目录之前先到dirname目录下搜寻相应的头文件。
image.png
第一种情况,预处理程序cpp在系统预设包含文件目录(如/usr/include)中搜寻相应的文件。
第二种情况,cpp先在当前目录中搜寻头文件,然后再去系统预设包含文件目录(如/usr/include)中搜寻。

3.编译时加载库文件(-l name)

image.png

2.2 Makefile文件的使用

一个工程有很多函数,但只有一个main函数,其他函数可能被写在工程的不同源文件中。
可采用两种方式编译生成可执行文件。
image.png
image.png
所以用户可以在一个名为Makefile文件中定义一系列的规则来指定需要编译的文件,如哪些文件需要先编译,那些
文件需要后编译,那些文件需要重新编译。这样用户只需输入make命令,即可根据makefile文件中的规则变异工程文件。
对于大型的开发项目来说,可能包括很多源文件,使用make和makefile文件可以清晰地理顺各个源文件之间的关系。

2.3.1 make命令

make主要是通过make命令调用makefile文件,通过makefile文件描述源程序之间的相互依赖关系,并自动维护编译工作。
make调用格式:

  • “make 目标”,查找makefile文件中指定的目标
  • 一般都省略目标直接用make来查找makefile文件中的第一个目标。

    image.png

    2.3.2 makefile文件的命名

    通常应该使用“makefile”或“Makefile”作为一个makefile文件的文件名。
    用户只需要输入make命令即可根据当前目录下的makefile文件中的规则编译工程文件。

    2.3.3 makefile文件的内容

    image.png

    1.显式规则

    描述了如何更新一个或多个目标文件。
    image.png
    如果不在vim中敲tab,gcc将会报Makefile missing separator. Stop.错误。
    image.png

  • 如果hello不存在则进行编译。

  • 如果hello日期没有hello.c日期新则进行编译。

    2.隐含规则

    隐含规则不需要在makefile命令中明确给出重建特定目标文件所需要的细节描述,而是make命令根据一类目标文件(通常根据文件名的后缀)自动推导。
    image.png
    同时,MakeFile文件还支持一些预设的自动化变量,且只能出现在规则的命令中。
    $ ^——所有的依赖文件,以空格隔开
    $<——第一个依赖文件的名称
    $@——目标的完整名称。

    3.变量定义

    类似于C语言的宏,大小写敏感(推荐大小写搭配名字)
    Makefile文件仅用一个字符或者字符串作为变量来代表一段字符串。
    定义之后就可以引用该变量。
    引用方式为:$(变量名)或 ${变量名}
    image.png

    4.指示符

    5.注释

    通过#进行注释。
    clean规则用于删除中间生成的临时文件和最终的可执行文件“-f”选项强制执行。

    2.3 GDB调试器

    编写源程序后通过编译器可以生成可执行程序。错误可以分为编译错误和执行错误。
    编译错误是指在gcc编译时产生错误,需要根据错误提示找到错误,从第一个错误开始找;然后在网上搜索错误提示;最后总结编译错误的解决方法。
    运行错误是指在gcc时正确,但在运行时产生了错误。可以通过GDB调试和打印调试找出问题。
    GDB是一种字符界面调试器。
    打印调试是指在程序适当位置加printf提示语句(记得在printf中加\n),在调用函数后根据函数返回值结合error值查找错误。

    2.3.1 GDB使用

    image.png
    使用gcc的-g选项编译源程序,生成带调试信息的可执行文件。
    image.png
    在命令行中输入:
    gdb hello
    进入GDB调试环境:
    image.png

    2.3.2 常用命令

    break:设置断点。
    clear:删除刚才停止处的断点。
    continue:从断点开始继续执行
    kill:异常中止在gdb下控制的程序
    print:显示变量或表达式的值
    next:执行下一个源程序行,若断点所在行为函数调用,则不进入函数内部
    quit:退出gdb
    run:执行该程序
    step:执行下一个程序行,若断点所在行为函数调用,则进入函数内部。
    whatis:显示变量或函数类型
    上述指令可直接缩写为首字母。
    image.png

2.4 库

库函数是由系统提供的,供程序员开发时调用,完成特定功能的函数,如printf等,一般是.o目标代码,都需要将.c源文件生成目标文件。
库函数便于编程;可以隐藏细节,降低开发难度和开发周期;也可以保护商业软件的知识产权。
存放函数的文件就像存放函数的仓库,如Glibc库,提供了系统调用和C库的基本函数。
linux库有两种形式:静态库和动态库(共享库)。由.o文件创建。
Linux的库都放在/usr/lib和/lib目录中。GCC在链接时将首先搜索这两个目录。linux将首先搜索指定库的共享库,如果找不到,才会去搜索静态库。
静态库的代码在编译时就已经链接到开发人员开发的应用程序中;
动态库(共享库)只是在程序开始运行时载入,在编译时,只需要简单的指定需要使用的库函数。
因为共享库并没有在程序里包括库函数的内容,只是包含了对库函数的引用,因此可执行文件的代码规模较小。
库文件名都是由前缀lib,库名以及后缀组成。

2.4.1 静态库

image.png
静态库的创建与使用:
将fun1.c和fun2.c生成静态库libc.a

  1. 分别生成目标文件fun1.o,fun.o

gcc -c fun1.c fun2.c

  1. 生成静态库(libxxx.a)

ar -rc/rsc libc.a fun1.o fun2.o

  1. 在usehello.c中使用静态库libc.a

gcc -o usehello_static usehello.c -L -lc -static

-static是强制调用libc.a静态库,如果共享库和静态库同时存在的话,优先链接共享库。
如果将libc.a复制到/lib或/usr/lib,就可以去除上述命令中的-L命令。
cp libc.a /usr/lib
可使用lld命令或ls -l命令查看usehello_static依赖库
lld usehello_static
ls -l usehello_static

2.4.2 动态库

image.png
so是 share object的缩写。
动态库的使用与创建:

  1. 生成目标文件fun1.o fun2.o:
    gcc -fpic -c fun1.c fun2.c

-fpic产生位置独立的代码,pic是position independence code的缩写。因为在编译时,还不知道装入内存的位置,所以不加上此选项,库文件不会正确执行。

  1. 生成共享库
    gcc -shared -o libc.so fun1.o fun2.o
  2. 共享库的使用

共享库默认搜索路径是/usr/lib或/lib,拷贝过去:
cp libhello.so /usr/lib
使用动态链接库:
gcc -o usehello_dynamic usehello.c –lc
或者不拷贝直接使用以下命令编译:
gcc -o usehello_dynamic usehello.c –L ./ –lc