侧边栏壁纸
博主头像
张种恩的技术小栈博主等级

行动起来,活在当下

  • 累计撰写 748 篇文章
  • 累计创建 65 个标签
  • 累计收到 39 条评论

目 录CONTENT

文章目录

Linux软件安装(2)之使用传统程序语言进行编译的简单案例

zze
zze
2019-10-24 / 0 评论 / 0 点赞 / 579 阅读 / 5784 字

不定期更新相关视频,抖音点击左上角加号后扫一扫右方侧边栏二维码关注我~正在更新《Shell其实很简单》系列

经过前面的介绍之后,你应该比较清楚的知道源码文件、编译程序、函式库与可执行文件之间的相关性了。不过,详细的流程可能还是不很清楚,所以,在这里我们以一个简单的程序范例来说明整个编译的过程!赶紧进入 Linux 系统,实地的操作一下下面的的案例!

单一程序:印出Hello World

我们以 Linux 上最常见的 C 语言来编写第一个程序!第一个程序的作用就是在屏幕上面印出 Hello World 的字样。当然,这里我们是以简单的 C 语言来编写,好了,不啰嗦,开始吧!
请先确认你的 Linux 系统里面已经安装了 gcc 了。

如果你已经有网络了, 那么直接使用 yum groupinstall "Development Tools" 预安装好所需的所有软件即可。

编辑程序代码,亦即源代码

编辑 hello.c 文件,内容如下:

#include <stdio.h>
int main(void)
{
    printf("Hello World\n");
}

开始编译与测试执行

[root@localhost test]# gcc hello.c 
[root@localhost test]# ll
总用量 16
-rwxr-xr-x. 1 root root 8440 10月 24 15:53 a.out
-rw-r--r--. 1 root root   67 10月 24 10:44 hello.c
[root@localhost test]# ./a.out 
Hello World

我们直接以 gcc 编译源代码,并且没有加上任何参数,则执行档的档名会被自动设定为 a.out 这个文件名!所以你就能够直接执行 ./a.out 这个执行档啦!上面的例子很简单吧!那个 hello.c 就是原始码,而 gcc 就是编译程序,至于 a.out 就是编译成功的可执行 binary program。
咦!那如果我想要产生目标文件(object file)来进行其他的动作,而且执行档的档名也不要用预设的 a.out ,那该如何是好?其实你可以将上面的第 2 个步骤改成这样:

[root@localhost test]# gcc -c hello.c 
[root@localhost test]# ll
总用量 8
-rw-r--r--. 1 root root   67 10月 24 10:44 hello.c
-rw-r--r--. 1 root root 1496 10月 24 15:59 hello.o
[root@localhost test]# gcc -o hello.out hello.o 
[root@localhost test]# ll
总用量 20
-rw-r--r--. 1 root root   67 10月 24 10:44 hello.c
-rw-r--r--. 1 root root 1496 10月 24 15:59 hello.o
-rwxr-xr-x. 1 root root 8440 10月 24 15:59 hello.out
[root@localhost test]# ./hello.out 
Hello World

这个步骤主要是利用 hello.o 这个目标文件制作出一个名为 hello.out 的执行文件。这个动作后,我们可以得到 hello.outhello.o 两个文件,真正可以执行的是 hello.out 这个 binary program 喔~或许你会觉得, 咦!只要一个动作编译出 a.out 就好了,干嘛还要先制作目标文件再做成可执行文件呢?看完下个示例,你就可以知道为什么啦!

主、子程序链接:子程序的编译

如果我们在一个主程序里面又呼叫了另一个子程序呢?这是很常见的一个程序写法,因为可以简化整个程序的易读性。在底下的例子当中,我们以 thanks.c 这个主程序去呼叫 thanks_2.c 这个子程序,写法很简单:

撰写所需要的主、子程序

编写主程序 thanks.c

#include <stdio.h>
int main(void)
{
	printf("Hello World\n");
	thanks_2();
}

编写子程序 thanks_2.c

#include <stdio.h>
void thanks_2(void)
{
	printf("Thank you!\n");
}

开始将源码文件编译成可执行的 binary file:

