📄 0520source_code_and_tarball.htm
字号:
</pre></td></tr></table>
那要如何编译这支程式呢?我们先直接编译看看∶<br>
<table class="term"><tr><td class="term"><pre>
[root@linux ~]# <span class=term_command>gcc sin.c</span>
sin.c: In function 'main':
sin.c:5: warning: incompatible implicit declaration of built-in function 'sin'
/tmp/cciDlilg.o(.text+0x2c): In function `main':
sin.c: <b>undefined reference to `sin'</b>
collect2: ld returned 1 exit status
<span class=term_say># 注意看到上面最后一行,会有个错误讯息,代表没有成功!</span>
</pre></td></tr></table>
特别注意上面的错误讯息,唉啊!怎么没有编译成功?它说的是『<span class=text_import2>undefined
reference to sin</span>』,说的是『<span class=text_import2>没有
sin 的相关定义参考值!</span>』,为什么会这样呢?这是因为
C 语言里面的 sin 函示是写在 libm.so 这个函式库中,而我们并没有在原始码里面加入相关的说明,
所以当然就需要在编译与连结的时候将这个函式库给他连结进执行档里面啊!所以我们可以这样做∶<br>
<table class="term"><tr><td class="term"><pre>
[root@linux ~]# <span class=term_command>gcc sin.c -lm -L/lib -L/usr/lib</span>
<span class=term_say># 特别注意,那个 -lm 可以拆开成两部份来看∶
# -l ∶是『加入某个函式库(library)』的意思,而
# m ∶则是 libm.so 这个函式库,其中, lib 与附档名(.a 或 .so)不需要写
# 所以 -lm 表示使用 libm.so (或 libm.a) 这个函式库的意思~
# 至于那个 -L 后面接的路径呢?这表示∶
#『<span class=term_note_b>我要的函式库 libm.so 请到 /lib 或 /usr/lib 里面搜寻</span>!』</span>
[root@linux ~]# <span class=term_command>./a.out</span>
1.000000
</pre></td></tr></table>
上面的说明很清楚了吧!!不过,要注意的是,由于
Linux 预设是将函式库放置在 /lib 与 /usr/lib 当中,所以您没有写 -L/lib 与
-L/usr/lib 也没有关系的!不过,万一哪天您使用的函式库并非放置在这两个目录下,那么
-L/path 就很重要了!否则会找不到函式库喔!<br><br>
除了连结的函式库之外,您或许已经发现一个奇怪的地方,那就是在我们的
sin.c 当中第一行『<span class=text_import2><font face="细明体"> #include
<stdio.h></span></font>』,这行说的是要将一些定义资料由
stdio.h 这个档案读入,这包括 printf 的相关设定。这个档案其实是放置在 /usr/include/stdio.h
的!那么万一这个档案并非放置在这里呢?那么我们就可以使用底下的方式来定义出要读取的
include 档案放置的目录∶<br>
<table class="term"><tr><td class="term"><pre>
[root@linux ~]# <span class=term_command>gcc sin.c -lm -I/usr/include</span>
</pre></td></tr></table>
-I/path 后面接的路径( Path )就是设定要去搜寻相关的
include 档案的目录啦!不过,同样的,预设值是放置在 /usr/include 底下,除非您的
include 档案放置在其他路径,否则也可以略过这个项目!<br><br>
透过上面的几个小范例,您应该对于 gcc 以及原始码有一定程度的认识了,再接下来,我们来稍微整理一下
gcc 的简易使用方法吧!<br><br>
</div>
<hr><a NAME="simple_ex_gcc"></a><img src="../images/penguin-s.gif" alt="小标题的图示" height="23" width="16" align="middle" /><span class=text_h2>gcc 的用法</span>
<div class=block2>
前面说过, gcc 为 Linux 上面最标准的编译器,这个 gcc 是由
<a href="http://www.gnu.org/" target="_blank">GNU</a>
所维护的,有兴趣的朋友请自行前往参考。既然 gcc 对于 Linux 上的 Open source
是这么样的重要,所以底下我们就列举几个 gcc 常见的参数,如此一来大家应该更容易了解原始码的各项功能吧?!<br>
<table class="term"><tr><td class="term"><pre>
<span class=term_hd># 仅将原始码编译成为目标档,并不制作连结等功能∶</span>
[root@linux ~]# <span class=term_command>gcc -c hello.c</span>
<span class=term_say># 会自动的产生 hello.o 这个档案,但是并不会产生 binary 执行档。</span>
<span class=term_hd># 在编译的时候,依据作业环境给予最佳化执行速度</span>
[root@linux ~]# <span class=term_command>gcc -O hello.c -c</span>
<span class=term_say># 会自动的产生 hello.o 这个档案,并且进行最佳化喔!</span>
<span class=term_hd># 在进行 binary file 制作时,将连结的函式库与相关的路径填入</span>
[root@linux ~]# <span class=term_command>gcc sin.c -lm -L/usr/lib -I/usr/include</span>
<span class=term_say># 这个指令较常下达在最终连结成 binary file 的时候,
# -lm 指的是 libm.so 或 libm.a 这个函式库档案;
# -L 后面接的路径是刚刚上面那个函式库的搜寻目录;
# -I 后面接的是原始码内的 include 档案之所在目录。</span>
<span class=term_hd># 将编译的结果输出成某个特定档名</span>
[root@linux ~]# <span class=term_command>gcc -o hello hello.c</span>
<span class=term_say># -o 后面接的是要输出的 binary file 档名</span>
<span class=term_hd># 在编译的时候,输出较多的讯息说明</span>
[root@linux ~]# <span class=term_command>gcc -o hello hello.c -Wall</span>
<span class=term_say># 加入 -Wall 之后,程式的编译会变的较为严谨一点,
# 所以警告讯息也会显示出来!</span>
</pre></td></tr></table>
比较重要的大概就是这一些。<span class=text_import2>另外,我们通常称
-Wall 或者 -O 这些非必要的参数为旗标( FLAGS ),因为我们使用的是 GCC ,所以有时候也会简称这些旗标为
CCFLAGS ,</span>这些变数偶尔会被使用的喔!尤其是在后头会介绍的
make 相关的用法时,更是重要的很呐! ^_^<br>
</div>
</div>
<hr><a NAME="make"></a><img src="images/penguin-m.gif" alt="大标题的图示" height="34" width="25" align="middle" /><span class=text_h1>make 的简易用法</span>
<div class=block1>
在前言的部分我们提到过 make 的功能是可以简化编译过程里面所下达的指令,
同时还具有很多很方便的功能!那么底下咱们就来试看看使用
make 简化下达编译指令的流程吧!<br><br>
<hr WIDTH="100%"><a NAME="make_why"></a><img src="../images/penguin-s.gif" alt="小标题的图示" height="23" width="16" align="middle" /><span class=text_h2>为什么要用 make</span>
<div class=block2>
先来想像一个案例,假设我的执行档里面包含了四个原始码档案,分别是
main.c haha.c sin_value.c cos_value.c 这四个档案,这四个档案您可以到
<a href="http://linux.vbird.org/linux_basic/0520source/main.tgz"
>http://linux.vbird.org/linux_basic/0520source/main.tgz</a>
来下载,由于这四个档案里面包含了相关性,并且还用到数学函式在里面,
所以如果您想要让这个程式可以跑,那么就需要这样编译∶<br>
<table class="term"><tr><td class="term"><pre>
[root@linux ~]# <span class=term_command>gcc -c main.c</span>
[root@linux ~]# <span class=term_command>gcc -c haha.c</span>
[root@linux ~]# <span class=term_command>gcc -c sin_value.c</span>
[root@linux ~]# <span class=term_command>gcc -c cos_value.c</span>
<span class=term_say># 先以上面的动作制作出四个目标档,然后再进行下面的动作∶</span>
[root@linux ~]# <span class=term_command>gcc -o main main.o haha.o sin_value.o cos_value.o \</span>
> <span class=term_command>-lm -L/usr/lib -L/lib</span>
<span class=term_say># 这样就可以制作出 main 这个执行档棉!执行看看吧!</span>
[root@linux ~]# <span class=term_command>./main </span>
HaHa! I'm the King of the world
0.706825
0.707388
</pre></td></tr></table>
呵呵!要做好多动作啊!真是麻烦,如果可以的话,能不能一个步骤就给他完成上面所有的动作呢?
试看看在这个目录下建立一个 makefile 档案,内容如下∶<br>
<table class="term"><tr><td class="term"><pre>
<span class=term_hd># 1. 先建立编译的规则</span>
[root@linux ~]# <span class=term_command>vi makefile</span>
<span class=term_write>main: main.o haha.o sin_value.o cos_value.o
gcc -o main main.o haha.o sin_value.o cos_value.o -lm</span>
<span class=term_say># 注意∶第二行的 gcc 之前是 <tab> 按键产生的空格喔!</span>
<span class=term_hd># 2. 尝试给他建立规则看看</span>
[root@linux ~]# <span class=term_command>rm -f main *.o <span class=term_note><==先将之前的目标档去除</span></span>
[root@linux ~]# <span class=term_command>make</span>
cc -c -o main.o main.c
cc -c -o haha.o haha.c
cc -c -o sin_value.o sin_value.c
cc -c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
<span class=term_say># 这个时候 make 会主动去读取 makefile 这个档案的内容,
# 并根据内容直接去给他编译起相关的执行档棉!</span>
<span class=term_hd># 3. 如果再执行一次 make 会怎样?!</span>
[root@linux ~]# <span class=term_command>make</span>
make: `main' is up to date.
<span class=term_say># 看到了吧?!是否很方便呢?!</span>
</pre></td></tr></table>
或许您会说∶『如果我建立一个 shell script 来将上面的所有动作都集结在一起,
不是具有同样的效果吗?』呵呵!效果当然不一样,以上面的测试为例,我们仅写出
main 需要的目标档,结果 make 会主动的去判断每个目标档相关的原始码档案,并直接予以编译,
最后再直接进行连结的动作!哈哈!真的是很方便啊!此外,如果我们更动过某些原始码档案,则
make 也可以主动的判断哪一个原始码与相关的目标档档案有更新过,
并仅更新该档案,如此一来,将可大大的节省很多编译的时间呢!要知道,某些程式在进行编译的行为时,会消耗很多的
CPU 资源呢!所以说, make 有这些好处∶<ul><span class=text_import2>
<li>简化编译时所需要下达的指令;
<li>若在编译完成之后,修改了某个原始码档案,则 make 仅会针对被修改了的档案进行编译,其他的
object file 不会被更动;
<li>最后可以依照相依性来更新( update )执行档。</ul></span>
既然 make 有这么多的优点,那么我们当然就得好好的了解一下
make 这个令人关心的家伙啦!而 make 里面最需要注意的大概就是那个规则档案,也就是
makefile 这个档案的语法啦!底下我们针对 makefile 的语法来加以介绍棉。<br><br>
</div>
<hr><a NAME="make_makefile"></a><img src="../images/penguin-s.gif" alt="小标题的图示" height="23" width="16" align="middle" /><span class=text_h2>make 的基本语法与变数</span>
<div class=block2>
make 的语法可是相当的多而复杂的,有兴趣的话可以到
<a href="http://www.gnu.org/software/make/manual/html_mono/make.html"
target="_blank">http://www.gnu.org/software/make/manual/html_mono/make.html</a>
去查阅相关的说明,鸟哥这里仅列出一些基本的规则,重点在于让读者们未来在接触原始码时,不会太紧张啊!
^_^好了,基本的 makefile 规则是这样的∶<br>
<table class="term"><tr><td class="term"><pre>
<span class=term_write>标的(target): 目标档1 目标档2
<tab> gcc -o 欲建立的执行档 目标档1 目标档2</span>
</pre></td></tr></table>
那个标的(target)就是我们想要建立的资讯,而目标档就是具有相关性的
object files ,那建立执行档的语法就是以 <tab> 按键开头的那一行!特别给他留意喔,
『<span class=text_import1>命令列必须要以 tab 按键作为开头</span>』才行!
他的规则基本上是这样的∶<ul><span class=text_import2>
<li>在 makefile 当中的 # 代表注解;
<li><tab> 需要在命令行的第一个字元;
<li>标的(target)与相依档案(就是目标档)之间需以『:』隔开。</ul></span>
同样的,我们以刚刚上一个小节的范例进一步说明,如果我想要有两个以上的执行动作时,
例如下达一个指令就直接清除掉所有的目标档与执行档,该如何制作呢?<br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -