大家好,我是慕兮兮
文章目录
1. 背景知识 1.1 预处理
预处理功能主要包括宏定义、文件包含、条件编译、注释去除等。
预处理指令是以#号开头的代码行。
示例:gcc –E hello.c –o hello.i
选项“-E”,该选项的作用是让gcc在预处理完成后停止编译过程。
选项“-o”指的是目标文件,“.i”文件是预处理后的C原程序。
在预处理过程中,编译器会扩展头文件、删除注释、宏替换、条件编译等。
选项-E是对程序进行翻译,并在预处理完成后停止编译过程。
选项“-o”指的是目标文件,“.i”文件是预处理后的C原程序
头文件扩展、注释去除、宏替换、条件编译
gcc -E test.c -o test.i
-E:从现在开始,翻译程序,预处理完成后停止。
-o:指定形成的临时文件的名称(.i)
此时test.i就生成了,我们使用vim进入test.c,使用底线模式vs test.i:
可以看到预处理扩展了头文件、删除了注释、宏替换和条件编译。 (此时临时文件还是C语言的)
可以将参数传递给编译器以确保宏是在命令行上定义的
1.2 编译(生成程序集)
在这个阶段,gcc首先要检查代码的规范性以及是否存在语法错误等,以确定代码实际要做的工作。 检查正确后,gcc 将代码翻译成汇编语言。
用户可以使用“-S”选项来查看。 该选项仅编译而不汇编并生成汇编代码。
示例:gcc –S hello.i –o hello.s
将 C 语言转换为汇编语言:
S:从现在开始,我们将翻译程序,完成编译工作,编译成汇编后停止。
用vim打开test.s,我们可以看到汇编代码:
1.3 汇编(生成机器可读代码)
汇编阶段是将编译阶段生成的“.s”文件转换为目标文件
读者可以在这里使用选项“-c”看到汇编代码已经转换为“.o”二进制目标代码
示例:gcc –c hello.s –o hello.o
注意:此时生成的临时文件不是可执行文件,而是一个由于没有链接而无法执行的二进制目标文件。
-c 从现在开始c标准库源码分析,进程程序的翻译将在完成汇编工作并将其转换为可重定向目标二进制文件后停止。
用vim打开test.o,但是我们根本看不懂:
然后输入命令od test.o(以二进制形式打开文件):
1.4 链接(生成可执行文件或库文件)
链接过程就是将写好的代码与C标准库中的代码结合起来
编译成功后,进入链接阶段。
示例:gcc hello.o –o 你好
-o 链接过程形成一个可执行程序,一个可执行二进制程序(库+您的代码)
1.5 练习 练习 1
1.在编译过程中,产生parse tree的过程是哪个阶段?
A.语法分析
B.语义分析阶段
C.词法分析
D.目标代码生成阶段
答案:A
练习2
2.程序的完整编译过程分为是:预处理,编译,汇编等,
如下关于编译阶段的编译优化的说法中不正确的是
A.死代码删除指的是编译过程直接抛弃掉被注释的代码
B.函数内联可以避免函数调用中压栈和退栈的开销
C.for循环的循环控制变量通常很适合调度到寄存器访问
D.强度削弱是指执行时间较短的指令等价的替代执行时间较长的指令
答案:A
2、函数库
注意:我们自己写的代码和库是两个不同的东西。
链接的本质是我们调用库函数时如何将其与标准库关联起来。
C标准库是别人给我们准备好的,可以直接使用。 我们所有使用库中函数(比如printf)的代码都只是自己写了函数的调用,并没有相应的实现。 只有链接的时候,相应的实现才和我们的代码关联起来。
在我们的C程序中,“printf”的函数实现并没有定义,而预编译中包含的“stdio.h”只有函数的声明,而没有定义函数的实现。 那么,“那printf呢”函数的实现在哪里呢?
最终的答案是:系统已经将这些函数实现到了一个名为libc.so.6的库文件中。 如果不指定,gcc将去系统默认的搜索路径“/usr/lib”。 搜索,即链接到libc.so.6库函数,这样就可以实现“printf”这个函数了,这就是链接的作用
第一步,生成可执行文件
gcc test.o -o mytest
第二步是检查可执行文件是如何链接的。
file mytest
动态链接是动态链接
第三步查看可执行文件的动态库:
其中libc.so.6为动态库。
函数库一般分为静态库和动态库两种。
2.1 动态库
动态库在编译和链接时不会将库文件的代码添加到可执行文件中。 相反,该库是在程序执行时由运行时链接文件加载的,这样可以节省系统开销。 动态库一般带有“.so”后缀。 前面提到,libc.so.6是一个动态库。 gcc编译时默认使用动态库。 完成链接后,gcc可以生成可执行文件。
形成的可执行程序较小(节省资源、内存、磁盘、网络)
2.2 静态库
静态库是指在编译链接时,将库文件的所有代码都添加到可执行文件中,因此生成的文件比较大,但运行时不再需要库文件。 其后缀一般为“.a
不受库升级或删除影响,线程可执行程序过大,网络、磁盘、内存占用过大
静态库就是在你的程序中放置一份C标准库的副本。 链接不是关联,而是在我们的程序中使用的方法。 给我的程序一份c标准库源码分析,静态链接就完成了。
优点:不受库升级或库删除的影响。
缺点:生成的可执行程序太大——网络、磁盘、内存
去掉前缀lib,去掉后缀.so。 其余的是库名称。
去掉上面的libc.so.6后,就剩下了C,所以这个库就是C标准库(动态库)
该图书馆的位置:
ll /lib64/libc.so.6
Linux下默认形成可执行程序,默认使用动态库/lib64/libc-2.17.so。
gcc test.c -o mytest.s -static
查看链接:
Staticly linked就是静态链接,其中ldd命令只能查看动态链接的可执行程序。
我们无法删除系统中的C动态库,因为Linux命令是动态链接的。 不要删除C动态库! ! ! 有许多程序使用该库,但该库只有一份副本。 所有用C语言编写的程序都不会出现重复的库代码。
那么动态库就相当于共享库。 以后我们下载用C写的程序就不用下载C标准库了。
静态链接复制的不是.so里面的代码,而是系统中必须存在的、以.a结尾的静态库。 /lib64/lib.a 静态库。
动态链接只能找到动态库,静态链接只能找到静态库(一般来说,系统会自动携带动态库,因为系统运行需要动态库,如果静态库不存在,则需要自己安装吧!)
检查libc.a是否已经安装:
sudo find / -name 'libc.a'
安装:
sudo yum install -y glibc-static
3.g++的基本使用
sudo yum install -y gcc-c++
sudo yum install -y libstdc++-static
当然C++也有相应的静态库和动态库。
4.gcc选项
-E 仅激活预处理,这不会生成文件,您需要将其重定向到输出文件
-S 编译为汇编语言,无需汇编和链接
-c 编译为目标代码
-o 文件输出到文件
-static 此选项将静态链接应用于生成的文件
-g 生成调试信息。 GNU 调试器可以利用此信息。
-shared 该选项会尝试使用动态库,因此生成的文件较小,但系统需要动态库。
-O0
-O1
-氧气
-O3 4个级别的编译器优化选项,-O0表示不优化,-O1是默认值,-O3具有最高优化级别
-w 不生成任何警告消息。
-Wall 生成所有警告消息。
4.写在最后
为了支持我们的编程,系统本身提供了标准库的.h(告诉我们如何使用)
标准动静态库.so/.a(告诉我们方法实现)
我们的程序使用include头文件,然后链接别人的库,将我的代码+库代码组成可执行程序! ! !
我的代码+库代码==可执行程序
另外,Windows下的原理是一样的。 默认形成可执行的动态链接,动态:.dll,静态:.lib
暂无评论内容