Linux学习(二):makefile

Linux学习(二):makefileLinux学习(二):makefile gcc gcc 是用来编译代码的编译器 编译完后有一些常见的输出文件: .a 静态库(文档) .c 需要预处理的C语言源代码 .h C语言源代码的头文件 .i

Linux学习(二):makefile

gcc

gcc 是用来编译代码的编译器 编译完后有一些常见的输出文件: .a 静态库(文档) .c 需要预处理的C语言源代码 .h C语言源代码的头文件 .i 经过预处理后的C语言源代码 .o 目标文件(经过汇编产生) .s 经过编译后产生的汇编语言代码

gcc编译过程:

首先对.c和.h文件进行预处理操作,生成.i文件,然后将其编译产生汇编语言代码.s,再对.s文件进行处理,生成目标文件.o,.o文件就是计算机能看懂,且可以执行的机器语言。

先创建文件写一个简单代码 在这里插入图片描述

#include <stdio.h>
int main()
{
    printf("HelloWorld");
}

预处理

预处理指令:

gcc -E helloWorld.c -o helloWorld.i

执行后多出一个.i文件 在这里插入图片描述 再通过ls -l指令查看文件详细内容,可以看到文件的大小,在经过预处理后由59B变为16335B,翻了好几倍 在这里插入图片描述 变大的原因:我们之前引用了头文件stdio.h,在执行预处理后,等同于将.h中的所有内容拷贝到了该文件,所以文件大小变大。

通过less helloWorld.i 可以查看该文件详细内容: 在这里插入图片描述

产生汇编

这里主要使用 -s 命令,可以看见下图能直接对.c 和 .i 文件进行操作,这是因为-s中已经集成了预处理操作 在这里插入图片描述

生成目标文件

同理,-o同样集成了预处理和转为汇编的操作,因此可以直接对.c文件进行操作: 在这里插入图片描述 我们再看文件的详细信息: 在这里插入图片描述 这里可以看到.o 文件要小于.s和.i文件,这是因为.o文件只是一个可执行文件,但是它不具备可执行的能力,.o文件是将汇编文件转为的机器码,也就是二进制文件。

执行文件

gcc可以直接读懂二进制.o文件,所以可以直接使用这种指令生成可执行文件:gcc helloWorld.o -o myHello

在这里插入图片描述 再看看myHello 的文件大小,再次变大,前面我们提到.o文件是可执行文件,但是不具备可执行能力。为了让它具备可执行能力,gcc需要在.o文件原来的基础上再添加一些配置,让它具备可执行文件。 在这里插入图片描述

题外话

可以看到,我们可以直接编译.c 生成可执行文件,那么,我们之前学的那些生成.i,.s,.o的过程有什么用呢。虽然现在的指令都集成好了,可以直接用,但是我们还是需要对这些过程做一些了解,因为c语言不同于java,Java是解释型语言,而c则是编译型语言,例如程序有时候出错可能不是错在.c文件,而是在其他文件中出了一些问题,所以这些基本性的内容仍然需要做一些了解。

此外,这种方法只试用于头文件为官方库的情况,如果用到了其他的库则这种一步会出错,遇到这种情况的做法到后面再说。 在这里插入图片描述

Makefile

概念:在我们使用gcc进行编译时,如果有很多个.c文件,那么我们就需要一条一条的输入指令,这样做太过麻烦,所以Makefile就应运而生。Makefile本质上就是一种脚本,帮我们简化了许多重复性的操作。

多文件编译

首先准备好多个.c文件和.h文件,这里准备的是加减除三个函数,用c语言简单写一下就可以。 在这里插入图片描述 然后myProject.c中写的是主函数:

#include <stdio.h>
#include "myadd.h"
#include "mysub.h"
#include "mymul.h"

void main()
{
    int n1 = 5;
    int n2 = 20;
    int n3 = 3;
    printf("%d\n", myAdd(n1, n2));
    printf("%d\n", mySub(n2, n3));
    printf("%d\n", myMul(n1, n2));
}

那么,怎样将几个文件编译起来呢,具体操作如下所示: 在这里插入图片描述 很显然,这样做的操作比较麻烦。需要执行很多指令,我们可以用makefile来进行操作。

makefile 规则

target:depend command

target:目标,可以是一个中间文件,也可以是最终的执行文件 depend:依赖,指要生成目标文件所需要的文件或目标 command:make需要执行的命令

比如在这个指令中:

gcc -c mymul.c -o mymul.o

mymul.c为依赖,mymul.o为目标

makefile 执行顺序: 默认执行第一条,在执行第一条时,先找所有的依赖文件,如果没有,继续往下找有没有脚本能生成这个依赖文件,如果有就会先执行下面生成依赖的语句,当需要的依赖齐全后再执行那个语句。 事例:

test:prog.o code.o
		gcc -o test prog.o code.o
prog.o:prog.c
		gcc -c prog.c -o prog.o
code.o:code.c
		gcc -c code.c -o code.o
clean:
		rm -f *.o

没有第一条命令中需要的prog.o 和 code.o文件时,就先往下执行,通过编译.c 文件得到了prog.o和code.o文件,然后再执行第一条命令,就可以生成需要的test文件。

简单的makefile实例

写一个makefile文档:

myProject: myProject.o myadd.o mysub.o mymul.o
	gcc *.o -o myProject
myProject.o: myProject.c
	gcc -c myProject.c -o myProject.o
myadd.o:myadd.c
	gcc -c myadd.c -o myadd.o
mysub.o:mysub.c
	gcc -c mysub.c -o mysub.o
mymul.o:mymul.c
	gcc -c mymul.c -o mymul.o
clean:
	rm -f *.o

如图所示,写好makefile文件后,直接输入make就可以执行已经写好的指令,十分方便 在这里插入图片描述 全部编译好后,如果想要清除掉没用的文件,如.o文件,可以使用在makefile中写好的文件make clean。

makefile变量和通配符

变量理解为字符串
OBJ = a b c —— 表示OBJ就是a b c 这三个,不能改变了
OBJ := a b c —— 表示OBJ是a b c 但可以用+=再去追加
OBJ += a b c —— 表示OBJ变量添加了d这一个变量
变量引用:
$(变量名)
通配符:
% —— 表示任意一个
* —— 表示所有
?—— 表示匹配一个未知的东西

所以,原来的代码就可以进化成这样了:

OBJ:=myadd.o mysub.o mymul.o
OBJ+=myProject.o

myProject:$(OBJ)
	gcc $(OBJ) -o myProject

*.o:*.c
	gcc -c *.c -o *.o

clean:
	rm -f *.o myProject

可见代码更加简洁,且后续修改也更加方便,但是还可以更加简洁。

$@ —— 表示目标文件 $^ —— 表示依赖文件

代码再次进化:

target=myProject
OBJ:=myadd.o mysub.o mymul.o
OBJ+=myProject.o

CC:=gcc
CFLAGS:=-c

$(target):$(OBJ)
	$(CC) $^ -o $@

*.o:*.c
	$(CC) $(CFLAGS) $^ -o $@

clean:
	rm -f *.o $(target)

这样后续修改直接改变量就可以了,非常方便

今天的文章Linux学习(二):makefile分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/17910.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注