目录
三、linux下C的编译的四个步骤——预处理、编译、汇编和链接
一、引入
(一)引例
在shell下用vim编辑器可编写C语言代码,如:
#include <stdio.h>
int main(){
printf("hello!\n");
return 1;
}
编写完上面的代码后,只需做两步:
(1)输入 gcc hello.c -o hello
(2)输入 ./hello
如果一切正常的话,此时你应该会在屏幕上看到输出hello并换行的打印。
当然,我们不会满足于这么简单的打印功能。下面就编写一个简单的迭代函数:
#include <stdio.h>
int iterate(int value){
if(1 == value) return 1;
return iterate(value - 1) + value;
}
int main(){
printf("%d\n", iterate(10));
return 1;
}
此时,同样需重复上面的步骤:
(1)输入gcc hello.c -o hello
(2)输入./hello
当然此时如果一切正常的话,你就会看到屏幕上输出55这个数。(1到10的数之和就是55, 这说明我们的程序是正确的)
注:之所以使用“./可执行文件名”,是因为运行可执行文件时,必须指明路径。又因仍处于当前目录(可执行文件所在目录),故使用相对路径时,就表述为“./文件名”。
(二)总概
1.完整编译流程:
创建一个c语言源代码文件;
gcc -E hello.c -o hello.i(头文件展开,进行源文件中的宏替换,注释过滤)
gcc -S hello.i -o hello.s(将源文件编译成汇编文件)(这一阶段耗时最大)
gcc -c hello.s -o hello.o(将汇编文件转换成二进制文件)
gcc hello.o -o hello(将二进制文件打包成可执行文件)
2.简单编译方式:
gcc hello.c -o hello(将hello.c文件直接生成可执行文件hello)
gcc hello.c(默认会将hello.c转换成可执行文件并命名为a.out)
二、GCC命令选项 -c 和 -o
(一)选项-o
1.点睛
选项-o用于指定要生成的结果文件,后面跟的就是结果文件,结果文件可是预处理文件、汇编文件、目标文件或最终可执行文件(o是output的意思)
2.示例生成.i文件
gcc -E test.c -o test.i
# -E 要求 只进行到预编译阶段(即生成.i文件)
# -o 说明 把生成的结果文件的文件名指定为了test.i
3.示例生成.s文件(汇编语言文件)
gcc -S test.i -o test.s
# -S 要求 只进行到编译阶段(即生成.s文件)
# -o 说明 把生成的结果文件的文件名指定为test.s
4.示例生成.o文件(目标文件:二进制机器指令)
gcc -c test.cpp -o test
# -c 要求 只进行到汇编阶段(即生成.o文件)
# -o 说明 把生成的结果文件的文件名指定为test
# test是目标文件,不是可执行文件(因这里用的-c,是告诉gcc到汇编为止,不链接)
5.示例生成可执行文件
gcc test.c -o test
# gcc命令与test.c之间没有其他选项 说明直接进行到链接阶段(生成目标文件阶段)
# -o 说明 把生成的结果文件的文件名指定为test
# test是可执行文件(因这里使用的是不带任何选项的gcc命令,相当于一步到位)
(二)选项-c
1.点睛
选项-c要求gcc对源文件进行到汇编阶段,但不进行链接。此时,将生成目标文件(.o文件)。
2.当使用-c并不使用-o时,示例:
[root@localhost temp]# gcc -c test.c
# 不指定输出文件的文件名时,会生成同名的.o文件(即生成目标文件test.o)
[root@localhost temp]# ll
total 8
-rw-r--r--. 1 root root 188 Mar 10 11:00 test.c
-rw-r--r--. 1 root root 1504 Mar 10 11:00 test.o
3. 当使用-c且使用-o时,示例:
[root@localhost temp]# gcc -c test.c -o test
# 指定输出文件的文件名时,生成的.o文件的文件名按所要求的来(test与test.o的内容完全相同)
4.-c后跟多源文件情况
当-c后跟多个源文件,会为每个源文件生成一个.o文件,但此时不能使用“-o”选项来指定输出文件文件名
三、linux下C的编译的四个步骤——预处理、编译、汇编和链接
(一)预编译:
首先预处理,完成预替换、去注释、头文件按照路径展开、以及条件编译。
建立一个test.c文件。里面写入注释、宏定义、头文件、条件编译。如下图:
使用的gcc命令是:gcc -E test.c -o test.i
#gcc -E的作用是将.c文件转化成.i文件
使用vi命令打开test.i:
此时我们发现宏定义已被替换,处理所有条件预编译指令,处理“#include”预编译指令,注释也已经被删除。
(二)编译:
把预处理完的文件进行一系列词法分析,语法分析,语义分析及生成相应的汇编代码。
使用的gcc命令是:gcc –S tets.i -o test.s
#gcc -S的作用是将.i文件转化成.s文件
打开test.s文件
此时我们发现,编译已经把C语言转化为汇编语言了。
(三)汇编:
将汇编代码转变成机器可执行的指令。
使用的gcc命令是:gcc –c test.s -o test.o
#gcc -S的作用是将.s文件转化成.o文件
打开test.o文件
通过上图,我们可以发现“汇编”实际上指“把汇编语言代码翻译成目标机器指令的过程“。
目标文件中所存放的,即与源代码等效的目标的机器语言代码。汇编程序生成的目标文件实际上是可重定位文件,它其中包含有适合于其它目标文件链接来创建一个可执行的或者共享的目标文件的代码和数据。
(四)链接:
生成exe可执行程序。
使用的gcc命令是:gcc test.o -o test
# 即若使用gcc时不加任何的选项,则表明进行的是生成可执行文件的过程
# 这样可以得到计算机能读懂的二进制文件
打开test.exe文件
四、Linux下C语言编程实战
(一)通常我们建立一个程序的基本步骤如下:
标志-c表示gcc在汇编阶段完成后就停止,即会生成目标文件main.o
(二)实例1:
- #include <stdio.h>
- int odd(int n){
- if(1 == n%2) return 1;
- return 0;
- }
上述代码存在ex_1.c中,则:
hyh@ubuntu:~$ls
ex_1.c
hyh@ubuntu:~$gcc -c ex_1.c
#-c表明只进行到汇编阶段,无-o表明最终生成同名.o目标文件
hyh@ubuntu:~$ls
ex_1.c ex_1.o
#ex_1.o目标代码文件包含了机器指令,即可以处理机器上运行的指令,但一个目标代码是不能直接被执行的,必须经过链接操作变成一个可执行代码程序。而链接可以是多个目标代码组织在一起安排它们形成一个可执行文件,目标代码可以来自多个源代码文件。
(三)实例2:
- #include <stdio.h>
- int odd(int n);
- int main(){
- int i;
- while(1){
- printf(">");
- scanf("%d", &i);
- if(odd(i)) printf("%d is odd number!\n", i);
- else printf("%d is not odd number!\n", i);
- }
- return 0;
- }
上面代码保存为ex_2.c,下面来进行编译和链接:
hyh@ubuntu:~$gcc -c ex_1.c
#-c表明只进行到汇编阶段,无-o表明最终生成同名.o目标文件
hyh@ubuntu:~$gcc -c ex_2.c
#-c表明只进行到汇编阶段,无-o表明最终生成同名.o目标文件
hyh@ubuntu:~$ls
ex_1.c ex_1.o ex_2.c ex_2.o
hyh@ubuntu:~$gcc ex_1.o ex_2.o
# 若使用gcc时不加任何选项,则表明最终直接生成可执行文件
# 若使用gcc时不加任何选项,且不使用-o选项,则默认生成a.out文件
hyh@ubuntu:~$ls
a.out ex_1.c ex_1.o ex_2.c ex_2.o
#建立了可执行文件a.out
hyh@ubuntu:~$./a.out
#运行可执行文件时,必须指明路径
>3
3 is odd number!
>4
4 is not odd number!
注:本文由H同志编写,欢迎批评指正、交流探讨