gcc与Makefile

一、gcc命令

在linux开发编程过程中,需要对完成的代码进行编译运行,这时我们便需要用到gcc命令,下面我介绍gcc的安装与使用

1.安装

在ubuntu系统下终端中输入下面的命令

sudo apt-get install build-essential

该命令中,sudo表示使用管理员权限安装,需要输入系统的密码,安装完成后可以通过如下指令查看gcc版本

gcc -v

输出结果如下
gcc与Makefile_第1张图片
可以看到我的gcc版本为7.5.0,出现该界面则安装成功.

2.使用

gcc语法如下 g c c ( 选项 ) ( 文件名 ) gcc (选项) (文件名) gcc(选项)(文件名)具体选项如下表

选项 作用
-o 指定生成的输出文件
-E 仅执行编译预处理
-S 将C代码转为汇编代码
-wall 显示警告信息
-c 仅进行编译操作,不进行链接操作

下面以一个例子介绍gcc使用,首先在ubuntu创建一个test文件夹,并在文件夹下创建test.c文件

mkdir test
cd test
touch test.c

在test文件夹下可以看到test文件创建成功,打开test.c文件,输入如下代码

#include<stdio.h>

#define mynum1 12
#define mynum2 15

int main()
{
  int a = mynum1+mynum2;
  printf("测试结果为:%d",a);
  return 0;
}

程序编译一般经过这几个步骤:预处理,编译,汇编与链接。在预处理阶段,会处理以#开头的预处理命令,比如#include,#define等命令,处理完成后一般是生成.i文件,利用gcc对test.c进行预处理

gcc -E test.c -o test.i
//命令中test.c为源文件,-o参数指定预处理后的输出文件为test.i

打开test.i,找到文件最后,可以看到如下结果
gcc与Makefile_第2张图片
通过gcc -E命令解释了代码中#include,并且替换了#define的内容。
C语言编译的第二个步骤为编译,这一步是将C语言代码(上一步预处理后的.i文件)转换为汇编语言,具体命令如下

gcc -S test.i -o test.s
//命令中test.i为处理源文件,-o参数指编译后的输出文件为test.s

运行完该命令会产生.s汇编文件
C语言编译的第三个步骤为汇编,在这个阶段,汇编代码会生成二进制代码(二进制代码文件以.o为后缀名)

gcc -c test.s -o test.o

C语言编译的最后一个步骤为链接,在这个阶段,会链接找到依赖的库文件(静态与动态),将二进制文件链接为可执行文件,一般链接命令如下

gcc  [目标文件] -o [可执行文件] -l [动态库名]

由于这个例子没有动态库,可以直接

gcc  test.o -o test

运行过后你会生成可执行文件,./test执行,结果如图
在这里插入图片描述
如果利用gcc进行多文件编译,一般有两种编译方法,假设有文件test1.c与test2.c,第一种编译方法为多个文件一起编译

gcc  test1.c test2.c -o test

将test1.c与test2.c编译连接成test可执行文件
第二种编译方法为分别编译各个源文件,之后对编译输出文件进行链接

gcc -c test1.c //将test1.c编译为test1.o
gcc -c test2.c  //将test2.c编译为test2.o
gcc -o test1.o test2.o -o test//链接

一般而言,我们更多的使用第二种编译方式,主要原因如下:在一个很多文件的大型工程中,假如我们修改了其中一个文件,使用第一种编译方法就需要把所有文件都重新编译,而第二种编译方法只需要重新编译这个文件然后链接即可。

二、Makefile文件

在使用gcc命令时,我们往往需要对工程进行操作,一个工程又具有多个文件,如果按照上述编译方法,往往需要输入很多指令,而且修改文件也不方便,因此引入Makefile文件解决该问题。下面以一个例子简单讲解Makefile的编写与使用。首先建立一个文件夹,叫Makefiletest,然后在该文件夹里创建cal.c,cal.h,input.c,input.h,main.c一共5个文件,分别输入以下代码:

//cal.c
#include"cal.h"

int caltwonum(int a,int b)
{
	return a+b;
}
//cal.h
#ifndef _CAL_H
#define _CAL_H

int caltwonum(int a,int b);
#endif

//input.c
#include<stdio.h>
#include"input.h"

void inputint(int* a,int* b)
{
	printf("请输入两个整数:");
	scanf("%d %d",a,b);
	printf("\r\n");
}

//input.h
#ifndef _INPUT_H
#define _INPUT_H

void inputint(int* a,int* b);
#endif

//main.c
#include<stdio.h>
#include"input.h"
#include"cal.h"

int main()
{
	int a,b,sum;
	inputint(&a,&b);
	sum = caltwonum(a,b);
	printf("%d + %d = %d \r\n",a,b,sum);
	return 0;
}

