我们在 Windows 下使用 C 语言开发的时候,一般都会有支持 Windows 的开发工具,比如我们学习 51 单
片机或者 STM32,所使用的的 Keil 开发软件。此类开发工具一般都会集编辑、编译于一体,我们只需要编辑
好代码,电机开发工具的编译按钮,就可以自动为我们编译出可执行的二进制文件了。Ubuntu 下的 C 语言
开发与 Windows 下的 C 语言开发是不一样的,并没有图形界面的开发工具,而且编辑和编译是分开的。我
们需要使用文本编辑软件先编写代码,然后使用编译工具(GCC)来编译,最终生成可执行的二进制文件。
如果我们的工程中有多个源文件,在编译的时候我们通常会通过 Makefile 文件来管理整个工程的源文件。
本章我们来学习如何在 Ubuntu 下进行 C 语言成的编辑,GCC 编译,Makefile 文件的使用。通过本章学习我
们可以掌握 Linux 下的 C 语言开发基本方法,为后面的学习做好准备。
3.1 x Linux 下编写第一个 C C 程序 程序
本章节开始的部分我们介绍了 Ubuntu 下 C 程序开发分成两部分:编辑和编译。Ubuntu 下有很多种文本
编辑的工具,如 vim、Gedit、Emacs,这里我们使用前面章节介绍的 vim 文本编辑工具来编辑 C 程序。相信
大家在学习 C 语言的时候都是从“Hello World”这个程序开始的吧,下面我们通过这个程序,来学习下 Linux
下 C 程序开发的流程。
3.1.1 代码编写
首先我们在用户根目录下建立文件夹“work”,用于保存所有 C 语言的程序,运行结果如下图所示:
然后进入创建的文件夹 work,为了方便管理,我们每个例程都创建单独的文件夹,首先我们创建文件
夹“hello_world”来保存我们的第一个 C 程序,如下图所示:
然后进入上图中的 hello_world 文件夹,使用 vi 命令新建文件“main.c”,然后在里面输入下面的代
码:
#include
int mian(int argc, char *argv[])
{
printf("Hello World!\n");
return 0;
}
编写完以后保存并退出 vim 编辑器,然后可以使用 cat 命令查看代码是否保存成功,如下图所示:
通过上图可以看到代码已经编辑完成了。
3.1.2 代码编译
Ubuntu 下使用 gcc 编译器来编译 C 程序,我们在安装 Ubuntu 系统的时候,gcc 编译器 morning 安装好
了,我们可以在终端输入“gcc -v”来查看下 gcc 的版本,如下图所示:
通过上图可以看到 gcc 的版本信息,说明我们的 Ubuntu 上已经安装了 gcc 编译器了,下面我们来看看
怎么通过 gcc 编译我们的第一个 C 程序,我们在终端输入“gcc main.c -o main”,然后回车,会生成 main 文
件,如下图所示:
在上面的“gcc main.c -o main”这个命令里面“-o“用来指定编译生成的文件名字,我们指定的是 main,
所以在图 3.1.2.2 中可以看到生成了文件“main”,如果我们想生成其它的文件名字,我们只需要修改“-o”
后面的 main(修改成您希望生成的文件名字)。经过前面的步骤已经生成了可执行文件 main,现在我们演
示下在终端如何运行一个可行性的程序,我们直接在终端当前目录下(可执行文件所在的目录下)输入
“./main”,然后回车,就可以运行可执行文件 main 了,运行结果如下图所示:
在上图中运行的命令“./main”,其中的“./”代表当前目录下。我们可以看到在终端打印出了 Hello
World!。至此,Linux 下的 C 语言编辑和编译的一整套流程我们就介绍完了。
3.2 c gcc 编译器 编译器
3.2.1 gcc 命令分析
在 3.1 节我们已经使用 gcc 编译了 Linux 下的第一个 C 程序,gcc 命令的格式如下:
gcc [参数] [文件名]
主要参数说明如下:
-c 编译、汇编到目标代码(.o),不链接成可执行文件
-g 生成调试信息
-o 编译完成后生成的文件名,如果不使用该选项,默认生成 a.out 文件
-O 对程序进行优化编译,产生的可执行文件执行效率高
-w 不生成任何警告
-S 仅编译到汇编语言,不进行汇编和链接
3.2.2 编译警告错误处理
我们是 Windows 下使用 Keil 或者其他开发工具,在编译的时候,如果程序有错误,开发工具会提示出
具体的错误信息,可以很方便的定位到问题点,快速的修改出现的问题,gcc 同样也有类似的功能,下面我
们来看下 gcc 的错误提示功能,首先我们在 work 目录下建立文件夹“test2”,然后使用 vim 在 test2 文件
夹创建 main.c 文件夹,在 main.c 文件输入如下代码:
#include
int main(int argc, char *argv[])
{
int a;
a = 1
printf("a=\n", a);
return 0;
}
上面代码有两处错误:
第 7 行 最后缺少“;”
第 9 行 printf 语法不对,应该为:printf("a =%d\n", a);
我们使用 gcc 编译 main.c,可以看到 gcc 会提示错误信息,如下图所示:
从上图中可以看到在 mian.c 文件的第 9 行 printf 前面缺少“;”我们在第 7 行“a = 1”后面加上“;”,
然后继续编译,如下图所示:
从上图可以看出编译提示语法格式不对,我们把第 9 行修改成“printf("a=%d\n", a);”,然后在继续编
译,如下图所示:
我们可以看到这次 gcc 编译通过,最终生成了文件 main。我们在终端执行 main,运行结果如下图所示:
从上图可以看到运行的结果和我们涉及到额结果一致,通过本例程可以看到 gcc 编译器不仅可以检测出
程序的错误,而且还会标记处错误在哪个文件的哪一行,很方便的帮助我们去修改问题。
从上图可以看到运行的结果和我们涉及到额结果一致,通过本例程可以看到 gcc 编译器不仅可以检测出
程序的错误,而且还会标记处错误在哪个文件的哪一行,很方便的帮助我们去修改问题。
3.2.3 gcc 编译流程
gcc 的编译流程可以分成四个步骤:
1.预处理,生成预编译文件(.文件)
2.编译,生成汇编代码(.S 文件)
3.汇编,生成目标文件(.o 文件)
4.链接,生成可执行文件
3 3.3 初识 Makefile
3.3.1 什么是 Makefile
在 3.2 章节我们了解了在 Ubuntu 系统下通过 gcc 编译器来编译 C 程序,在我们演示的历程中只有一个
C 文件,我们直接在终端输入 gcc 的编译命令,就完成了 C 程序的编译。我们在实际开发过程中,如果我们
的工程有几十个,或者几百几千个 C 文件,我们通过在终端输入 gcc 命令来编译,这显然是不现实的。为
了解决这个问题我们可以使用“make”命令,它会解析 Makefile 文件中的指令(应该说是规则)来编译整
个工程。在 Makefile 文件中描述了整个工程的所有文件的编译顺序,编译规则。
作为一个专业的程序员,一定要掌握 Makefile 的,我们可以通过 Makefile 能了解到整个工程的处理过程
的。
由于Makefile涉及到很多的知识点,以至于可以单独写本书来讲述,所以本章我们只是讲解下Makefile
的基础入门,如果详细的研究 Makefile,可以给大家推荐《跟我一起写 Makefile》这个电子文档,该文档
已经放在了:i.MX6UL 终结者光盘资料\09_其它参考资料里面了。
3.3.2 第一个 Makefile
在本节我们建立这样一个工程,计算两个整形数的和,并将结果在终端显示出来。在这个工程中一共有
main.c、calc.c 两个 C 文件和 calc.h 这个头文件。其中 main.c 是主文件,calc.c 负责接收 main.c 传过
来的数据,然后进行相加。main.c 文件的内用如下:
#include
include "calc.h"
int main(int argc, char *argv[])
{
int a = 3, b = 6, sum;
sum = calc(a, b);
printf("%d + %d = %d\n", a, b, sum);
return 0;
}
calc.c 文件内容如下:
#include
int calc(int a, ing b)
{
return (a+b);
}
文件 calc.h 内容如下:
#ifndef _CALC_H
#define _CALC_H
int calc(int a, int b);
#endif
上面就是我们这个工程的所有源文件,我们在终端使用 gcc 编译这个工程,在终端输入“gcc main.c
calc.c -o main”,该命令的意思是用 gcc 对 main.c、calc.c 进行编译,然后输出可执行文件 main,运行
结果如下图所示:
通过上图可以看到生成了可执行文件 main,我们在终端运行 main 执行文件,运行结果如下图所示:
我们可以看到上图的运行结果和我们设计的结果是一致的。由于我们的这个工程是有三个文件,如果
工程有几百个,几千个的文件,或者如果有一个文件被修改,使用上面的命令将会编译所有的文件,如果
我们的工程有上万个文件,编译完一次工程所需要的时间就很可怕。最优的方法就是编译过一次以后,如
果后面在编译,只编译修改的文件,这样就会节约很多时间,因此我们修改下编译方法,命令如下:
gcc -c main.c
gcc -c calc.c
gcc main.o calc.o -o main
我们在终端输入上面的命令,结果如下图所示:
上图的第一条和第二条命令里面使用了参数“-c”是把 main.c 和 calc.c 编译成对应的.o 文件,最后
一条命令是把编译生成的.o 文件链接成可执行文件 main。假如我们修改了 main.c 这个文件。只需将 main.c
这个一个文件重新编译下,然后在把所有的.o 文件重新链接成可执行文件,对应的命令如下:
gcc -c main.c
gcc main.o calc.o -o main
可是这样还有一个问题,如果需要修改的文件有很多,这样工作量也会不小,所以我们需要一个工具:
1.如果工程没有编译过,就会把工程中的.c 文件全部编译并连接成可执行文件
2.如果工程中有某些文件修改了,只编译修改的文件并连接成可执行文件
3.如果工程中的头文件修改了,那么就要编译所有引用这个头文件的.c 文件,并且连 接成可执
行文件
我们开头说的 Makefile 就是完成这个功能的,下面我们在工程中建立一个 Makefile 文件来实现这样的功
能(注意:文件名字必须为 Makefile,大小写是区分的)。我们使用 vim 创建 Makefile 文件(Makefile
和我们的 main.c、calc.c 在同一级目录下),然后输入下面的脚本:
main:main.o calc.o
gcc -o main main.o calc.o
main.o:main.c
gcc -c main.c
calc.o:calc.c
gcc -c calc.c
clean:
rm -rf *.o
rm -rf main
上面脚本缩进的行需要使用“Tab”键缩进,不要使用空格,这是 Makefile 的语法要求,编写完成的脚本
如下图所示:
编写好 Makefile,保存并退出,然后我们在终端输入“make”命令来编译我们的工程,make 命令会在
当前目录下查找“Makefile”文件,如果存在的话就按照 Makefile 里面的规则进行编译,如下图所示:
通过上图可以看到编译产生了 main.o、calc.o 和 main 执行文件,说明编译成功了。接下来我们修改
下 main.c 这个文件,如下图所示:
然后保存并退出,然后在终端输入“make”再次编译下工程,如下图所示:
通过上图我们可以看到只重新编译了修改的 main.c,并最终重新链接生成可执行文件 main,我们在终
端运行可执行文件 main,如下图所示:
从上图的运行结果可以看到最后的结果等于 10 了,和我们程序的设计结果是一样的。
|