入门LinuxC编程(嵌入式Linux掌握)

   在windows下我们可以使用各种各样的IDE进行编程,比如强大的vs。但在ubuntu下如何进行编程呢?
ubuntu下也有进行编程的工具,但大多都只是编辑器,也就是只能进行编辑,如果要编译的话就需要用到
GCC编译器,使用GCC编译器肯定就要接触到Makefile。
   本次就学学如何在ubuntu下进行C语言的编辑和编译、GCC和makefile的使用和编写。通过本次学习可以
掌握Linux进行C编程的方法,为以后的ARM逻辑和Linux驱动学习做准备。

3.1Hello World!
   在Linux下编辑和编译代码是分开的,我们可以用VIM进行代码编写,编写完成后再使用GCC进行编译,
其中代码编写工具很多,比如VIM、Emacs、VScode等,这里我们使用ubuntu自带的VIM编辑器。

mkdir C_program        //先在用户目录下创建一个文件夹,以后所有的C练习都保存这

    1、设置TAB键为4字节
    VI编辑器默认TAB键8字节,我们用vi打开文件/etc/vim/vimrc,在此文件最后面输入如下代码:
        set ts = 4
    修改完成以后保存并关闭文件,如果没有权限修改可以sudo vi打开
    2、VIM编辑器显示行号
    VIM默认不显示行号,同样vi打开/etc/vim/vimrc中添加:
        set nu
    VIM可以自行定制,网上有很多博客讲解如何设置VIM。设置好VIM后就可以正式开始编写代码了。
    进入C_program使用vi创建一个main.c然后输入标准hello函数代码。
    编写完成后保存退出vi编辑器,可以使用cat命令查看代码是否编写成功cat main.c.
3.1.2编译代码
   ubuntu下C语言编译器是GCC默认安装好了,可以通过如下命令查看GCC编译器版本:
    gcc -v
   中有“Target: x86_64-linux-gnu”一行,这说明 Ubuntu 自带的 GCC 编译器是针对 X86 架构的。
因此只能编译在 X86 架构 CPU 上运行的程序。如果想要编译在 ARM上运行的程序就需要针对 ARM 
的 GCC 编译器,也就是交叉编译器!
   我们是ARM开发,因此肯定要安装针对ARM架构的GCC交叉编译器,当然这是后面的事情了,现在不用管hh。
只要知道不同的目标架构,其GCC编译器是不同的。
   现在,如何使用GCC编译器来编译main.c呢?GCC编译器是命令模式的,因此需要输入命令来使用GCC。
    gcc main.c
    编译完成后会生成一个a.out 文件,这个文件就是可执行文件,执行此文件:
    ./a.out
    在使用gcc命令时加上-o来指定生成的可执行文件的名字。
    gcc main.c -o main        //指定可执行文件名为main
    
至此我们就玩成了Linux下C编程和编译的一整套过程。

————————————
3.2GCC编译器
   上小节我们已经使用过GCC编译器来编译C文件了,我们使用到的是gcc命令,格式如下:
    gcc [选项] [文件名]
    -c    只编译不链接为可执行文件,编译器将输入的.c文件编译为.o目标文件
    -o    <输出文件名>,自定义可执行文件名,如果不使用默认生成a.out
    -g    添加调试信息,如果要使用调试工具的话就必须加入此选项,此选项指示编译的时候
        生成调试所需的符号信息
    -O    对程序进行优化编译,如果使用此选项的话整个源代码在编译、链接的时候都会进行
        优化,这样产生的可执行文件效率就高
    -O2    比-O更大幅度优化,但整个编译过程会很慢
GCC编译器在检查出代码错误时也可以发出警告。
GCC编译流程:预处理、编译、汇编和链接。预处理就是展开所有的头文件、替换程序中的宏、解析条件编译
    并添加到文件中。编译是将经过预编译处理的代码编译成汇编代码,也就是我们常说的程序编译。
    汇编就是将汇编语言文件编译成二进制目标文件。链接就是将汇编出来的多个二进制文件链接
    在一起,形成最终的可执行文件,链接的时候还会涉及静态库和动态库等问题。
    hello例程只有一个文件,而且文件非常简单,因此可以直接使用gcc命令生成可执行文件,并没有
    先将c文件编译成.o文件然后再链接一起。

————————————
3.3Makefile基础
   3.3.1何为Makefile
   如果我们工程只有一两个C文件还好,需要输入的命令不多,当文件有十几个、上百个的时候用终端输入
