# 使用传统程序语言进行编译的简单范例
通过上面的介绍之后,差不多能清楚一点概念了,本章以一个简单的程序范例来说明整个编译的过程
# 单一程序:打印 Hello World
TIP
请确保你的 Linux 已经安装了 gcc,如果没有安装先参考后续章节的 RPM 或则 yum 方式安装
# 编写程序代码
[root@study ~]# vim hello.c
#include <stdio.h>
int main(void){
printf("Hello word\n");
}
2
3
4
5
6
上面是用 C 语言的语法写成的一个程序文件,第一行的是声明信息
# 开始编译与测试执行
[root@study ~]# gcc hello.c
[root@study ~]# ll hello.c a.out
-rwxr-xr-x. 1 root root 8360 Apr 5 16:24 a.out # 产生这个文件名
-rw-r--r--. 1 root root 63 Apr 5 16:24 hello.c
[root@study ~]#./a.out
Hello word
# 执行成功了
2
3
4
5
6
7
8
在默认情况下,以 gcc 编译源码,没有加任何参数的情况下,执行文件的名称会以 a.out 名称出现。a.out 就是编译成功的可执行的 binary program
如果想要产生目标文件(object file)来进行其他的动作,而且自定义执行文件名
[root@study ~]# gcc -c hello.c
[root@study ~]# ll hello*
-rw-r--r--. 1 root root 63 Apr 5 16:24 hello.c
-rw-r--r--. 1 root root 1496 Apr 5 16:27 hello.o # 产生的目标文件
[root@study ~]# ll hello*
-rwxr-xr-x. 1 root root 8360 Apr 5 16:28 hello # 自定义文件名的可执行文件 -o
-rw-r--r--. 1 root root 63 Apr 5 16:24 hello.c
-rw-r--r--. 1 root root 1496 Apr 5 16:27 hello.o
[root@study ~]./hello
Hello word
2
3
4
5
6
7
8
9
10
11
12
# 主、子程序连接:子程序的编译
如果我们在一个主程序里面又调用了另一个字程序呢?下面的例子中以 thans.c 这个主程序去调用 thanks_2.c 这个子程序
# 编写主、子程序代码
# 1. 编写主程序
[root@study ~]# vim thanks.c
#include <stdio.h>
int main(void){
printf("Hello word\n");
thanks_2();
}
# 2. 编写子程序
[root@study ~]# vim thanks_2.c
#include <stdio.h>
int thanks_2(void){
printf("Thank you!\n");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 程序的编译与连接 Link
# 2. 将源码编译为颗执行的 binary file
[root@study ~]# gcc -c thanks.c thanks_2.c
[root@study ~]# ll thanks*
-rw-r--r--. 1 root root 76 Apr 5 16:33 thanks.c
-rw-r--r--. 1 root root 1560 Apr 5 16:35 thanks.o
-rw-r--r--. 1 root root 67 Apr 5 16:34 thanks_2.c
-rw-r--r--. 1 root root 1504 Apr 5 16:35 thanks_2.o
# *.o 文件是编译产生的
[root@study ~]# gcc -o thanks thanks.o thanks_2.o
[root@study ~]# ll thanks*
-rwxr-xr-x. 1 root root 8424 Apr 5 16:36 thanks
-rw-r--r--. 1 root root 76 Apr 5 16:33 thanks.c
-rw-r--r--. 1 root root 1560 Apr 5 16:35 thanks.o
-rw-r--r--. 1 root root 67 Apr 5 16:34 thanks_2.c
-rw-r--r--. 1 root root 1504 Apr 5 16:35 thanks_2.o
# 3. 执行
[root@study ~]# ./thanks
Hello word
Thank you!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
这里你就明白了,为什么要先制作出目标文件了,由于我们的源码文件有时并非只有一个文件,所以无法直接进行编译。这个时候就需要先产生目标文件,再以连结制作为 binary 可执行文件
另外,如果有一天,你更新了 thanks_2.c 这个文件的内容,则你只需要重新编译 thanks_2.c 来产生新的 thanks_2.o ,然后再以连结制作出新的 binary 可执行文件即可。这功能对于庞大的软件功能源码来说会很有用,节省很多的时间
此外,想要程序再执行的时候有比较好的效率,或则是其他的除错功能时,可以在编译的过程里加入适当的参数,如
# -O 产生优化的参数
[root@study ~]# gcc -O -c thanks.c thanks_2.c
# -Wall 产生更详细的编译过程信息
[root@study ~]# gcc -Wall -c thanks.c thanks_2.c
thanks.c: In function 'main':
thanks.c:5:2: warning: implicit declaration of function 'thanks_2' [-Wimplicit-function-declaration]
thanks_2();
^
thanks.c:6:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
thanks_2.c: In function 'thanks_2':
thanks_2.c:5:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
# 上面的信息为 warning 信息,所以可以忽略也没有关系
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
至于更多的 gcc 额外参数功能,请自行 man gcc
# 调用外部函数库:加入连结的函数库
比如要计算数学公式,计算三角函数里面的 sin(90 度角)。要注意的是,大多数程序语言都是使用径度,而不是一般我们计算的角度,180 度角约等于 3.14 径度
[root@study ~]# vim sin.c
# include <stdio.h>
# include <math.h>
int main(void){
float value;
value = sin(3.14 / 2);
printf("%f\n",value);
}
# 编译
[root@study ~]# gcc sin.c
2
3
4
5
6
7
8
9
10
11
12
新版的 GCC 会主动将你所需要的函数库抓进来编译,所以不会出现错误信息,事实上数据函数库使用的是 libm.so 这个函数库,最好在编译的时候将整个函数库链接进来比较好。
函数库放置的地方是系统默认会去找 /lib
、/lib64
,所以无须使用 -L
加入搜索的目录,而 libm.so 在编译的写法上,使用的是 -lm (lib 简写为 l)
,
# 编译时加入额外的函数库连结方式
[root@study ~]# gcc sin.c -lm -L/lib64
# 重点在 -lm,后面的 -L 是指定搜索的目录
[root@study ~]#./a.out
1.000000
2
3
4
-lm
是有意义的,可以拆开成两部分来看:
-l
:加入某个函数库(library)m
:libm.so 这个函数库,其中 lib 与扩展名 (.a 或 .so )不需要写
对于第一行的 stdio.sh
则是放在 /usr/include/stdio.sh
,如果不在这里的位置,那么久需要指明在哪里
gcc sin.c -lm -I/usr/include
# include 的路径指明格式为:-I/path
2
通过以上范例,对 gcc 与源码有一定程度的认识了,下面整理下 gcc 的简易使用方法
# gcc 的简易用法(编译、参数与连结)
下面列举几个常用的 gcc 常见的参数用法(详情请 man gcc)
# 仅将源码编译为目标文件,并不制作连结等功能
gcc -c hello.c
# 会自动产生 hello.o 文件,但是并不会产生 binary 执行文件
# 在编译时,根据作业环境给予优化执行速度
gcc -O hello.c -c
# 会自动产生 hello.o 文件,并进行优化
# 在进行 binary file 制作时,将连结的函数库与相关的路径填入
gcc sin.c -lm -L/lib -I/usr/include
# 该指令常下达在最终连结成 binary file 的时候
# -lm 指 libm.so 或 libm.a 这个函数库文件
# -L 后面接的路径是刚刚那个函数库的搜索目录
# -I 后面接的是源码内的 include 文件所在目录
# 将编译的结果输出到某个特定的文件
gcc -o hello hello.c
# -o 后面接的是要输出的 binary file 文件名
# 在编译的时候,输出较多的信息说明
gcc -o hello hello.c -Wall
# -Wall,程序的编译会变得较为严谨一点,所以警告信息也会显示出来
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
比较常用的如上,另外,通常称 -Wall
或 -O
这些非必要的参数为旗标 (FLAGS),因为使用的是 C 语言,所以有时候也会简称这些旗标为 CFLAGS,这些变量偶尔会被使用。
在后面的 make 相关用法时,用得较多