根据教程的步骤首先设置TAB键为4字节,显示行号
$ cd /
$ cd /etc/vim
$ sudo gedit vimrc
在打开的文本最后一行添加
set ts=4
set nu
并保存
bj@bj-MS-7C83:~$ mkdir C_Program
bj@bj-MS-7C83:~$ cd C_Program/
bj@bj-MS-7C83:~$ mkdir hello
bj@bj-MS-7C83:~/C_Program$ cd hello/
bj@bj-MS-7C83:~/C_Program/hello$ vim main.c
输入代码
#include <stdio.h>
int main(){
printf("hello world!\n");
}
bj@bj-MS-7C83:~/C_Program/hello$ cat main.c
//接下来对文件进行编译
bj@bj-MS-7C83:~/C_Program/hello$ gcc main.c
bj@bj-MS-7C83:~/C_Program/hello$ ls
a.out main.c
bj@bj-MS-7C83:~/C_Program/hello$ ./a.out
hello world!
//成功输出
Makefile
GCC 编译器的编译流程是:预处理、编译、汇编和链接。预处理就是展开所有的头文件、
替换程序中的宏、解析条件编译并添加到文件中。编译是将经过预编译处理的代码编译成汇编
代码,也就是我们常说的程序编译。汇编就是将汇编语言文件编译成二进制目标文件。链接就
是将汇编出来的多个二进制目标文件链接在一起,形成最终的可执行文件,链接的时候还会涉
及到静态库和动态库等问题。
所以我们需要使用一个叫做Makefile的功能,他可以帮助我们处理一个大工程下面的所有C文件。
使用方法
Makefile 和C是处在同一个目录的,首先打开我们的工程目录,在工程目录下创建名为Makefile的文件(文件名必须是Makerfile,大小写不能变)
首先我们用刚才的代码在C_Program文件下创建工程文件add,在其中创建三个.c文件,分别是main.c,input.c,calcu.c,创建.h文件 input.h,calcu.h这两个头文件。
main.c
#include<stdio.h>
#include "input.h"
#include "calcu.h"
int main(){
int a,b,num;
input_int(&a,&b);
num=calcu(a,b);
printf("%d+%d =%d\r\n",a,b,num);
}
input.c
#include <stdio.h>
#include "input.h"
void input_int(int *a,int *b){
printf("input two numbers:");
scanf("%d %d",a,b);
printf("\r\n");
}
input.h
#ifndef _INPUT_H
#define _INPUT_H
void input_int(int *a,int *b);
#endif
calcu.c
#include "calcu.h"
int calcu(int a,int b)
{
return(a+b);
}
calcu.h
#ifndef _CALCU_H
#define _CALCU_H
int calcu(int a,int b);
#endif
bj@bj-MS-7C83:~/C_Program/add$ mkdir Makefile
打开Makefile并输入以下代码
bj@bj-MS-7C83:~/C_Program/add$ vim Makefile
main:main.o input.o calcu.o
gcc -o main main.o input.o calcu.o
main.o:main.c
gcc -c main.c
input.o:input.c
gcc -c input.c
calcu.o:calcu.c
gcc -c calcu.c
clean:
rm *.o
rm main
注意 :命令列表中的每条命令必须以 TAB 键开始,不能使用空格!!!
然后直接在命令行输入make,就能输出结果了。
如果发生错误,有两种可能
1. Makefile中命令缩进没有使用TAB键
2. VI/VIM 编辑器使用空格代替了 TAB 键,修改文件/etc/vim/vimrc,在文件最后面加上如
下所示代码:
set noexpandtab
在make结束后,我们把文件列出
bj@bj-MS-7C83:~/C_Program/add$ make
gcc -c input.c
gcc -o main main.o input.o calcu.o
bj@bj-MS-7C83:~/C_Program/add$ ls
a.out calcu.h input.c input.o main.c Makefile
calcu.c calcu.o input.h main main.o
可以看出已经生成了可执行文件,运行它
bj@bj-MS-7C83:~/C_Program/add$ ./main
就可以看到我们的代码开始运行
Makefile语法
Makefile 里面是由一系列的规则组成的,这些规则格式如下:
目标...... : 依赖文件集合......
命令 1
命令 2
......
例如我们刚才的代码
1 main:main.o input.o calcu.o
2 gcc -o main main.o input.o calcu.o
3 main.o:main.c
4 gcc -c main.c
5 input.o:input.c
6 gcc -c input.c
7 calcu.o:calcu.c
8 gcc -c calcu.c
9 clean:
10 rm *.o
11 rm main
上述代码中一共有 5 条规则,1~2 行为第一条规则,3~4 行为第二条规则,5~6 行为第三条
规则,7~8 行为第四条规则,10~12 为第五条规则,make 命令在执行这个 Makefile 的时候其执行步骤如下:
首先更新第一条规则中的 main,第一条规则的目标成为默认目标,只要默认目标更新了那
么就认为 Makefile 的工作。在第一次编译的时候由于 main 还不存在,因此第一条规则会执行,第一条规则依赖于文件 main.o、input.o 和 calcu.o 这个三个.o 文件,这三个.o 文件目前还都没有,因此必须先更新这三个文件。
make 会查找以这三个.o 文件为目标的规则并执行。以 main.o
为例,发现更新 main.o 的是第二条规则,因此会执行第二条规则,第二条规则里面的命令为“gcc–c main.c”,就是不链接编译 main.c,生成 main.o,其它两个.o 文件同理。
最后一个规则目标是 clean,它没有依赖文件,因此会默认为依赖文件都是最新的,所以其对应
的命令不会执行,当我们想要执行 clean 的话可以直接使用命令“make clean”,执行以后就会删除当前目录下所有的.o 文件以及 main,因此 clean 的功能就是完成工程的清理,“make clean”
Makefile变量
在Makefile中所有变量都是字符串,类似于C语言中的宏.
注释在前面加#
变量引用的方法是“&(变量名)”
赋值符“=”:
使用“=”在给变量的赋值的时候,不一定要用已经定义好的值,也可以使用后面定义的值。
name = zzk
curname = $(name)
name = blackjack
print:
@echo curname: $(curname)
前三步都是赋值,最后两步是打印出来,相当于C 的printf,在echo前面加@是不显示执行步骤
我们实验一下,在新创建的Makefile文件下输入上述代码并执行
bj@bj-MS-7C83:~/C_Program/add$ cd ..
bj@bj-MS-7C83:~/C_Program$ mkdir file
bj@bj-MS-7C83:~/C_Program$ cd file
bj@bj-MS-7C83:~/C_Program/file$ vim Makefile
bj@bj-MS-7C83:~/C_Program/file$ make
curname: blackjack
如果去掉@echo curname: $(curname)前面的@,则会打印出运行步骤:
bj@bj-MS-7C83:~/C_Program/file$ make
echo curname: blackjack
curname: blackjack
可以看出变量的真实值取决于他所引用变量的最后一次有效值
赋值符“:=”
如果将上述代码中的=改成:=,代码则不会再进行赋值blackjack那一步,即不会使用后面定义的变量,只能使用前面已定义好的。
赋值符“?=”
以这句为例,意思就是
name ?= blackjack
如果变量name前面没有被赋值,那么就被赋值为blackjack,如果前面被赋值过了,就使用前面赋的值。
变量追加“+=”
Makefile 中的变量是字符串,有时候我们需要给前面已经定义好的变量添加一些字符串进
去,此时就要使用到符号“+=”,比如如下所示代码:
objects = main.o inpiut.o
objects += calcu.o
一开始变量 objects 的值为“main.o input.o”,后面我们给他追加了一个“calcu.o”,因此变量 objects 变成了“main.o input.o calcu.o”,这个就是变量的追加。
Makefile模式规则:
模式规则中,至少在规则的目标定定义中要包涵“%”,否则就是一般规则,目标中的“%”
表示对文件名的匹配,“%”表示长度任意的非空字符串,比如“%.c”就是所有的以.c 结尾的
文件,类似与通配符,a.%.c 就表示以 a.开头,以.c 结束的所有文件。
当“%”出现在目标中的时候,目标中“%”所代表的值决定了依赖中的“%”值,使用方
法如下:
%.o : %.c
命令
Makefile 自动化变量
自动化变量就是这种变量会把模式中所定义的一系列的文件自动的挨个取出,直至所有的符合模式的文件都取完,自动化变量只应该
出现在规则的命令中。
结合起来我们可以把之前Makefile的代码修改为
objects = main.o input.o calcu.o
main: $(objects)
gcc -o main $(objects)
%.o : %.c
gcc -c $<
clean:
rm *.o
rm main
我们可以试验一下,打开刚才的add工程,把Makefile修改为如上所示,然后运行。
成功运行
Makefile 伪目标
伪目标的目标是防止在Makefile中执行命令的目标和工作目标下的实际文件出现名称冲突
使用命令:
.PHONY :(名字)
申明为伪目标后,无论当前目录下是否出现相同文件名,都不会影响Makefile中该文件命令的执行
Makefile条件判断
<条件关键字>
<条件为真时执行的语句>
endif
或
<条件关键字>
<条件为真时执行的语句>
else
<条件为假时执行的语句>
endif
条件关键字有四个:
ifeq,ifneq, ifdef, ifndef
ifeq用法:
ifeq (<参数 1>, <参数 2>)
ifeq ‘<参数 1 >’,‘ <参数 2>’
ifeq “<参数 1>”, “<参数 2>”
ifeq “<参数 1>”, ‘<参数 2>’
ifeq ‘<参数 1>’, “<参数 2>”
比较参数1与参数2是否相同,如果相同则为真,
参数1和参数2可以为函数的返回值
ifneq用法:
用法和ifeq相同,用作比较参数1和参数2是否不相等,不相等则为真
ifdef用法:
ifdef<变量名>
如果变量名的值非空则为真,反之为假
ifndef用法:
ifdef<变量名>
如果变量名的值非空则为假,反之为真
Makefile函数使用
$(函数名 参数集合)
或者:
${函数名 参数集合}
函数名和参数之间用空格隔开,参数与参数之间用逗号隔开
函数的调用以$开头
常用函数:
subst:字符串替换
此函数的功能是将字符串
$(subst <from>,<to>,<text>)
例如:
$(subst <eat>,<sleep>,<i want to eat>)
替换完的字符串变为
i want to sleep
dir:获取目录
此函数的功能是从文件名序列names中提取出目录,返回值是文件名序列的目录部分
$(dir <names...>)
例如:
$(dir </src/a.c>)
提取“/src/a.c”的目录部分,也就是“/src”
patsubst: 模式字符串替换
此函数查找字符串text中的单词是否符合模式pattern,如果匹配就用replacement来替换掉
$(patsubst <pattern>,<replacement>,<text>)
例如:
$(patsubst %.c,%.o,a.c b.c c.c)
将字符串“a.c b.c c.c”中的所有符合“%.c”的字符串,
替换为“%.o”,替换完成以后的字符串为“a.o b.o c.o”
notdir:提取文件名
$(notdir <names...>)
例如:
$(notdir </src/a.c>)
提取文件“/src/a.c”中的非目录部分,也就是文件名“a.c”
foreach: 模式字符串替换
此函数查找字符串text中的单词是否符合模式pattern,如果匹配就用replacement来替换掉
$(patsubst <pattern>,<replacement>,<text>)
例如:
$(patsubst %.c,%.o,a.c b.c c.c)
将字符串“a.c b.c c.c”中的所有符合“%.c”的字符串,
替换为“%.o”,替换完成以后的字符串为“a.o b.o c.o”