单一程序:印出 HelloWorld
- 编辑程序代码,亦即原始码
[root@study ~]# vim hello.c <==用 C 语言写的程序扩展名建议用 .c
#include <stdio.h>
int main(void)
{
printf("Hello World
");
}
在预设的状态下,如果我们直接以 gcc 编译原始码,并且没有加上任何参数,则执行档的档名会被 自动设定为 a.out 这个文件名!所以妳就能够直接执行 ./a.out 这个执行档啦!那如果我想要产生目标文件 (object file) 来进行其他的动作,而且执行档的档名也不要用 预设的 a.out ,那该如何是好?其实妳可以将上面的第 2 个步骤改成这样:
[root@study ~]# gcc -c hello.c
[root@study ~]# ll hello*
-rw-r--r--. 1 root root 71 Sep 4 11:32 hello.c
-rw-r--r--. 1 root root 1496 Sep 4 11:34 hello.o <==就是被产生的目标文件
[root@study ~]# gcc -o hello hello.o
[root@study ~]# ll hello*
-rwxr-xr-x. 1 root root 8503 Sep 4 11:35 hello <==這就是可執行檔! -o 的結果
-rw-r--r--. 1 root root 71 Sep 4 11:32 hello.c
-rw-r--r--. 1 root root 1496 Sep 4 11:34 hello.o
[root@study ~]# ./hello
Hello World
这个步骤主要是利用 hello.o 这个目标文件制作出一个名为 hello 的执行文件,
主、子程序链接:子程序的编译
如果我们在一个主程序里面又呼叫了另一个子程序呢?这是很常见的一个程序写法, 因为可以简化整个程序的易读性!在底下的例子当中,我们以 thanks.c 这个主程序去呼叫 thanks_2.c 这个子程序,写法很简单:
- 撰写所需要的主、子程序
# 1. 编辑主程序:
[root@study ~]# vim thanks.c
#include <stdio.h>
int main(void)
{
printf("Hello World
");
thanks_2();
}
# 上面的 thanks_2(); 那一行就是呼叫子程序啦!
[root@study ~]# vim thanks_2.c
#include <stdio.h>
void thanks_2(void)
{
printf("Thank you!
");
}
- 进行程序的编译与链接 (Link)
# 2. 开始将原始码编译成为可执行的 binary file :
[root@study ~]# gcc -c thanks.c thanks_2.c
[root@study ~]# ll thanks*
-rw-r--r--. 1 root root 75 Sep 4 11:43 thanks_2.c
-rw-r--r--. 1 root root 1496 Sep 4 11:43 thanks_2.o <==编译产生的!
-rw-r--r--. 1 root root 91 Sep 4 11:42 thanks.c
-rw-r--r--. 1 root root 1560 Sep 4 11:43 thanks.o <==编译产生的!
[root@study ~]# gcc -o thanks thanks.o thanks_2.o
[root@study ~]# ll thanks*
-rwxr-xr-x. 1 root root 8572 Sep 4 11:44 thanks <==最终结果会产生这玩意儿
# 3. 执行一下这个文件:
[root@study ~]# ./thanks
Hello World
Thank you!
知道为什么要制作出目标文件了吗?由于我们的原始码文件有时并非仅只有一个文件,所以我们无法 直接进行编译。这个时候就需要先产生目标文件,然后再以连结制作成为 binary 可执行文件。另外, 如果有一天,你更新了 thanks_2.c 这个文件的内容,则你只要重新编译 thanks_2.c 来产生新的 thanks_2.o ,然后再以连结制作出新的 binary 可执行文件即可!而不必重新编译其他没有更动过的 原始码文件。
此外,如果你想要让程序在执行的时候具有比较好的效能,或者是其他的除错功能时, 可以在编译 的过程里面加入适当的参数,例如底下的例子:
[root@study ~]# gcc -O -c thanks.c thanks_2.c <== -O 为产生优化的参数
[root@study ~]# gcc -Wall -c thanks.c thanks_2.c
thanks.c: In function ‘main’:
thanks.c:5:9: 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]
}
^
# -Wall 为产生更详细的编译过程信息。上面的讯息为警告讯息 (warning) 所以不用理会也没有关系!
呼叫外部函式库:加入连结的函式库
刚刚我们都仅只是在屏幕上面印出一些字眼而已,如果说要计算数学公式呢?例如我们想要计算出三 角函数里面的 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
",value);
}
那要如何编译这支程序呢?我们先直接编译看看:
[root@study ~]# gcc sin.c
# 新的 GCC 会主动将函数抓进来给你用,所以只要加上 include <math.h> 就好了!
新版的 GCC 会主动帮你将所需要的函式库抓进来编译,所以不会出现怪异的错误讯息! 事实上, 数学函式库使用的是 libm.so 这个函式库,你最好在编译的时候将这个函式库纳进去比较好~另外要 注意, 这个函式库放置的地方是系统默认会去找的 /lib, /lib64 ,所以你无须使用底下的 -L 去加入 搜寻的目录! 而 libm.so 在编译的写法上,使用的是 -lm
- 编译时加入额外函式库连结的方式:
[root@study ~]# gcc sin.c -lm -L/lib -L/lib64 <==重点在 -lm
[root@study ~]# ./a.out <==尝试执行新文件!
1.000000
特别注意,使用 gcc 编译时所加入的那个 -lm 是有意义的,他可以拆开成两部份来看:
- -l :是『加入某个函式库(library)』的意思,
- m :则是 libm.so 这个函式库,其中, lib 与扩展名(.a 或 .so)不需要写
所以 -lm 表示使用 libm.so (或 libm.a) 这个函式库的意思~至于那个 -L 后面接的路径呢?这表示: 『我要的函式库 libm.so 请到 /lib 或 /lib64 里面搜寻!』
除了连结的函式库之外,你或许已经发现一个奇怪的地方,那就是在我们的 sin.c 当中第一行『 #include <stdio.h>』,这行说的是要将一些定义数据由 stdio.h 这个文件读入,这包括 printf 的相关设定。这个文件其实是放置在 /usr/include/stdio.h 的!那么万一这个文件并非放置在这里呢?那 么我们就可以使用底下的方式来定义出要读取的 include 文件放置的目录:
[root@study ~]# gcc sin.c -lm -I/usr/include
-I/path 后面接的路径( Path )就是设定要去搜寻相关的 include 文件的目录。不过,同样的,默认值 是放置在 /usr/include 底下,除非你的 include 文件放置在其他路径。
gcc 的简易用法 (编译、参数与链结)
底下我们就列举 几个 gcc 常见的参数,如此一来大家应该更容易了解原始码的各项功能吧!
# 仅将原始码编译成为目标文件,并不制作链接等功能:
[root@study ~]# gcc -c hello.c
# 会自动的产生 hello.o 这个文件,但是并不会产生 binary 执行档。
# 在编译的时候,依据作业环境给予优化执行速度
[root@study ~]# gcc -O hello.c -c
# 会自动的产生 hello.o 这个文件,并且进行优化喔!
# 在进行 binary file 制作时,将连结的函式库与相关的路径填入
[root@study ~]# gcc sin.c -lm -L/lib -I/usr/include
# 这个指令较常下达在最终连结成 binary file 的时候,
# -lm 指的是 libm.so 或 libm.a 这个函式库文件;
# -L 后面接的路径是刚刚上面那个函式库的搜寻目录;
# -I 后面接的是原始码内的 include 文件之所在目录。
# 将编译的结果输出成某个特定档名
[root@study ~]# gcc -o hello hello.c
# -o 后面接的是要输出的 binary file 檔名
# 在编译的时候,输出较多的讯息说明
[root@study ~]# gcc -o hello hello.o -Wall
# 加入 -Wall 之后,程序的编译会变的较为严谨一点,所以警告讯息也会显示出来!