gcc/g++ 编译器

1 gcc/g++ 编译器

Linux 下最常用的C语言编译器是gcc。g++是用来编辑C++程序的,但所有的C++编译器都可以用来编译C程序。g++编译器实际上调用的也是gcc,只是以必要的选项参数来调用,使gcc能识别出C++源代码。
gcc -v:查看gcc的版本,从而确定某些语法特性是否可用。

在这里插入图片描述
常用的选项:
-c:表示编译源文件
-o:表示输出目标文件
-g:表示在目标文件中产生调试信息,用于gdb调试
-D:<宏定义>编译时将宏定义传入进去
-Wall:打开所有类型的警告

1.1 gcc编译过程

使用gcc编译程序的过程是预处理、编译、汇编和链接,所使用的工具依次是预处理器、编译器、汇编器as、链接器ld。预处理将源文件中的宏进行展开;gcc将c文件编译成汇编文件,汇编器将汇编文件编译成机器码;链接器将目标文件和外部符号进行连接,得到一个可执行二进制文件。

在这里插入图片描述

在这里插入图片描述

1.1.1 预处理

gcc -E test.c -o test.i

在这里插入图片描述在这里插入图片描述

1.1.2 编译

gcc -S test.i -o test.s

在这里插入图片描述

1.1.3 汇编

as test.s -o test.o

在这里插入图片描述

利用as命令(汇编器)可以将汇编文件编译成机器码。.o 文件中为目标机器上的二进制文件,使用nm命令可以查看文件中的符号表

在这里插入图片描述

1.1.4 链接

得到.o文件后,如果直接执行这个文件,就会提示无法运行。这是因为前面的符号表当中还有很多U的部分,也就是地址未确定的部分,这种文件是无法执行的。在完成链接以后,各个部分的代码的地址都确定以后,文件才能执行。使用gcc命令可以完成链接。
gcc test.o -o test
ld test.o [其他系统库文件] -o test

在这里插入图片描述
gcc编译过程参数汇总:
-c:只编译不链接,如果不指定输出文件,将自动生成后缀为.o的目标文件
-S:只编译不汇编,生成汇编代码
-E:只进行预处理
-o file:将file文件作为输出文件
-v:打印编译器的版本信息

1.2 其他编译选项

在gcc命令之后可以添加一些参数来实现不同的需求。
-D 参数
-I:如果代码里面包含的头文件不是位于代码所在的目录之下,那么可以在编译时指定头文件所在的目录。
-D:通常测试版本会多一些测试语句,例如调试、报错信息打印等。可以采用测试开关的形式来打开或者关闭测试语句
gcc -E test1.c -o test1.i -D DEBUG
gcc -S test1.i -o test1.s
gcc -o test1 test1.s

不添加-D选项,编译出来的文件就不会执行测试语句。

在这里插入图片描述
gcc -o test2 test2.c -D

在这里插入图片描述

通常来说,在编写程序的时候,一些不规范的写法是不会违背C语言的语法规范的,但是却会很有可能在运行的时候带来意想不到的问题。为了解决这个问题,则会添加警告信息。
② -Wall:列出所有的警告信息。

在这里插入图片描述

③ -ansi:生成标准语法(ANSI C标准)所要求的警告信息,并不列出所有警告。
④ -pedantic:列出ANSI C标准的全部信息。

1.3 静态库和动态库

  库是一组预先编译好的函数的集合,这些函数都是按照可重用的原则编写的。它们通常由一组相互关联的函数组成以执行某项常见的任务。
  标准系统库文件一般存储在/lib和/usr/lib目录中。C语言编译器(链接程序)需要知道搜索哪些库文件,因为在默认情况下,它只搜索标准C语言库。仅把库文件放在标准目录中,就希望编译器能够找到它是不够的,库文件必循遵守特定的命名规范并且需要在命令行中明确指定。
  函数库通常同时以静态库和共享库两种格式存在。库文件的名字总是以lib开头,随后的部分指明这是什么库。文件名的最后部分以.开始,然后给出库文件的类型:.a代表传统的静态函数库;.so代表共享函数库。

