gcc与Makefile

2023-05-16

一、gcc命令

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

1.安装

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

sudo apt-get install build-essential

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

gcc -v

输出结果如下
在这里插入图片描述
可以看到我的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 -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
在这里插入图片描述
可以看到,通过Makefile完成了一系列代码编译与链接,假如此时我修改了cal.c,把a+b换成a*b,此时再次make
在这里插入图片描述

可以看到,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,可以得到如下结果
在这里插入图片描述
首先我们来对比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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

gcc与Makefile 的相关文章

随机推荐

  • Bug:Warning: [antd: ***] overlay is deprecated. Please use menu instead.以及解决方案

    Ant Design Pro umi4Bug记录 xff1a Bug xff1a Warning antd overlay is deprecated Please use menu instead 解决方案 xff1a yarn add
  • 前端初学者的Ant Design Pro V6总结(上)

    前端初学者的Ant Design Pro V6总结 xff08 上 xff09 一 UI组件开发流程 61 gt 通用 xff08 异步 xff09 函数useEmotionCss 定义CSSuseModel获取全局状态useCallbac
  • 关于C语言在VS2017上开头格式

    span class token macro property span class token directive keyword include span span class token string lt stdio h gt sp
  • Java中Long和long的区别

    Java中Long和long的区别 Java的数据类型分为两种 xff1a 1 基本类型 xff1a byte 8 short 16 int 32 long 64 float 32 double 64 char 16 boolean 1 2
  • Windows 10 下如何彻底关闭 Hyper-V 服务

    CMD运行bcdedit set hypervisorlaunchtype off 恢复使用 xff1a bcdedit set hypervisorlaunchtype auto
  • 从百度运维实践谈“基于机器学习的智能运维”

    清华大学计算机系副教授裴丹于运维自动化专场发表了题为 基于机器学习的智能运维 的演讲 xff0c 上篇参看 科研角度谈 如何实现基于机器学习的智能运维 文章 xff0c 此为下篇 从百度运维实践谈基于机器学习的智能运维 以下为演讲实录 xf
  • python通过udp传输图片

    首先要了解UDP的工作模式 对于服务器 xff0c 首先绑定IP和端口 xff0c 本机测试的时候可以使用127 0 0 1是本机的专有IP xff0c 端口号 大于1024的是自定义的 xff0c 所以用大于1024的端口号 xff0c
  • D435i跑通ORB-SLAM2

    这篇文章主要记录我的实现过程 根据官方安装文档 xff0c 并参考以下两篇博客 xff0c 可以比较顺利的实现 xff1a Realsense D435i 在ubuntu上安装SDK与ROS Wrapper 运行ORB SLAM2 RTAB
  • 通俗理解滑模变结构控制(1)

    这里写自定义目录标题 1 什么是滑模变结构控制2 滑模变结构的一些基本知识3 滑模控制器设计4 滑模控制器例子 1 什么是滑模变结构控制 在开始介绍滑模变结构控制之前 xff0c 最好先学习一些线性控制的基础 xff0c 知道什么是李亚普诺
  • 终端滑模(Terminal滑模)理解

    一 什么是终端滑模 在前面所介绍的滑模控制中 xff0c 我们是选取了一个线性的滑模面s xff0c 使系统达到滑模面后 xff0c 误差逐渐收敛到0 xff0c 收敛的速度可以通过调节滑模面的参数来实现 后来人们为了使滑模控制有更好的性能
  • PX4环境git submodule update --init --recursive失败的解决办法

    最近开始搭建PX4环境 xff0c 搭建是需要从github下载工程 xff0c 然后使用语句git submodule update init recursive更新工程子模块 xff0c 但往往由于网络原因这一步需要很久 xff0c 甚
  • 变结构滑模控制抖振处理(1)------动态滑模法

    1 什么是动态滑模 从前面一些关于滑模的介绍 xff0c 我们知道 xff0c 在设计滑模控制器时 xff0c 避不开的问题就是抖振 至于抖振的产生 xff0c 很大程度上是由于一般滑模控制器的控制律u是一个不连续的函数 xff0c u中往
  • 四旋翼双环PID控制

    在我上篇博客 四旋翼无人机Matlab建模 中 xff0c 我建立了四旋翼的模型 xff0c 并在simulink中搭建了仿真 xff0c 但并没有设计控制器 本章便针对四旋翼设计最常见的串级PID控制器 xff0c 本篇文章主要从两个部分
  • PX4代码解析(1)

    前言 做pixhawk飞控有一段时间了 xff0c 但在学习过程中遇到许多困难 xff0c 目前网上找不到比较完整的PX4学习笔记 xff0c 我打算结合自己理解 xff0c 写写自己对PX4源码的理解 xff0c 不一定对 xff0c 只
  • PX4代码解析(2)

    前言 在大致了解PX4代码架构后 xff0c 我们需要了解PX4的通信机制 在PX4代码架构中 xff0c 每通信总线主要分为两个部分 xff0c 一是内部通信总线uORB 即PX4内部进程通信采用的协议 xff0c 例如PX4内部姿态控制
  • px4代码解析(3)

    前言 在使用PX4飞控时 xff0c 我们难免要对其进行二次开发 xff0c 例如修改控制算法 xff0c 添加新的传感器 xff0c 这时需要在代码中定义属于自己的消息 本节主要分享一下如何在PX4代码中添加自己的消息 一 消息添加与配置
  • 七月在线NLP千元课程笔记

    xfeff xfeff NLP七月在线 照着PDF的内容解释 第一课 NLP基础知识 Python基础知识7分钟 正则表达式验证工具 https regexr com v1 基本字符 匹配除了换行符外所有字符 d匹配所有数字 能找到所有数字
  • PX4代码解析(4)

    一 引言 PX4程序是基于实时操作系统 xff08 Real time operating system RTOS xff09 的上层应用程序 xff0c PX4飞控程序的很多重要模块都是在Nuttx操作系统的调度下运行的 因此 xff0c
  • PX4代码解析(5)

    一 前言 我所讨论的PX4代码是基于v1 11版本 xff0c 该版本与之前的版本有不少不同 xff0c 其中一个比较大的区别在于新版本大部分用到了C 43 43 中模板 xff0c 使得代码没有以前那么容易理解 xff0c 因此我在后面介
  • gcc与Makefile

    一 gcc命令 在linux开发编程过程中 xff0c 需要对完成的代码进行编译运行 xff0c 这时我们便需要用到gcc命令 xff0c 下面我介绍gcc的安装与使用 1 安装 在ubuntu系统下终端中输入下面的命令 sudo apt