Ubuntu
下也有一些可以进行编程的工具,但是大多都只是编辑器,
也就是只能进行代码编辑,如果要编译的话就需要用到
GCC
编译器,使用
GCC
编译器肯定就
要接触到Makefile。
1:hello world!!!
我们所说的编写代码包括两部分:代码编写和编译,在
Windows
下可以使用
VisualStudio
来完成这两部分,可以在
VisualStudio下编写代码然后直接点击编译就可以了。
C
编译器进行编译,其中代码编写工具很多,比如VIM
编辑器、
Emacs
编辑器、
VScode
编辑器等等
1.1编写代码
先在用户根目录下创建一个工作文件夹: C_Program ,所有的 C 语言练习都保存到这个工作文件夹下,创建过程如图
为了方便管理,我们后面每个例程都创建一个文件夹来保存所有与本例程有关的文件,创建一个名为“3.1”的文件夹来保存我们的“HelloWorld”程序相关的文件,操作如图![]()
在使用VI编辑器之前我们先做如下设置:
1、设置TAB键为4字节
VI 编辑器默认 TAB 键为 8 空格,我们改成 4 空格,用 vi 打开文件 /etc/vim/vimrc ,在终端输入sudu vi /etc/vim/vimrc在此文件最后面输入如下代码set ts=42 、 VIM 编辑器显示行号
VIM 编辑器默认是不显示行号的,不显示行号不利于代码查看,我们设置 VIM 编辑器显示行号,同样是通过在文件 /etc/vim/vimrc 中添加代码来实现
写完set nu 后,不要动,按ESC,然后按冒号,wq
使用 vi 指令创建一个名为“main.c”的文件,然后在里面输入如下代码:编写完成以后保存退出 vi 编辑器,可以使用“cat”命令查看代码是否编写成功
编译代码
![]()
如果输入命令“ gcc -v ”命令以后,你的终端输出类似图 3.1.2.1 中的信息,那么说明你的电脑已经有 GCC 编译器如何使用 GCC 编译器来编译 main.c 文件呢? GCC 编译器是命令模式的,因此需要输入命令来使用 gcc 编译器来编译文件,输入如下命令:gcc main.c
当编译完成以后会生成一个 a.out 文件,这个 a.out 就是编译生成的可执行文件,执行此文件看看是否和我们代码的功能一样,执行的方法很简单使用命令:“./+ 可执行文件”,比如本例程就是命令:./a.out
。 a.out 这个文件的命名是 GCC 编译器自动命名的,那我们能不能决定编译完生成的可执行文件名字呢?在使用 gcc 命令的时候加上 -o 来指定生成的可执行文件名字,比如编译 main.c 以后生成名为“main”的可执行文件,操作如图![]()
2:GCC编译器
gcc 命令
![](https://i-blog.csdnimg.cn/blog_migrate/d64c3166d0c0b936ce57b03755bf1738.png)
主要选项如下:
-c
:
只编译不链接为可执行文件,编译器将输入的
.c
文件编译为
.o
的目标文件。
-o
:
<
输出文件名
>
用来指定编译结束以后的输出文件名,如果不使用这个选项的话
GCC
默
认编译出来的可执行文件名字为
a.out
。
-g
:
添加调试信息,如果要使用调试工具
(
如
GDB)
的话就必须加入此选项,此选项指示编
译的时候生成调试所需的符号信息。
-O
:对程序进行优化编译,如果使用此选项的话整个源代码在编译、链接的的时候都会进
行优化,这样产生的可执行文件执行效率就高。
-O2
:
比
-O
更幅度更大的优化,生成的可执行效率更高,但是整个编译过程会很慢。
编译错误警告
使 用 vi
在文件夹“
3.2
”中创建一个 main.c 文件,在文件里面输入如下代码:
![](https://i-blog.csdnimg.cn/blog_migrate/e6c8b5f97410994fa561ecf614243133.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bba84803529a461feb371ffd1200f489.png)
![](https://i-blog.csdnimg.cn/blog_migrate/165c27dae6a0be04aad2a3b6931666b9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3685be5a9418db84e510095b9bc2afc1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/71bbacd20ffd950da953e655baa8ee55.png)
3:编译流程
GCC 编译器的编译流程是:预处理、编译、汇编和链接。
预处理就是展开所有的头文件、 替换程序中的宏、解析条件编译并添加到文件中。编译是将经过预编译处理的代码编译成汇编 代码,也就是我们常说的程序编译。汇编就是将汇编语言文件编译成二进制目标文件。链接就 是将汇编出来的多个二进制目标文件链接在一起,形成最终的可执行文件,链接的时候还会涉 及到静态库和动态库等问题。
2:makefile基础
Makefile 就跟脚本文件一样, Makefile 里面还可以执行系统命令。使用的时候只需要一个 make命令即可完成整个工程的自动编译,极大的提高了软件开发的效率。作为一个专业的程序员,是一定要懂得 Makefile 的,一是因为在 Linux 下你不得不懂 Makefile ,再就是通过 Makefile 你就能了解整个 工程的处理过程。Makefile 的引入新建vi![]()
![]()
![]()
![]()
![]()
在终端输入如下命令:
上面命令的意思就是使用 gcc 编译器对 main.c 、 calcu.c 和 input.c 这三个文件进行编译,编译生成的可执行文件叫做 main 。编译完成以后执行 main 这个程序,测试一下软件是否工作正 常,结果如图
改变编译方法:第一次编 译工程,我们先将工程中的文件都编译一遍,然后后面修改了哪个文件就编译哪个文件,命令 如下:
假如我们现在修改了 calcu.c 这个文件,只需要将 caclue.c 这一个文件 重新编译成.o 文件,然后在将所有的.o 文件链接成可执行文件即,只需要下面两条命令即可:
但是这样就又有一个问题,如果修改的文件一多,我自己可能都不记得哪个文件修改过了,然后忘记编译,然后 …… ,为此我们需要这样一个工具:1 、如果工程没有编译过,那么工程中的所有 .c 文件都要被编译并且链接成可执行程序。2 、如果工程中只有个别 C 文件被修改了,那么只编译这些被修改的 C 文件即可。3 、如果工程的头文件被修改了,那么我们需要编译所有引用这个头文件的 C 文件,并且链接成可执行文件。很明显,能够完成这个功能的就是 Makefile 了,在工程目录下创建名为 “Makefile” 的文件,文件名一定要叫做“Makefile”!!!区分大小写的哦!在 Makefile 文件中输入如下代码:
上述代码中所有行首需要空出来的地方一定要使用“ TAB ”键!不要使用空格键!这是Makefile 的语法要求Makefile 编写好以后我们就可以使用 make 命令来编译我们的工程了,直接在命令行中输入“ make ”即可, make 命令会在当前目录下查找是否存在“ Makefile ”这个文件,如果存在的话就会按照 Makefile 里面定义的编译方式进行编译,如图
,使用命令“ make ”编译完成以后就会在当前工程目录下生成各种 .o 和可执行文件,说明我们编译成功了。使用 make 命令编译工程的时候可能会提示如图
错误来源一般有两点 :1 、 Makefile 中命令缩进没有使用 TAB 键!2 、 VI/VIM 编辑器使用空格代替了 TAB 键,修改文件 /etc/vim/vimrc ,在文件最后面加上如下所示代码:set noexpandtab我们修改一下 input.c 文件源码,随便加几行空行就行了,保证 input.c 被修改过即可,修改完成以后再执行一下“make”命令重新编译一下工程,结果如图
总结一下 Make 的执行过程:1 、 make 命令会在当前目录下查找以 Makefile(makefile 其实也可以 ) 命名的文件。2 、当找到 Makefile 文件以后就会按照 Makefile 中定义的规则去编译生成最终的目标文件。3 、当发现目标文件不存在,或者目标所依赖的文件比目标文件新 ( 也就是最后修改时间比目标文件晚 ) 的话就会执行后面的命令来更新目标。。 Makefile 的好处就是“自动化编译”,一旦写好了 Makefile文件,以后只需要一个 make 命令即可完成整个工程的编译,极大的提高了开发效率。![]()
3:Makefile 变量
跟
C
语言一样
Makefile
也支持变量的,先看一下前面的例子:
main.o input.o
和
calcue.o
这三个依赖文件,我们输入了两遍,我们
这个
Makefile
比较小,如果
Makefile
复杂的时候这种重复输入的工作就会非常费时间,而且非
常容易输错,为了解决这个问题,Makefile 加入了变量支持。
![](https://i-blog.csdnimg.cn/blog_migrate/43d8dc369c3fb6f3dcb0ba228765803f.png)
第
1
行是注释,
Makefile
中可以写注释,注释开头要
用符号“
#
”,不能用
C
语言中的“
//
”或者“
/**/
”!第
2
行我们定义了一个变量
objects
,并且
给这个变量进行了赋值,其值为字符串“
main.o input.o calcu.o
”,第
3
和
4
行使用到了变量
objects
, Makefile 中变量的引用方法是“
$(
变量名
)
”,比如本例中的“
$(objects)
”就是使用变量
objects。
我们在定义变量
objects
的时候使用“
=
”对其进行了赋值,
Makefile
变量的赋值符还有其它两个“
:=
”和“
?=
”,我们来看一下这三种赋值符的区别:
1 、赋值符“=”
第 1 行定义了一个变量 name ,变量值为“ zzk ”,第 2 行也定义了一个变量 curname ,curname 的变量值引用了变量 name ,按照我们 C 写语言的经验此时 curname的值就是“ zzk ”。第 3 行将变量 name 的值改为了“ zuozhongkai ”,第 5 、 6 行是输出变量 curname的值。在 Makefile 要输出一串字符的话使用“ echo ”,就和 C 语言中的“ printf ”一样,第 6 行中的“ echo ”前面加了个“ @ ”符号,因为 Make 在执行的过程中会自动输出命令执行过程,在命令前面加上“ @ ”的话就不会输出命令执行过程,大家可以测试一下不加“ @ ”的效果。使用命令“make print”来执行上述代码,结果如图
2 、赋值符“:=”修改完成以后重新执行一下 Makefile
可以看到此时的 curname 是 zzk ,不是 zuozhongkai 了。这是因为赋值符“ :=”不会使用后面定义的变量,只能使用前面已经定义好的,这就是“ = ”和“ := ”两个的区别。
3 、赋值符“?=”4 、变量追加“ += ”
Makefile 中的变量是字符串,有时候我们需要给前面已经定义好的变量添加一些字符串进去,此时就要使用到符号“+=”,比如如下所示代码:
一开始变量 objects 的值为“ main.o input.o ”,后面我们给他追加了一个“ calcu.o ”,因此变量 objects 变成了“ main.o input.o calcu.o ”,这个就是变量的追加。
4:Makefile 模式规则
我们编写了一个 Makefile 文件用来编译工程
Makefile 条件判断
5:Makefile 函数使用
![](https://i-blog.csdnimg.cn/blog_migrate/530d772de6528edda678202829c23d42.png)
上述
Makefile
中第
3~8
行是将对应的
.c
源文件编译为
.o
文件,每一个
C
文件都要写一个对
应的规则,如果工程中
C
文件很多的话显然不能这么做。为此,我们可以使用
Makefile
中的模
式规则,通过模式规则我们就可以使用一条规则来将所有的
.c
文件编译为对应的
.o
文件。
模式规则中,至少在规则的目标定定义中要包涵“
%
”,否则就是一般规则,目标中的“
%
”
表示对文件名的匹配,“
%
”表示长度任意的非空字符串,比如“
%.c
”就是所有的以
.c
结尾的
文件,类似与通配符,
a.%.c
就表示以
a.
开头,以
.c
结束的所有文件。
当“
%
”出现在目标中的时候,目标中“
%
”所代表的值决定了依赖中的“
%
”值,使用方 法如下:
![](https://i-blog.csdnimg.cn/blog_migrate/1338e3f75afc4f151ac575fbb250d705.png)
![](https://i-blog.csdnimg.cn/blog_migrate/725843b13ae3a06b8ed3e8ff21e90996.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0921141138b432f1f57d01031268aea6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/a8246b3321353070ab21e547faa80e88.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8fee8de9d9d82f0c156ebb1972d222e6.png)
上述用法中都是用来比较“参数
1
”和“参数
2
”是否相同,如果相同则为真,“参数
1
”和
“参数
2
”可以为函数返回值。
ifneq
的用法类似,只不过
ifneq
是用来了比较“参数
1
”和“参
数
2
”是否不相等,如果不相等的话就为真。
5:Makefile 函数使用![](https://i-blog.csdnimg.cn/blog_migrate/3b26569d9f96125aefe0032cab78b1b0.png)
使用符号“
$
”来标识。参数集合是函数的多个 参数,参数之间以逗号“,
”隔开,函数名和参数之间以“空格”分隔开,函数的调用以“
$
”开 头。
1、函数 subst
把字符串“ my name is zzk ”中的“ zzk ”替换为“ ZZK ”,替换完成以后的字符串为“ my nameis ZZK ”。2 、函数 patsubst函数 patsubst 用来完成模式字符串替换,使用方法如下:$(patsubst <pattern>,<replacement>,<text>)此函数查找字符串 <text> 中的单词是否符合模式 <pattern> ,如果匹配就用 <replacement> 来替换掉, <pattern> 可以使用通配符“ % ”,表示任意长度的字符串,函数返回值就是替换后的字 符串。如果<replacement> 中也包涵“ % ”,那么 <replacement> 中的“ % ”将是 <pattern>中的那个 “%”所代表的字符串,
3、函数 dir4 、函数 notdir
5、函数 foreach
6、函数 wildcard
![]()