程序翻译的过程 源文件生成可执行程序的过程
预处理的作用/工作: 宏替换,头文件展开,条件编译,去注释。
[yzl@VM-4-5-centos testdir]$ ll
total 4
-rw-rw-r-- 1 yzl yzl 314 Jul 30 17:14 test.c
[yzl@VM-4-5-centos testdir]$ gcc -E test.c -o test.i
[yzl@VM-4-5-centos testdir]$ ll
total 24
-rw-rw-r-- 1 yzl yzl 314 Jul 30 17:14 test.c
-rw-rw-r-- 1 yzl yzl 17030 Jul 30 17:15 test.i
gcc -E test.c -o test.i 选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程。选项“-o”是指目标文件,“.i”文件为已经过预处理的C原始程序。
上图中左边为预处理之后的test.i文件,可以看出预处理做了哪些操作。 即宏替换,头文件展开,条件编译,去注释。
在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查 无误后,gcc 把代码翻译成汇编语言。
gcc -S test.i -o test.s 进行程序的翻译,如果编译完成,就停下来,并生成test.s文件
-S 该选项只进行编译而不进行汇编,生成汇编代码。
[yzl@VM-4-5-centos testdir]$ gcc -S test.i -o test.s
[yzl@VM-4-5-centos testdir]$ ll
total 28
-rw-rw-r-- 1 yzl yzl 328 Jul 30 17:42 test.c
-rw-rw-r-- 1 yzl yzl 16991 Jul 30 17:50 test.i
-rw-rw-r-- 1 yzl yzl 745 Jul 30 17:51 test.s
汇编 : 把汇编语言test.s 翻译为可重定向二进制目标文件 test.o,里面存储的是二进制目标代码。
gcc -c test.s -o test.o 进行程序的翻译,如果汇编完成,就停下来,并生成test.o文件
[yzl@VM-4-5-centos testdir]$ gcc -c test.s -o test.o
[yzl@VM-4-5-centos testdir]$ ll
total 32
-rw-rw-r-- 1 yzl yzl 328 Jul 30 17:42 test.c
-rw-rw-r-- 1 yzl yzl 16991 Jul 30 17:50 test.i
-rw-rw-r-- 1 yzl yzl 1576 Jul 30 17:53 test.o
-rw-rw-r-- 1 yzl yzl 745 Jul 30 17:52 test.s
利用hexdump工具,可以看到test.o里的二进制内容,此时test.o是一个可重定向二进制目标文件
成功编译之后,进入链接阶段。
链接是将多个.o ,.obj可重定向二进制目标文件和静态库/动态库链接起来,生成可执行程序
gcc test.o -o test 也可以 gcc test.c -o test
只是一个从test.o目标文件开始翻译,一个是从.c文件开始从头翻译。最终都生成可执行文件
小总结:
以.c程序为例,无论是Linux下,还是Windows的vs2019下,都要遵循这个程序翻译的过程:
预处理,编译,汇编,链接。我们的头文件在预处理之后,就已经在.c/.cpp文件中展开了。而gcc这个工具可以做到做完某个流程之后停下来。指令就是-E -S -c 分别对应完成预处理,完成编译,完成汇编。 生成的文件依次为.i .s .o
任何一个C程序或者C++程序都离不开动态库或者静态库。在之前VS2019这样的IDE中,其实每次编译运行程序都用到了动态库,只是我们把关注点放在了编写代码上,对于程序的翻译过程没有研究,从而忽略了这个点。
当我们在.c文件中使用printf这样的库函数时,需要包头文件,我们知道,头文件提供库函数的声明,而库函数的定义就是在动态库/静态库中。
静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时可以独立运行,不再需要库文件。Linux下静态库后缀名一般为“.a” 动态库后缀名为“.so”
动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件 加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”, gcc 在编译时默认使用动态库。
有关动态库和静态库,这里的理解还不够全面,随着后期的学习再逐渐加深理解。
上面讲的是文件的编译链接过程,中间穿插了gcc的命令行使用方法。
gcc/g++ 默认形成的可执行程序是动态链接的!
[yzl@VM-4-5-centos testdir]$ ldd test
linux-vdso.so.1 => (0x00007ffdd6fb0000)
/$LIB/libonion.so => /lib64/libonion.so (0x00007f695bc3c000)
libc.so.6 => /lib64/libc.so.6 (0x00007f695b755000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f695b551000)
/lib64/ld-linux-x86-64.so.2 (0x00007f695bb23000)
[yzl@VM-4-5-centos testdir]$ file test
test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=adae662af334b0bcb43e4bfacc2fe0559dd66250, not stripped
如上,.so就是Linux下动态库文件的后缀,下方的dynamically linked就是表明动态链接。
指定生成静态链接的可执行程序
加-static即可指定采用静态链接的方式编译链接源文件。可以明显看出静态链接的可执行程序比动态链接的可执行程序大很多。
[yzl@VM-4-5-centos testdir]$ gcc test.c -o test_static -static
[yzl@VM-4-5-centos testdir]$ ll
total 888
-rwxrwxr-x 1 yzl yzl 8384 Jul 30 21:04 test
-rw-rw-r-- 1 yzl yzl 328 Jul 30 17:42 test.c
-rw-rw-r-- 1 yzl yzl 16991 Jul 30 17:50 test.i
-rw-rw-r-- 1 yzl yzl 1576 Jul 30 17:53 test.o
-rw-rw-r-- 1 yzl yzl 745 Jul 30 17:52 test.s
-rwxrwxr-x 1 yzl yzl 861240 Jul 30 21:50 test_static
[yzl@VM-4-5-centos testdir]$ ldd test_static
not a dynamic executable
[yzl@VM-4-5-centos testdir]$ file test_static
test_static: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=aab0a9e371c993a62c024b75d99c488c7bd1d3e0, not stripped