GCC命令的啊方法显然是不实际的。如果我们能够编写一个文件,这个文件描述了编译哪些源码文件、如何编译
那就好了,每次需要编译工程的时候只需要使用这个文件就行了。makefile就跟脚本文件一样,里面还可以
执行系统命令。使用的时候只需哟一个make命令即可完成整个工程的自动编译,极大提高了软件开发效率。
   如果大家以前一直使用IDE来编写C语言的话肯定没有听说过makefile这个东西,其实IDE是有的,只不过
这些IDE对其进行了封装,提供给大家的是已经经过封装后的图形界面了,我们再IDE中给添加要编译的C文件,
然后点击按钮就玩成了编译。在linux下用的最多的是GCC,这是个没有UI的编译器,因此makefile就需要我们
自己来编写了。作为一个专业的程序员,一定要懂得makefile的,一是因为在lunux下你不得不懂makefile,
再就是通过makefile就能了解整个工程的处理过程。
   由于makefile的知识比较多完全可以写一本书,因此只讲解makefile基础入门。
   3.3.2makefile的引入
   我们完成一个小工程,通过键盘输入两个整形,然后计算他们的和并打印,这个工程中我们有main.c、
input.c和calcu.c三个C文件和input.h、calcu.h两个头文件。
    在终端输入如下命令:
    gcc main.c calcu.c input.c -o main
   上面命令就是使用gcc对全部文件按进行编译并生成可执行文件main。如果工程有非常非常多的文件,想想
这几万个文件编译一次需要的时间就可怕。最好的办法就是知道哪个文件被修改了,为此我们改变我们的编译
方法,如果后面修改了哪个文件就编译哪个文件:
    gcc -c main.c
    gcc -c input.c
    gcc -c calcu.c
    gcc main.o input.o calcu.o -o main
   但是又有一个问题,如果修改的文件一多,自己都忘了然后忘记编译,然后...,为此我们需要一个工具:
1、如果工程没有编译过,那么工程中的所有.c文件都要被编译并且链接成可执行文件
2、如果只有个别C文件被修改了,那么只编译这些被修改的C文件即可。
3、如果工程的头文件被修改了,那么我们需要编译所有引用这个头文件的C文件,并且链接可执行文件
   很明显能完成这个功能的就是Makefile了,在工程目录下创建名为"Makefile"的文件,文件名一定要叫做
“Makefile”!!!区分大小写!

Makefile和C文件是处于同一个目录的,在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”键!不要使用空格键!这是
Makefile 的语法要求,编写好 Makefile后就可以使用make命令来编译我们的工程了,直接在命令行中输入
make即可,make命令会在当前目录下查找是否存在"Makefile"文件,如果存在就会按照Makefile里定义的
编译方式进行编译。
   使用make编译完以后就会在当前工程目录下生成各种.o和可执行文件,说明我们编译成功了。
   使用make命令可能会提示Makefile *** missing separator  错误来源有两点:
    Makefile中命令缩进没有使用TAB键
    VI/VIM使用了空格代替TAB,修改文件/etc/vim/vimrc,在文件最后加上set noexpandtab
   接下来我们讲解一下上面代码是什么意思。
   3.4Makefile语法
   Makefile里面是由一系列规则组成的,这些规则格式如下:
    目标...:依赖文件集合……
       命令1
       命令2
       ……
    main: main.o input.o calcu.o
        gcc -o main main.o input.o calcu.o
   这条规则的目标是main,main.o、input.o和calcu.o是生成main的依赖文件,如果要更新目标main,就必须
先更新它的所有依赖文件,如果依赖文件中的任何一个有更新,那么目标也必须更新,“更新”就是执行一遍
规则中的命令列表。
   命令列表中的每条命令必须以TAB键喀什,不能使用空格!
   make命令会为Makefile中的每个以TAB开始的命令创建一个Shell进程去执行。
   了解了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

   代码共有5条规则,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 的执行过程:
1、make 命令会在当前目录下查找以 Makefile(makefile 其实也可以)命名的文件。
2、当找到 Makefile 文件以后就会按照 Makefile 中定义的规则去编译生成最终的目标文件。
3、当发现目标文件不存在,或者目标所依赖的文件比目标文件新(也就是最后修改时间比目标文件晚)的话
就会执行后面的命令来更新目标。
   这就是 make 的执行过程,make 工具就是在 Makefile 中一层一层的查找依赖关系,并执行