1.3.1 静态库

  函数库最简单的形式是一组处于“准备好使用”状态的目标文件。当程序需要使用函数库中某个函数时,它包含一个声明该函数的头文件。编译器和链接器负责将程序代码和函数库结合在一起以组成一个单独的可执行文件。
  静态库,也称作归档文件(archive)。在Linux系统中,静态库以一种称为存档(archive)的特殊文件格式存放在磁盘中。存档文件是一组连接起来的可重定位目标文件的集合,有一个头部用来描述每个成员目标文件的大小和位置。

  ① 为每个函数分别创建各自的源文件;为库文件创建一个头文件,这个头文件将声明库文件中的函数;编写调用函数的程序,它包含库的头文件并且调用库中的函数。
  ② 编译函数以产生要包含在库文件中的目标文件。通过调用带有-c选项的C语言编译器来完成,-c选项的作用是阻止编译器创建一个完整的程序。
语法:gcc -c function.c
  ③ 创建并使用一个库文件。使用ar程序创建一个归档文件并将目标文件添加进去。
语法:ar crsv libname.a function.ogcc -o program program.o libname.a

在这里插入图片描述
  可以使用-l选项来访问函数库,未保存在标准位置,必须使用-L选项来告诉编译器在何处找到它。-L选项告诉编译器在当前目录(.)中查找函数库。-lname选项告诉编译器使用名为libname.a的函数库。
语法:gcc -o program program.o -L. -lname

在这里插入图片描述
  将库文件libname.a拷贝到/lib或者/usr/lib下,这是系统的默认搜素路径。

在这里插入图片描述

1.3.2 动态库

  静态库的一个缺点是,当同时运行许多应用程序并且他们都使用来自同一个函数库的函数时,内存中就会有同一函数的多份副本,而且在程序文件自身中也有多份同样的副本。这将消耗大量宝贵的内存和磁盘空间。
  共享库的保存位置与静态库是一样的,但共享库有不同的文件名后缀。当一个程序使用一个共享库时,它的链接方式是这样的:程序本身不在包含函数代码,而是引用运行时可访问的共享代码。当编译好的程序被装载到内存中执行时,函数引用被解析并产生对共享库的调用,如果有必要,共享库才被加载到内存中。
  创建动态库:
gcc -fPIC -Wall -c function.c;-fPIC选项指示编译器生成与位置无关的代码。
gcc -shared -o libname.so function.o
gcc -o main main.c -lname

  在运行main前,需要注册动态库的路径,将库文件拷贝到/lib或者/uer/lib下。

在这里插入图片描述

  动态库——编译时,动态库不会被复制到最终的二进制可执行文件中;执行时,二进制文件和动态库都会被加载到进程地址空间中,动态库方便更新。
  静态库——在执行程序时不依赖静态库是否存在,静态库会使程序臃肿并且难以升级。

1.3.3 符号链接生成

ln -s 源文件 软链接名

1.3.4 查看库的依赖关系

which ls
ldd /bin/lsldd只能检查动态依赖

在这里插入图片描述

1.3.5 升级版本

  ① 生成新的库文件 ② 将原来的软链接指向新的库文件 ③ 删除旧的库文件

1.4 gcc 优化选项

  gcc对代码进行优化通过选项“-On”来控制优化级别(n是整数)。使用优化选项“-O1”主要进行线程跳转和延迟退栈两种优化。使用优化选项“-O2”除了完成所有“-O1”级别的优化之外,还要进行一些额外的调整工作,如处理其指令调度等。选项“-O3”则还包括循环展开或其他一些与处理器特性相关的优化工作。
  虽然优化选项可以加速代码的运行速度,但对于调试而言将是一个很大的挑战。因为代码在经过优化之后,原先在源程序中声明和使用的变量很可能不再使用,控制流也可能会突然跳转到意外的地方,循环语句也有可能因为循环展开而变得到处都有,这些对调试来讲都是不好的。所以,在调试的时候最好不要使用任何的优化选项,只有当程序在最终发行的时候才考虑对其进行优化,通常用的是 -O2。

  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秩一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值