嵌入式Linux学习笔记3
文章目录
第一个C语言程序的编写
VIM编辑器的初始设置
1.打开文件/etc/vim/vimrc
sudo vim /etc/vim/vimrc
2.移动到文件的最后面,先按i进入insert模式,后输入以下代码
设置TAB键为4字节:
set ts = 4
VIM编辑器显示行号:
set nu
设置不用空格替代TAB
set noexpandtab
按Ese退出insert模式
3.最后输入 : w :w :w 保存文件,输入 : q :q :q 退出文件
创建.c文件,编写代码
1.cd到想要的目录,使用vim创建文件
vim main.c
2.然后按i进入insert模式
之后输入以下代码
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("Hello World!\n");
}
按Ese退出insert模式
3.最后输入 : w :w :w 保存文件,输入 : q :q :q 退出文件
4.检验是否编写成功
在shell中输入
cat main.c
编译代码
1.查看GCC编译器的版本号(非必须)
gcc -V
2.编译文件命令
gcc main.c
在编译之后会生成a.out文件,为生成的可执行文件
生成指定.out文件的方式
例:生成main.out文件
gcc main.c –o main
3.执行文件
./main
之后就可以得到程序执行的结果
GCC编译器
gcc命令
和上小节中使用的命令格式相同,如下:
gcc [选项] [文件名字]
选项如下:
- -c:只编译不链接为可执行文件,编译器将输入的.c 文件编译为.o 的目标文件
- -o <输出文件名>:用来指定编译结束以后的输出文件名,如果使用这个选项的话 GCC 默认编译出来的可执行文件名字为 a.out
- -g:添加调试信息,如果要使用调试工具(如 GDB)的话就必须加入此选项,此选项指示编译的时候生成调试所需的符号信息
- -O:对程序进行优化编译,如果使用此选项的话整个源代码在编译、链接的的时候都会进行优化,这样产生的可执行文件执行效率就高
- -O2:比-O 更幅度更大的优化,生成的可执行效率更高,但是整个编译过程会很慢
编译流程
GCC编译器的流程为:
预处理>>编译>>汇编>>链接
预处理:展开所有的头文件,替换程序中的宏,解析条件编译并添加到文件找那个
编译:将经过预编译处理的代码编译成汇编代码
汇编:将汇编语言文件编译成二进制目标文件
链接:将汇编出来的多个二进制目标文件链接在一起,形成最终的可执行文件
Makefile
Makefile是什么
如果我们的工程中有几十、上百甚至上万个.c文件的时候,用终端输入 GCC 命令的方法显然是不现实的。因此我们需要编写一个文件,这个文件描述了哪些源码文件需要编译、如何编译,每次需要编译工程的时只需要使用这个文件就行了。
Makefile的引入
按照建立.c文件的方式,创建下面5个文件
main.c
#include <stdio.h>
#include "input.h"
#include "calcu.h"
int main(int argc, char *argv[])
{
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 num:");
scanf("%d %d", a, b);
printf("\r\n");
}
calcu.c
#include "calcu.h"
int calcu(int a, int b)
{
return (a + b);
}
input.h
#ifndef _INPUT_H
#define _INPUT_H
void input_int(int *a, int *b);
#endif
calcu.h
#ifndef _CALCU_H
#define _CALCU_H
int calcu(int a, int b);
#endif
1.在不用Makefile的时候我们需要用以下的命令来编译代码:
gcc main.c calcu.c input.c -o main
2.在使用Makefile的时候我们用以下的命令来编译代码:
gcc -c main.c
gcc -c input.c
gcc -c calcu.c
gcc main.o input.o calcu.o -o main
之后只需要将修改过的代码文件单独编译,从而避免每次需要编译整个工程
3.为了避免我们忘记自己修改过哪个文件,我们需要用到Makefile工具
在工程目录下创建名为“Makefile”的文件
在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,不能用空格键)
编译的时候只需要在shell中输入
make
Makefile语法
Makefile规则格式
目标...... : 依赖文件集合......
命令 1
命令 2
......
上面例子中的,规则的目标是 main,main.o、input.o 和 calcu.o 是生成 main 的依赖文件,如果要更新目标 main,就必须先更新它的所有依赖文件,如果依赖文件中的任何一个有更新,那么目标也必须更新,“更新”就是执行一遍规则中的命令列表。
make 命令会为 Makefile 中的每个以 TAB 开始的命令创建一个 Shell 进程去执行。
首先更新第一条规则中的 main,第一条规则的目标成为默认目标,只要默认目标更新了那么就认为 Makefile 的工作,完成了整个 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的执行过程
-
make 命令会在当前目录下查找以 Makefile(makefile 其实也可以)命名的文件
-
当找到 Makefile 文件以后就会按照 Makefile 中定义的规则去编译生成最终的目标文件
-
当发现目标文件不存在,或者目标所依赖的文件比目标文件新(也就是最后修改时间比目标文件晚)的话就会执行后面的命令来更新目标
Makefile变量
Makefile 中的变量都是字符串,注释开头用符号“#”
#Makefile变量的使用
objects = main.o input.o calcu.o
main:$(objects)
gcc -o main $(objects)
在上面代码中变量为objects,$(objects)是使用变量objects
-
赋值符“=”
Makefile:
name = yc curname = $(name) name = yanchi print: @echo curname:$(curname)
输入:
make print
输出结果:
curname:yanchi
“@”存在的时候不会自动输出命令执行过程
变量的真实值取决于它所引用的变量的最后一次有效值
-
赋值符“:=”
Makefile:
name = yc curname := $(name) name = yanchi print: @echo curname:$(curname)
输入:
make print
输出结果:
curname:yc
变量的真实值不会使用后面定义的变量,只能使用前面已经定义好的
-
赋值符“?=”
如果变量前面没有被赋值就赋值,已经赋值就忽略本次赋值
-
变量追加“+=”
Makefile 中的变量是字符串,有时候我们需要给前面已经定义好的变量添加一些字符串进去,此时就要使用到符号“+=”,比如如下所示代码:
objects = main.o inpiut.o objects += calcu.o
Makefile模式规则
模式规则中,至少在规则的目标定定义中要包涵“%”,否则就是一般规则,目标中的“%”表示对文件名的匹配,“%”表示长度任意的非空字符串,比如“%.c”就是所有的以.c 结尾的文件,类似与通配符,a.%.c 就表示以 a.开头,以.c 结束的所有文件。
当“%”出现在目标中的时候,目标中“%”所代表的值决定了依赖中的“%”值,使用方法如下:
%.o:%.c
#命令
采用这种方式可以将上文代码中的Makefile改写成以下的形式:
objects = main.o input.o calcu.o
main: $(objects)
gcc -o main $(objects)
%.o : %.c
#命令
clean:
rm *.o
rm main
Makefile自动化变量
上面讲的模式规则中,目标和依赖都是一系列的文件,每一次对模式规则进行解析的时候,都会是不同的目标和依赖文件。这个时候就需要自动化变量来完成一行变量从不同的依赖文件中生成对应的目标。
自动化变量只应该出现在规则的命令中,常用的自动化变量如表所示:
自动化变量 | 描述 |
---|---|
$@ | 规则中的目标集合,在模式规则中,如果有多个目标的话,“$@”表示匹配模式中定义的目标集合(常用) |
$% | 当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空 |
$< | 依赖文件集合中的第一个文件,如果依赖文件是以模式(即“%”)定义的,那么“$<”就是符合模式的一系列的文件集合(常用) |
$? | 所有比目标新的依赖目标集合,以空格分开 |
$^ | 所有依赖文件的集合,使用空格分开,如果在依赖文件中有多个重复的文件,“$^”会去除重复的依赖文件只保留一份(常用) |
$+ | 和“$^”类似,但是当依赖文件存在重复的话不会去除重复的依赖文件 |
$* | 这个变量表示目标模式中"%"及其之前的部分,如果目标是 test/a.test.c,目标模式为 a.%.c,那么“$*”就是 test/a.test |
采用这种方式可以补上上文中Makefile中的命令,最终完整代码如下所示:
objects = main.o input.o calcu.o
main: $(objects)
gcc -o main $(objects)
%.o : %.c
gcc -c $<
clean:
rm *.o
rm main
Makefile伪目标
Makefile 有一种特殊的目标——伪目标,一般的目标名都是要生成的文件,而伪目标不代表真正的目标名,在执行 make 命令的时候通过指定这个伪目标来执行其所在规则的定义的命令。
使用伪目标的目的主要是为了避免 Makefile 中定义的只执行命令的目标和工作目录下的实际文件出现名字冲突,有时候我们需要编写一个规则用来执行一些命令,但是这个规则不是用来创建文件的。
例:
clean:
rm *.o
rm main
上述规则中并没有创建文件 clean 的命令,因此工作目录下永远都不会存在文件 clean,当我们输入“make clean”以后,后面的“rm *.o”和“rm main”总是会执行。可是如果我们“手贱”,在工作目录下创建一个名为“clean”的文件,那就不一样了,当执行“make clean”的时候,规则因为没有依赖文件,所以目标被认为是最新的,因此后面的 rm 命令也就不会执行,我们预先设想的清理工程的功能也就无法完成。
声明方式如下:
.PHONY:clean
我们可以用伪目标继续优化之前的代码,修改完成之后的结果为:
objects = main.o input.o calcu.o
main: $(objects)
gcc -o main $(objects)
.PHONY:clean
%.o : %.c
gcc -c $<
clean:
rm *.o
rm main
Makefile条件判断
语法规则如下:
<条件关键字>
<条件为真时执行的语句>
endif
或者
<条件关键字>
<条件为真时执行的语句>
else
<条件为假时执行的语句>
endif
其中条件关键词由两对:ifeq和ifneq,ifdef和ifndef。
ifeq和ifneq
ifeq和ifneq用于判断是否相等和是否不相等,用法如下:
ifeq (<参数 1>, <参数 2>)
ifeq ‘<参数 1 >’,‘ <参数 2>’
ifeq “<参数 1>”, “<参数 2>”
ifeq “<参数 1>”, ‘<参数 2>’
ifeq ‘<参数 1>’, “<参数 2>”
ifdef和ifndef
ifdef和ifndef用于判断是否定义,用法如下:
ifdef <变量名>
Makefile函数使用
Makefile 中的函数是已经定义好的,这点和C语言不同,可以直接使用。
函数的用法如下:
$(函数名 参数集合)
或者
${函数名 参数集合}
参数集合是函数的多个参数,参数之间以逗号“,”隔开,函数名和参数之间以“空格”分隔开,函数的调用以“$”开头。
-
函数subst
函数subst用来完成字符串的替换,调用形式如下:
$(subst <from>,<to>,<text>)
例:
$(subst yc,ys,my name is ys)
输出的结果为“my name is yc”
-
函数patsubst
函数 patsubst 用来完成模式字符串替换,使用方法如下:
$(patsubst <pattern>,<replacement>,<text>)
此函数查找字符串<text>中的单词是否符合模式<pattern>,如果匹配就用<replacement>来替换掉,<pattern>可以使用包括通配符“%”,表示任意长度的字符串,函数返回值就是替换后的字符串。如果<replacement>中也包涵“%”,那么<replacement>中的“%”将是<pattern>中的那个“%”所代表的字符串,比如:
$(patsubst %.c,%.o,a.c b.c c.c)
输出的结果为“a.o b.o c.o”
-
函数dir
函数 dir 用来获取目录,使用方法如下:
$(dir <names...>)
此函数用来从文件名序列<names>中提取出目录部分
返回值:文件名序列<names>的目录部分,比如:
$(dir </src/a.c>)
返回值为:“/src”
-
函数notdir
去除文件中的目录部分,也就是提取文件名,用法如下:
$(notdir <names...>)
比如:
$(notdir </src/a.c>)
返回值为:“a.c”
-
函数foreach
foreach 函数用来完成循环,用法如下:
$(foreach <var>, <list>,<text>)
此函数的意思就是把参数<list>中的单词逐一取出来放到参数<var>中,然后再执行<text>所包含的表达式。每次<text>都会返回一个字符串,循环的过程中,<text>中所包含的每个字符串会以空格隔开,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串将会是函数 foreach 函数的返回值。
-
函数wildcard
通配符“%”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动展开,这个时候就要用到函数 wildcard,使用方法如下:
$(wildcard PATTERN...)
比如:
$(wildcard *.c)
上面的代码是用来获取当前目录下所有的.c 文件,类似“%”。