相应的命令。编译出最终的可执行文件。Makefile的好处就是“自动化编译”,一旦写好了Makefile
文件,以后只需要一个 make 命令即可完成整个工程的编译,极大的提高了开发效率。

   3.4.2Makefile变量
   跟C语言一样Makefile也支持变量,例如上面第一个规则就输入了两边.o文件,如果Makefile复杂的时候
这种重复输入的工作就会非常费时间,而且容易输错,所以Makefile加入了变量支持。不像C语言中变量有
int、char等,Makefile中的变量都是字符串!类似C中的宏定义。
    #Makefile变量的使用
    objects = main.o input.o calcu.o
    main: $(objects)
        gcc -o main $(objects)
   Makefile中注释开头要用 # ,第二行我们定义了一个变量objects,然后变量的引用方法时“$(变量名)”,
我们赋值用的是=,还有其他两个“:=”“?:”我们来看看三种赋值符的区别
1、赋值符“=”
   使用=在给变量赋值的时候,不一定要用已经定义好的值,也可以使用后面定义的值:
    name = zzk
    curname = $(name)
    name = zuozhong

    print:
        @echo curname: $(curname)
   在Makefile要输出一串字符的话使用“echo”,就和printf一样,这里echo前面加了个@符号,因为
Make在执行的过程中会自动输出命令执行过程,在命令前面加上@的话就不会输出命令执行过程。
使用“make print”执行上述代码:
    我们得到结果:curname: zuozhong
   也就是变量最后一次赋值的姐u哦个,则就是=号的神奇之处。借助另一个变量,可以将变量的真实值推到
后面去定义。也就是变量真实值取决于它所引用的变量的最后一次有效值。
2、“:=”
   如果把上面代码改成curname := $(name)
    我们会得到结果:curname: zzk
   这就说明:=不会使用后面定义的变量,只能使用前面已经定义好的。这就是=和:=两个的区别
3、“?=”
   这是一个很有用的赋值符,比如如下代码:
    curname ?= zuozhong
   上述代码意思就是:如果变量curname前面没有被赋值,就会被赋为zuozhong,如果前面已经赋值了,
就不会赋值。
4、变量追加“+=”
    objects = main.o input.o
    objects += calcu.o

   3.4.3Makefile模式规则
   上述Makefile例程,每个C文件都要写一个对应的规则,如果工程中C文件很多的话显然不能这么做。
为此,我们可以使用Makefile中的模式规则,通过模式规则我们就可以使用一条规则来将所有的.c文件编译
为对应的.o文件。
   模式规则中,至少在规则的目标定义中要包含“%”某则就是一般规则,目标中的“%”表示对文件名的
匹配,“%”表示长度任意的非空字符串,比如“%.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
   但到这里还不能运行,因为第六行的#命令还没写,这里我们又需要借助另外一种强大的变量——自动化变量

   3.4.4Makefile 自动化变量
   上面讲的规则模式中,目标和依赖都是一系列文件,每一次对模式规则进行解析的时候都会是不同的目标
和依赖文件,而命令只有一行,如何通过一行命令来从不同的依赖文件中生成对应的目标?
   自动化变量就是完成则个功能的,所谓自动化变量就是这种变量会把模式中所定义的一系列文件自动的
挨个去除,直至所有的符合模式的文件都取完,自动化变量只应该出现在规则的命令中。
常用自动化变量:
$@    规则中的目标集合,在模式规则中,如果有多个目标的话,$@表示匹配模式中定义的目标集合
$<    依赖文件集合中的第一个文件,如果依赖文件是以%定义的,那么$<就是符合模式的一系列文件
$^    所有依赖文件的集合,使用空格分开,如果依赖文件中有多个重复的文件,$^会去除重复的依赖文件
其他自动化变量:
$%    当目标是函数库的时候表示规则中的目标成员名,如果目标不是函数库文件,那么其值为空
$?    所有比目标新的依赖目标集合,以空格分开
$+    和$^类似,但是当依赖文件重复的话不会去除重复的依赖文件
$*    表示目标模式中%及其之前的部分,如果目标是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,可以明显感觉比之前的精简了很多,核心就在于使用了规则模式
和自动化变量。

   3.4.5Makefile伪目标
   Makefile有一种特殊的目标——伪目标,一般目标名都是要生成的文件,而伪目标不代表真正的目标名,
