【gcc和make】


一、程序中bug分类

1.编译时错误

编译器只能翻译语法正确的程序,否则将导致编译失败,无法生成可执行文件。对于自然语言来说,一点语法错误不是很严重的问题,因为我们仍然可以读懂句子。而编译器就没那么宽容了,哪怕只有一个很小的语法错误,编译器就会输出一条错误提示信息然后罢工,你就得不到你想要的结果。

虽然大部分情况下编译器给出的错误提示信息就是你出错的代码行,但也有个别时候编译器给出的错误提示信息帮助不大,甚至会误导你。在开始学习编程的前几个星期,你可能会花大量的时间来纠正语法错误。等到有了一些经验之后,还是会犯这样的错误,不过会少得多,而且你能更快地发现错误原因。等到经验更丰富之后你就会觉得,语法错误是最简单最低级的错误,编译器的错误提示也就那么几种,即使错误提示是有误导的也能够立刻找出真正的错误原因是什么。相比下面两种错误,语法错误解决起来要容易得多。

2.运行时错误

编译器检查不出这类错误,仍然可以生成可执行文件,但在运行时会出错而导致程序崩溃。对于我们接下来的几章将编写的简单程序来说,运行时错误很少见,到了后面的章节你会遇到越来越多的运行时错误。

3.逻辑错误和语义错误

第三类错误是逻辑错误和语义错误。如果程序里有逻辑错误,编译和运行都会很顺利,看上去也不产生任何错误信息,但是程序没有干它该干的事情,而是干了别的事情。

不管怎么样,计算机只会按你写的程序去做,问题在于你写的程序不是你真正想要的。这意味着程序的意思(即语义)是错的。

找到逻辑错误在哪儿需要十分清醒的头脑,要通过观察程序的输出回过头来判断它到底在做什么。

二、安装GCC

GCC(GNU Compiler Collection,GNU编译器集合)是一套由GNU开发的编程语言编译器。它是一套GNU编译器套装。以GPL许可证所发行的自由软件,也是GNU计划的关键部分。GCC原本作为GNU操作系统的官方编译器,现已被大多数类UNIX操作系统(如Linux、BSD、Mac OS X等)采纳为标准的编译器。GCC同样适用于微软的Windows。GCC是自由软件过程发展中的著名例子,由自由软件基金会以GPL协议发布。

GCC原名为GNU C语言编译器(GNU C Compiler),因为它原本只能处理C语言。但GCC后来得到扩展,变得既可以处理C++,又可以处理Fortran、Pascal、Objective-C、Java,以及Ada与其他语言。

安装步骤如下:
(1)先检查是否安装

[root@RHEL7-2 ~]# rpm  -qa|grep  gcc
compat-libgcc-296-2.96-138
libgcc-4.1.2-46.el5
gcc-4.1.2-46.el5
gcc-c++-4.1.2-46.el5
上述结果表示已经安装了GCC

(2)若没有安装,先挂载

//挂载光盘到 /iso下
 [root@RHEL7-2 ~]# mount  /dev/cdrom  /iso

(3)制作用于安装的yum源文件

[root@RHEL7-2 ~]# vim  /etc/yum.repos.d/dvd.repo
# /etc/yum.repos.d/dvd.repo 
# or for ONLY the media repo, do this: 
# yum --disablerepo=\* --enablerepo=c6-media [command] 
[dvd] 
name=dvd 
baseurl=file:///iso 
gpgcheck=0 
enabled=1 

(4)使用yum命令安装GCC

[root@RHEL7-2 ~]# yum clean all 		//安装前先清除缓存
[root@RHEL7-2 ~]# yum  install  gcc  -y

三、GCC编译

1.一个源程序

编写一个“Hello World”C语言文件。

[root@RHEL7-2 ~]# vim  hello.c   <==用 C 语言写的程序扩展名建议用.c
#include <stdio.h>
int main(void)
{
        printf("Hello World\n");
}

GCC格式:
gcc [options] [filenames]
常用参数:
-c :只编译,不链接为可执行文件。编译器由输入的.c等源代码文件生成.o为后缀的目标文件。
-o output_filename:确定输出文件的名称为output_filename。同时这个名称不能和源文件同名,如果不给出这个选项,gcc就给出默认的可执行文件a.out。
-O :对程序进行优化编译、链接。采用这个选项,整个源代码会在编译、链接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是编译、链接的速度就相应地要慢一些。
-Wall :使 gcc 产生尽可能多的警告信息。不仅可以帮助程序员写出更加健壮的程序,而且还是跟踪和调试程序的有力工具。