同时在Makefiletest文件夹下创建Makefile文件,输入以下内容

#main是需要生成的目标文件,它依赖main.o input.o cal.o
main:main.o input.o cal.o
#gcc命令,通过main.o input.o cal.o生成main,语句前面要用TAB
	gcc -o main main.o input.o cal.o
#main.o是目标文件,依赖main.c文件,通过gcc -c main.c生成main.o文件
main.o:main.c
	gcc -c main.c
input.o:input.c
	gcc -c input.c
cal.o:cal.c
	gcc -c cal.c

#增加clean命令
clean:
#删除所有以.o结尾的文件,使用了通配符*
	rm *.o
#删除可执行文件main
	rm main

创建完成后输入make命令就可以编译文件,生成可执行文件main
gcc与Makefile_第3张图片
可以看到,通过Makefile完成了一系列代码编译与链接,假如此时我修改了cal.c,把a+b换成a*b,此时再次make
gcc与Makefile_第4张图片

可以看到,make仅仅重新编译了cal.c文件,不会整个工程全部编译。
其实,在上述Makefile中,我们可以利用一些Makefile语法简化编写。
1.变量,可以通过定义变量替换某些语句,例如对上述Makefile进行修改

#用objects替换main.o input.o cal.o
objects = main.o input.o cal.o
main:$(objects)
	gcc -o main $(objects)

在Makefile中,变量赋值有四种方式,下面分别讲解
1.简单赋值(:=)常规赋值方式,只对当前语句变量有效
2.递归赋值(=)赋值语句会影响多个变量,所有与目标变量相关的其他变量都会受影响
3.条件赋值(?=)如果变量没有定义,则使用符号中的值定义变量,如果该变量已经被赋值,则此语句无效
4.追加赋值(+=)原变量用空格隔开追加一个新值

下面以一段Makefile代码讲解不同赋值方式

x:=hjl
y:=hjl$(x)
x:=kk
x1=hjl
y1=hjl$(x1)
x1=kk
x2=lop
x2?=56
x3=12
x3+=$(x2)
test1:
	@echo "x == $(x)"
	@echo "y == $(y)"
	@echo "x1 == $(x1)"
	@echo "y1 == $(y1)"
	@echo "x2 == $(x2)"
	@echo "x3 == $(x3)"

编写好以后在终端输入make test1,可以得到如下结果
gcc与Makefile_第5张图片
首先我们来对比y与y1的输出,可以看到x采用简单赋值,后续x的改变并没有影响到y,而x1采用递归赋值,一旦x1改变将影响整个文件中与x1有关的变量
2.Makefile目标文件搜索
一个工程可能存在多个源文件,这些源文件不一定在同一个目录下,如果不在一个目录下,按之前Makefile的写法就会出错,我们需要告诉Makefile文件工程源文件在哪?因此Makefile引入了目标文件搜索的功能。
常见的搜索方式有VPATH与vpath,这两个一看仅仅只有大小写的区别,但实际上功能大不相同。首先是VPATH,你可以把它理解为一个环境变量,它的用法如下

VPATH := 搜索路径
#例子
#如果Makefile在当前文件夹没有找到需要文件,则去src文件夹下寻找
VPATH := src
#多个路径之间需要:或者空格隔开,下面这句话表示先搜索hjl文件夹,再搜索src文件夹
VPATH := hjl src

当Makefile在当前文件下找不到需要的文件时,会访问到VPATH变量中的目录下寻找该文件,相当于VPATH给Makefile提供了更多搜索位置
vpath相当于加了条件的VPATH,使用语法如下

#用法一
vpath 条件 路径
#例子
#从hjl文件夹搜索test.c文件
vpath test.c hjl
#多个搜索路径的情况,路径之间用空格或冒号隔开
vpath test.c hjl:src
#用法二
vpath
#清除所有已设置的文件路径

3.伪目标
在之前的例子中,我们在Makefile使用了clean命令,在终端中输入make clean,它会执行clean语句之下的命令,方便我们进行操作。伪目标在Makefile文件中起到了命令替代的作用,用一个简单语句代替原本复杂的命令,简化书写。此外,伪目标要避免与文件重名,一般在Makefile文件中可以用.PYTHON来指明一个目标为伪目标

.PYTHON : clean
clean:
	rm *.o

4.函数
Makefile为了方便用户使用,提供了一些函数给用户使用,使用语法如下

$(<function> <arguments>)
or
${<function> <arguments>}

function为函数名,argument为函数参数

三、更多Makefile学习

参考跟我一起写Makefile

你可能感兴趣的:(操作系统,linux,运维,服务器,gcc)