在执行make时通过指定这个伪目标来执行所在规则的定义的命令。
   使用伪目标主要是为了避免Makefile中定义的执行命令的目标和工作目标下的实际文件出现名字冲突,
有时候我们需要编写一个规则来执行一些命令,但是这个规则不是用来创建文件的,比如前面的
    clean:
        rm *.o
        rm main
   上述规则中并没有创建文件clean的命令,当输入make clean后就会执行命令。
   可以如果在工作目录创建了个名为“clean”的文件就不一样了,当执行make clean时,规则因为没有依赖
文件,所以目标被认为是最新的,因此下面的命令也不会执行,我们预先设想的清理工程的功能也就无法完成。
为了避免这个问题,我们可以将clean声明为伪目标,声明方法如下:
    .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

以后不管当前目录下是否存在名为“clean”的文件,输入make clean就总能执行下面的命令了。

   3.4.6Makefile条件判断
   在C语言中我们通过条件判断语句来根据不同的情况执行不同的分支,Makefile也支持条件判断,语法:
<条件关键字>
    <命令>
endif
以及:
<条件关键字>
    <命令>
else
    <命令>
endif

其中条件关键字有4个:ifeq、ifneq、ifdef和ifndef,ifeq用来判断是否相等,ifneq判断是否不相等
ifeq(... , ...)
ifeq "..." , "..."
ifeq '...' , "..."
而ifdef和ifndef用法:
ifdef<变量名>
如果变量名值非空,那么表达式为真。变量名同样可以是一个函数的返回值。ifndef则相反。

   3.4.7Makefile函数使用
   Makefile中的函数是已经定义好的,直接可以使用,不支持我们自定义函数。make所支持的函数不多,但
绝对够我们使用的了,函数的用法如下:
    $(函数名 参数集合)
    或者
    ${函数名 参数集合}
   可以看出,调用函数和调用普通变量一样,使用符号$来表示。参数集合是函数的多个参数,参数之间用逗号
隔开,函数名和参数之间用空格隔开。接下来我们介绍几个常用的函数,其他的函数可以参考《一起写Makefile》
1、函数subst
   函数subst用来完成字符串替换,调用形式:
    $(subst<from>,<to>,<text>)
   此函数功能是将字符串text中的from内容替换为to,函数返回被替换以后的字符串。
   例如:
    $(subst zzk,ZZK,my name is zzk)
   把字符串中的zzk换为了ZZK。

2、函数patsubst
   函数patsubst用来完成模式字符串替换,使用方法如下:
    $(patsubst <pattern>,<replacement>,<text>)
   此函数查找字符串text中的单词是否符合模式pattern,如果匹配就用replacement来替换掉,pattern可以使用
通配符%,表示任意长度的字符串,函数返回值就是替换后的字符串。如果replacement也包含%,那么
replacement中的%将是pattern中的那个%所代表的字符串。
    $(patsubst %.c,%.o,a.c b.c c.c)
    将字符串“a.c b.c c.c”中所有符合%.c的字符串替换为%.o,替换完后就是“a.o b.o c.o”

3、函数dir
   函数dir用来获取目录,使用方法如下:
    $(dir <names..>)
   此函数用来从文件名序列names中提出目录部分,返回值是文件名序列names的目录部分,比如:
    $(dir </src/a.c>)
    提取文件/src/a.c的目录部分,也就是"/src"

4、函数notdir
   提取文件名,用法如下
    $(notdir </src/a.c>)
    提取文件中非目录的部分,也就是文件名a.c

5、函数foreach
   用来完成循环,用法:
    $(foreach <var>,<list>,<text>)
   此函数的意思就是把参数list中的单词逐一取出来放到参数var中,然后再执行text所包含的表达式。
每次text都会返回一个字符串,循环的过程中,text中所包含的每个字符串会以空格隔开,最后当整个循环
结束时,text返回的每个字符串所组成的整个字符串将会是foreach函数的返回值

6、函数wildcard
   通配符“%”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动
展开,这个时候就要用到wildcard了,使用方法:
    $(wildcard PATTERN...)
    比如:
    $(wildcard *.c)
    上面代码就是用来获取当前目录下所有的.c文件,类似"%"

关于Makefile的相关内容就讲解到这里,只是对Makefile做了基本的讲解,确保能完成后续的学习,还有大量
的知识并没有涉及,有兴趣可以专门深入学习Makefile。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值