gcc编译不加参数时,生成a.out的可执行文件,如下:

[root@RHEL7-2 ~]# gcc  hello.c
[root@RHEL7-2 ~]# ll  hello.c  a.out
-rwxr-xr-x. 1 root root 8512 Jul 15 21:18 a.out <==此时会生成这个文件名
-rw-r--r--. 1 root root   72 Jul 15 21:17 hello.c 
[root@RHEL7-2 ~]# ./a.out
Hello World  <==成果出现了!

可以使用参数指定输出可执行文件名称,先生成目标文件,再链接生成hello可执行文件:

[root@RHEL7-2 ~]# gcc  -c  hello.c
[root@RHEL7-2 ~]# ll  hello*
-rw-r--r--. 1 root root   72 Jul 15 21:17 hello.c
-rw-r--r--. 1 root root 1496 Jul 15 21:20 hello.o  <==这就是生成的目标文件

[root@RHEL7-2 ~]# gcc  -o  hello  hello.o
[root@RHEL7-2 ~]# ll  hello*
-rwxr-xr-x. 1 root root 8512 Jul 15 21:20 hello <==这就是可执行文件(-o 的结果)
-rw-r--r--. 1 root root   72 Jul 15 21:17 hello.c
-rw-r--r--. 1 root root 1496 Jul 15 21:20 hello.o

[root@RHEL7-2 ~]# ./hello
Hello World

2.一个程序调用另一个程序

有如下两个程序,在thanks.c中调用thanks_2.c文件:

[root@RHEL7-2 ~]# vim  thanks.c
#include <stdio.h>
int main(void)
{
        printf("Hello World\n");
        thanks_2();
}

[root@RHEL7-2 ~]# vim  thanks_2.c
#include <stdio.h>
void thanks_2(void)
{
        printf("Thank you!\n");
}

接下来先编译生成两个c语言文件对应的目标文件,然后链接生成一个thanks的可执行文件。

(1)开始将源码编译成为可执行的 binary file。
[root@RHEL7-2 ~]# gcc  -c  thanks.c  thanks_2.c
[root@RHEL7-2 ~]# ll  thanks*
-rw-r--r--. 1 root root   76 Jul 15 21:27 thanks_2.c
-rw-r--r--. 1 root root 1504 Jul 15 21:27 thanks_2.o <==编译生成的目标文件!
-rw-r--r--. 1 root root   91 Jul 15 21:25 thanks.c
-rw-r--r--. 1 root root 1560 Jul 15 21:27 thanks.o <==编译生成的目标文件!
[root@RHEL7-2 ~]# gcc -o thanks thanks.o thanks_2.o
[root@RHEL7-2 ~]# ll thanks*
-rwxr-xr-x. 1 root root 8584 Jul 15 21:28 thanks   <==最终结果会生成可执行文件

(2)执行可执行文件。
[root@RHEL7-2 ~]# ./thanks
Hello World
Thank you!

3.调用外部函数库

在Linux下开发软件时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助一个或多个函数库的支持才能够完成相应的功能。

从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(.so 或 .a)的集合。虽然Linux下的大多数函数都默认将头文件放到 /usr/include/ 目录下,而库文件则放到 /usr/lib/ 目录下,但并不是所有的情况都是这样。正因如此,gcc 在编译时必须有自己的办法来查找所需要的头文件和库文件。常用的方法有:
-I :可以向 gcc 的头文件搜索路径中添加新的目录。
-L :如果使用了不在标准位置的库文件,那么可以通过 -L 选项向 gcc 的库文件搜索路径中添加新的目录。
-l:加入某个函数库。
-m :libm.so函数库。-lm表示使用libm.so(或libm.a)这个函数库,如果-L后面加上路径,表示程序要到该路径中找函数库,Linux默认是将函数库放置在/lib与/usr/lib中的,可以省略。

有如下调用三角函数外部函数库的程序:

[root@RHEL7-2 ~]# vim  sin.c
#include <stdio.h>
int main(void)
{
        float value;
        value = sin ( 3.14 / 2 );
        printf("%f\n",value);
}

直接进行编译,会报错,因为编译的时候没有把对应的函数库加进去,可以通过编译时加入额外函数库链接的方式。

