linux下C编写及编译、运行、Makefile的使用
2、gcc编译代码:gcc main.c 编译完成后默认生成a.out 的可执行文件,执行方法:“./+可执行文件”。使用-o 可指定生成的可执行文件名字:gcc main.c -o main
file +文件名 如file a.out 可查看该可执行文件运行于哪个架构。
(1)、编译流程:预处理、编译、汇编和链接。预处理就是展开所有头文件、替换程序中的宏、解析条件编译并添加到文件中。编译是将经过预编译处理的代码编译成汇编代码,也就是我们常说的程序编译。汇编就是将汇编语言文件编译成二进制目标文件。链接就是将汇编出来的多个二进制目标文件链接在一起,形成最终的可执行文件,链接的时候还会涉及到静态库和动态库等问题。
3、gcc命令——gcc [选项] [文件名字]
主要参数:-c:只编译不链接为可执行文件,编译器将输入的.c 文件编译为.o 的目标文件。
四、Makefile
1、简介:当有多个文件的时候用终端输入 GCC 命令的方法显然是不现实的。为此提出了一个解决大工程的编译工具:make,描述哪些文件需要编译、哪些需要重新编译的文件就叫做Makefile,跟脚本文件一样, 也可以执行系统命令。只需要一个 make命令即可完成整个工程的编译。
2、gcc编译多个.c文件示例:假设我们有两个.c文件(calcu.c,input.c),以及对应的头文件(calcu.h,input.h),还有一个main.c文件。此时我们可以使用命令:gcc main.c calcu.c input.c -o main即可对这几个文件进行编译生成可执行文件mian。使用这种方式编译时,即使只有一个文件改变也会编译所有文件。
但我们想要的是第一次编译整个工程,当工程比较大时,为节约时间,后面只编译被修改过的文件。命令如下:
假如现在修改了 calcu.c 文件,只需要将 caclue.c 这个文件重新编译成.o 文件,然后在将所有的.o 文件链接成可执行文件即,命令如下:
这样存在一个问题,如果修改的文件多,我们可能不记得哪个文件被修改过,然后忘记编译,为此我们需要这样一个工具:
图1
(1)、Makefile语法规则:
比如下面这条规则:
上面图1我们编写的Makefile代码中一共有5条规则,1~2行:第一条规则,3~4行第二条规则,5~6行第三条规则,7~8行第四条规则,10~12行第五条规则。其执行命令如下:
a、第一条规则依赖于 main.o、input.o 、 calcu.o 这个三个.o 文件,如果没有这三个文件(或者目标文件.o所依赖的.c文件比目标文件新)的话就会执行后面的命令来更新目标。
b、更新 main.o 的是第二条规则,第二条规则里面的命令为“gcc–c main.c”,即编译 main.c但不链接,生成 main.o,其它两个.o 文件同理。
编译:
编译时如果提示如下错误:可能原因为:(1)、Makefile命令缩进没有使用TAB键,(2)、VI编辑器中使用了空格代替TAB键,在/etc/vim/vimrc配置文件最后加上:set noexpandtab,修改完后随便改一个.C文件(加几个空格)再次编译试试。
编译成功如下 :会根据Makefile指示,在该目录下生成各种.O文件,及可执行文件,如果要清除这些文件则使用命令:make clean
(2)、Makefile 变量
上述Makefile语句中,main.o input.o 和 calcue.o 这三个依赖文件,我们输入了两遍,为避免重复输入,Makefile 支持变量。其变量都是字符串,类似 C 语言中的宏。
a、赋值符“=”
使用“=”给变量赋值的时候,不一定使用定义好的值,也可以使用后面定义的值,取决于它所引用的变量的最后一次有效值,如下:
按照C语言的经验我们会认为输出是:zzk,实际输出如下:
b、赋值符“:=” ——表示变量只能使用前面的赋值,不会使用后面的赋值,如上,改为":="后就会打印zzk,这就是“=”和“:=”两个的区别。
c、赋值符“?=”
表示如果变量 curname 前面没有被赋值,那么此变量就用现在的值“zuozhongkai”,如果前面被赋值过就用前面的值。
d、变量追加——使用符号“+=”
(4)、Makefile自动化变量
如何通过一行命令来从不同的依赖文件中生成对应的目标?自动化变量就是完成这个功能的,这种变量会把模式中所定义的一系列的文件自动的挨个取出,自动化变量只应该出现在规则的命令中,常用的自动化变量如下表 :
常用的三种:$@、$<、$^,使用自动化变量完成上述示例代码 中的 Makefile,使用命令:gcc -c $< 即可,如下:
至此,一个精简的Makefile文件就诞生了。
(5)、Makefile伪目标
Makefile 有一种特殊的目标——伪目标,为了避免 Makefile 中定义的执行命令的目标和工作目录下的实际文件出现名字冲突。一般目标名都是要生成的文件名字,而伪目标不是。
有时我们需要编写一些规则来执行一些命令,比如 clean:但是并不会创建clean文件,如果创建一个名为clean的文件,执行make clean时,规则因为没有依赖文件,因此后面的rm命令就不会执行,如下:
为避免这个问题,我们可将clean声明为伪目标,如下:
此时就算目录下有同名文件,使用make clean也能执行规则后的rm命令,如下:
(6)、Makefile条件判断
C 语言中通过条件判断语句来执行不同的分支,Makefile 也支持条件判断,语法有两种如下:
a、
b、
条件关键字有 4 个:ifeq、ifneq、ifdef 、 ifndef,这四个关键字其实分为两对、ifeq 与ifneq、ifdef 与 ifndef,先来看一下 ifeq 和 ifneq,ifeq 用来判断是否相等,ifneq 就是判断是否不相等。ifeq 用法如下:
则表示:如果"变量名"的值非空,表达式则为真,否则为假,“变量名”同样可以是一个函数的返回值。ifndef同上,含义相反。
(7)、Makefile 函数使用
类似 C 语言,Makefile 支持函数,但函数是已经定义好的,我们直接使用,不支持自定义。函数不多,但是绝对够用,用法如下:
$(函数名 参数集合) 或 ${函数名 参数集合}
调用函数和引用普通变量一样,都是使用$符号,参数集合是函数的多个参数,参数间用逗号隔开,函数名和参数之间用空格分开。
a、函数 subst
函数 subst 用来完成字符串替换,调用形式如:$(subst<from>,<to>,<text>) 此函数的功能是将字符串<text>中的<from>内容替换为<to>,函数返回被替换以后的字符串,如下:
把字符串“my name is zzk”中的“zzk”替换为“ZZK”,替换完成返回字符串“my name is ZZK”。
此函数查找字符串<text>中的单词是否符合模式<pattern>,如果符合就用<replacement>的模式替换掉,<pattern>可以使用通配符“%”,表示任意长度的字符串,函数返回值就是替换后的字符串。如果<replacement>中也包涵“%”,那么<replacement>中的“%”将是<pattern>中的那个“%”所代表的字符串,比如:
此函数是把参数<list>中的单词逐一取出来放到参数<var>中,然后再执行<text>所包含的表达式。每次<text>都会返回一个字符串,循环的过程中,<text>中所包含的每个字符串会以空格隔开,最后当整个循环结束时,<text>所返回的每个字符串所组成的整个字符串将会是函数 foreach 函数的返回值。
用法如下:
执行:make mytest 命令
五、X86架构和ARM架构的区别
1、简介:CPU只能在给定具体指令的情况下才能工作,不同架构的CPU能够识别的指令集不同。移动端及一些嵌入式系统上的应用并不是直接通过编写CPU指令来运行的,而是通过编程语言来编写的,编译器的目的就是将程序编译成不同指令集的指令,从而在ARM, X86等不同架构的CPU上运行。指令集就相当于软件和硬件之间的桥梁。
2、本质区别:(1)、X86架构——复杂指令集:电路单元丰富,指令丰富,处理特殊任务效率高,缺点:面积大、功耗高;
(2)、ARM架构——32位精简指令集(指令数有限):广泛运用于嵌入式系统,电路单元相对较少,制造工艺简单,功耗低(适用于移动通讯领域,便携式电子产品手机平板等),但实现复杂功能效率低(使用多个指令组合实现),ARM 公司本身并不制造 CPU ,而是将处理器架构授权给厂商。
3、性能:X86结构比ARM结构的系统在性能方面要强得多。其CPU随便就是1G以上、双核、四核等等、而ARM结构的CPU通常才几百兆,最近才出现1G左右的。
4、操作系统兼容性:几乎所有x86硬件平台都可以直接使用微软的视窗系统及现在流行的几乎所有工具软件;ARM系统几乎都采用Linux的操作系统,GOOGLE开发了开放式的Android系统后,才为ARM的发展提供了强大的支持和动力。
除此之外,还有一些其他架构的处理器,这里不再赘述。
六、Ubuntu交叉编译工具链安装(ARM)
1、交叉编译器安装
Ubuntu 自带的 gcc 编译器是针对 X86 架构的,要编译 ARM 架构的代码,就需要一个可以在 X86 架构的 PC 上运行并且编译 ARM 架构代码的 GCC 编译器,即交叉编译器。
安装ARM交叉编译链:执行 sudo apt-get install gcc-arm-linux-gnueabihf 命令,执行安装软件包命令前最好先执行: sudo apt-get update 命令来更新Ubuntu软件包库。
2、交叉编译器报错问题
使用如上编译器如果出现 error: unrecognized -march target: armv5
错误通常是因为 GCC 不支持 -march=armv5
选项,该选项指定生成代码运行在 ARMv5 指令集下。可能是因为 GCC 版本过低或是没有针对该指令集做过任何优化。可以使用 arm-linux-gnueabi-gcc 交叉编译工具链,它
支持ARMv5指令集。而上面的编译器在 ARMv7+ 上启用了硬件浮点支持。
3、设置交叉编译器环境变量,一般可在以下几个文件中进行:
(1)、/etc/environment
:系统级环境变量,对所有用户都有效
如:CROSS_COMPILE=/path/to/toolchain/bin/arm-linux-gnueabihf-
(2)、/etc/profile
:系统级环境变量,用户登录时执行,通过修改 PATH
等环境变量可以设置编译器的路径,用户一般配置这个文件。
(3)、~/.bashrc 用户级环境变量,每次 shell 启动时执行,通过修改 PATH
等环境变量可以设置编译器的路径
如:export CROSS_COMPILE=/path/to/toolchain/bin/arm-linux-gnueabihf-
(4)、/etc/bash.bashrc
:系统级 Bash 配置文件,在每次 shell 启动时执行,是 /root/.bashrc
的全局版本
配置完成需要执行 source +配置路径让更改生效,如:source /etc/profile
可使用 echo $PATH
命令来检查 PATH 环境变量中包含哪些路径。
4、如何修改只读文件权限:
如图我们再用vim编辑~/.bash_profile提示如下:
,可输入 :w !sudo tee % 强制保存,会提示警告,按提示退出即可,如果 :q 不能退出,使用 :qa! ,退出再打开可以看到已经更改的。谨慎使用(可能会导致数据丢失或系统损坏)。
安全起见可以先备份,可使用 sudo chmod a+w filename
更改只读文件的权限,编辑保存完文件后将可写权限删除sudo chmod a-w filename
恢复为只读状态。ls +文件名 -l 可查看权限。