[root@localhost test1]# gcc -c thanks.c thanks_2.c 
[root@localhost test1]# ll
总用量 16
-rw-r--r--. 1 root root   68 6月   5 2009 thanks_2.c
-rw-r--r--. 1 root root 1504 10月 24 16:08 thanks_2.o
-rw-r--r--. 1 root root   77 4月   8 2004 thanks.c
-rw-r--r--. 1 root root 1560 10月 24 16:08 thanks.o
[root@localhost test1]# gcc -o thanks.out thanks.
thanks.c  thanks.o  
[root@localhost test1]# gcc -o thanks.out thanks.o thanks_2.o 
[root@localhost test1]# ll
总用量 28
-rw-r--r--. 1 root root   68 6月   5 2009 thanks_2.c
-rw-r--r--. 1 root root 1504 10月 24 16:08 thanks_2.o
-rw-r--r--. 1 root root   77 4月   8 2004 thanks.c
-rw-r--r--. 1 root root 1560 10月 24 16:08 thanks.o
-rwxr-xr-x. 1 root root 8512 10月 24 16:09 thanks.out

执行生成的可执行文件 thanks.out

[root@localhost test1]# ./thanks.out 
Hello World
Thank you!

知道为什么要制作出目标文件了吗?由于我们的源代码文件有时并非仅只有一个,所以我们无法直接进行编译。这个时候就需要先产生目标文件,然后再以链接制作成为 binary 可执行文件。另外,如果有一天,你更新了 thanks_ 2.c 这个文件的内容,则你只要重新编译 thanks_ _2.c 来产生新的 thanks_2.o,然后再以链接制作出新的 binary 可执行文件即可!而不必重新编译其他没有更动过的原始码文件。这对于软件开发者来说,是一个很重要的功能,因为有时候要将偌大的原始码全部编译完成,会花很长的一段时间呢!
此外,如果你想要让程序在执行的时候具有比较好的效能,或者是其他的除错功能时,可以在编译
的过程里面加入适当的参数,例如底下的例子: .

调用外部函式库:加入链接的函式库

刚刚我们都仅只是在屏幕上面印出一些字眼而已,如果说要计算数学公式呢?例如我们想要计算出三角函数里面的 sin(90 度角)。要注意的是,大多数的程序语言都是使用径度而不是一般我们在计算的角度,180度角约等于 3.14 径度。那我们就来写一下这个程序吧!
编写 sin.c 文件:

#include <stdio.h>
#include <math.h>
int main(void)
{
	float value;
	value = sin ( 3.14 / 2 );
	printf("%f\n",value);
}

那要如何编译这支程序呢?我们先直接编译看看:

[root@localhost test2]# gcc sin.c

新版的 GCC 会主动帮你将所需要的函式库抓进来编译,所以不会出现怪异的错误信息!事实上,数学函式库使用的是 libm.so 这个函式库,你最好在编译的时候将这个函式库纳进去比较好~另外要注意,这个函式库放置的地方是系统默认会去找的 /lib/lib64,所以你无须使用底下的 -L 去加入搜寻的目录。而 libm.so 在编译的写法上,使用的是 -lmlib 简写为 l)因此就变成:

编译时加入额外函式库链接的方式

[root@localhost test2]# gcc sin.c -lm -L/1ib -L/1ib64
[root@localhost test2]# ./a.out 
1.000000

所以 -lm 表示使用 libm.so(或 libm.a)这个函式库的意思,至于那个 -L 后面接的路径呢?这表示要的函式库 libm.so 请到 /lib/lib64 里面搜寻。
上面的说明很清楚了吧,不过要注意的是,由于 Linux 默认是将函式库放置在 /lib/lib64 当中,所以你没有写 -L/lib-L/lib64 也没有关系的。不过,万一哪天你使用的函式库并非放置在这两个目录下,那么 -L/path 就很重要了!否则会找不到函式库。
你或许已经发现一个奇怪的地方,那就是在我们的 sin.c 当中第一行 #include <stdio.h>,这行说的是要将一些定义数据由 stdio.h 这个文件读入,这些数据包括 printf 的相关设定。这个文件其实是放置在 /usr/include/stdio.h 的,那么万一这个文件并非放置在这里呢?那么我们就可以使用下面的方式来定义出要读取的 include 文件放置的目录:

gcc sin.c -lm -I/usr/include

-Ipath 后面接的路径( Path )就是设定要去搜寻相关的 include 文件的目录啦!不过,同样的,默认值是放置在 /usr/include 底下,除非你的 include 文件放置在其他路径,否则也可以略过这个环节。

0

评论区