[root@RHEL7-2 ~]# gcc sin.c –lm –L/lib –L/usr/lib
[root@RHEL7-2 ~]# ./a.out

特别注意,使用GCC编译时所加入的那个-lm是有意义的,可以拆成两部分来分析。
-l:是加入某个函数库(library)的意思。
-m:是libm.so函数库。
所以-lm表示使用libm.so(或libm.a)这个函数库的意思。

-L后面接的路径表示程序需要的函数库libm.so请到/lib或/usr/lib里面寻找。
由于Linux默认将函数库放置在/lib与/usr/lib当中,所以即便没有写-L/lib与-L/usr/lib也没有关系。不过,当使用的函数库并非放置在这两个目录下,那么-L/path就很重要了,否则找不到函数库。
-I/path后面接的路径(Path)就是设置要去寻找相关的include文件的目录。不过,同样,默认值是放置在/usr/include下面,除非你的include文件放置在其他路径,否则也可以略过这个选项。

四、make宏编译

当我们有太多源码文件的时候,编译链接是一件非常麻烦的事情,make是一个批处理的思想,通过在makefile文件中编写相应的目标命令,然后通过make编译即可。

(1)制作makefile文件

基本的 makefile守则:
目标(target): 目标文件1 目标文件2
<tab> gcc -o 欲创建的可执行文件 目标文件1 目标文件2

1.目标(target)就是我们想要创建的信息,而目标文件就是具有相关性的 object files ,那创建可执行文件的语法就是以按“Tab”键开头的那一行,要特别留意,命令列必须要以按“Tab”键作为开头才行。
2.在makefile当中的 # 代表注解。
3.需要在命令行(例如gcc这个编译器命令)的第一个字节按“Tab”键。
4.目标(target)与相关文件(就是目标文件)之间需以“:”隔开。

(2)make编译

make [目标]

例:
4个源码文件,分别是main.c、haha.c、sin_value.c和cos_value.c这4个文件,这4个文件的功能如下所示。
l main.c:主要的目的是让用户输入角度数据与调用其他3个子程序。
l haha.c:输出一堆信息。
l sin_value.c:计算用户输入的角度(360)正弦数值。
l cos_value.c:计算用户输入的角度(360)余弦数值。

[root@rhel7-2 c]# cat main.c
#include <stdio.h>

#define pi 3.14159
char name[15];
float angle;
int main(void)
{	printf ("\n\nPlease input your name: ");
	scanf  ("%s", &name );
	printf ("\nPlease enter the degree angle (ex> 90): " );
	scanf  ("%f", &angle );
	haha(name);
	sin_value(angle);
	cos_value(angle);
}
[root@rhel7-2 c]# cat haha.c
#include <stdio.h>
int haha(char name[15])
{	printf ("\n\nHi, Dear %s, nice to meet you.", name);
}
[root@rhel7-2 c]# cat sin_value.c
#include <stdio.h>
#define pi 3.14159
float angle;
void sin_value(void)
{	float value;
	value = sin ( angle / 180. * pi );
	printf ("\nThe Sin is: %5.2f\n",value);
}
[root@rhel7-2 c]# cat cos_value.c
#include <stdio.h>
#define pi 3.14159
float angle;
void cos_value(void)
{
	float value;
	value = cos ( angle / 180. * pi );
	printf ("The Cos is: %5.2f\n",value);
}

先编辑makefile来建立新的规则,这里除了写一个main目标,还额外写了一个clean目标,因为每次生成完目标文件和可执行文件之后,再次去执行main就需要先清除一下原来的,才能更清楚判断下一次执行有没有成功,因此写了一个clean清除文件的目标,方便删除。

[root@RHEL7-2 ~]# vim  makefile
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
clean:
      rm -f main main.o haha.o sin_value.o cos_value.o

接下来进行make编译

make main  #执行main的命令
make clean  # 执行clean对应的命令
make clean main # 先清楚文件,再次编译

总结:gcc是GNU编译器套件,也可以简单认为是编译器,它可以编译很多种编程语言,比如说c、c++等。当程序只有一个源文件时,直接就可以用gcc命令编译它。但是当程序包含很多个源文件时,用gcc命令逐个去编译时,就会容易混乱且工作量大,出现了make工具。make工具可以看成一个批处理工具,它本身没有编译和链接功能,而是调用makefile文件中用户指定的命令来进行编译和链接,包含了调用gcc编译某个源文件的命令。

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值