Linux基础开发工具(三)gcc/g++与make/makefile篇

本文讲解 Linux 中 gcc/g++ 编译 C/C++ 代码的用法,以及 make 与 Makefile 工程构建,包括依赖规则、伪目标、自动变量,并且介绍批量编译的实现方式,方便管理多文件 C 语言项目。

目录

一、gcc/g++

1.简介

2.基础语法格式

常用选项汇总

3.示例演示

[演示 1:一次性编译](#演示 1:一次性编译)

[演示 2:分步编译(多文件工程原理)](#演示 2:分步编译(多文件工程原理))

[演示 3:预处理(-E)](#演示 3:预处理(-E))

[演示 4:生成汇编代码(-S)](#演示 4:生成汇编代码(-S))

[演示 5:带调试信息编译(gdb 调试用)](#演示 5:带调试信息编译(gdb 调试用))

二、make/makefile

[1.make 是什么](#1.make 是什么)

[2.Makefile 是什么](#2.Makefile 是什么)

[3.核心概念:依赖关系 & 依赖方法](#3.核心概念:依赖关系 & 依赖方法)

[1. 依赖关系](#1. 依赖关系)

[2. 多规则联动(Makefile 会自动递归推导依赖)](#2. 多规则联动(Makefile 会自动递归推导依赖))

[4.伪目标 .PHONY](#4.伪目标 .PHONY)

什么是伪目标

[5.Make 自动变量](#5.Make 自动变量)

6.批量编译

变量解析

批量获取文件的两种写法

三、总结


一、gcc/g++

1.简介

gcc:C 语言编译器 ,用于编译 .c 源码

g++:C++ 编译器 ,用于编译 .cpp/.cc/.cxx,内部底层依然调用 gcc,自动链接 C++ 标准库

.c 文件推荐用 gcc

.cpp 文件推荐用 g++

核心用途

注:该部分可结合上一篇博客:Linux基础开发工具(二)程序编译与动静态库篇,深入理解。

将 C/C++ 源代码,经过预处理 → 编译 → 汇编 → 链接,生成可执行程序

支持生成目标文件、静态库、动态库

支持调试、优化、警告检查

2.基础语法格式

gcc 选项 源文件 -o 输出文件名

g++ 选项 源文件 -o 输出文件名

常用选项汇总

表格

参数 作用
-o filename 指定输出文件名,不加默认生成 a.out
-c 只编译汇编,不链接 ,生成 .o 目标文件
-E 只做预处理,输出预处理后的代码
-S 生成汇编代码 .s,不汇编、不链接
-g 添加调试信息,gdb 调试必须加
-O0 / -O1 / -O2 / -O3 代码优化级别,O0 不优化(调试用),O3 最高优化
-Wall 打开所有警告,强烈推荐,帮你发现代码隐患
-I路径 指定头文件搜索目录(大写 i)
-L路径 指定库文件搜索目录
-lxxx 链接库,如 -lpthread 线程库

3.示例演示

gcc 示例(g++同理)

新建 3 个文件:main.c、calc.c、calc.h

calc.h

cpp 复制代码
#pragma once
int add(int a, int b);
int sub(int a, int b);

calc.c

cpp 复制代码
#include "calc.h"

int add(int a, int b)
{
    return a + b;
}

int sub(int a, int b)
{
    return a - b;
}

main.c

cpp 复制代码
#include <stdio.h>
#include "calc.h"

int main(void)
{
    int a = 10, b = 3;
    printf("a + b = %d\n", add(a, b));
    printf("a - b = %d\n", sub(a, b));
    return 0;
}

演示 1:一次性编译

bash 复制代码
gcc main.c calc.c -o calc_demo -Wall

运行

复制代码
./calc_demo

输出:

复制代码
a + b = 13
a - b = 7

演示 2:分步编译(多文件工程原理)

  1. 只编译,不链接,生成 .o 目标文件
bash 复制代码
gcc -c main.c -o main.o -Wall
gcc -c calc.c -o calc.o -Wall
  1. 链接目标文件,生成可执行程序

    gcc main.o calc.o -o calc_demo

演示 3:预处理(-E)

复制代码
gcc -E main.c -o main.i

打开 main.i,可以看到头文件展开、宏处理后的完整源码。

演示 4:生成汇编代码(-S)

复制代码
gcc -S main.c

生成 main.s 汇编文件。

演示 5:带调试信息编译(gdb 调试用)

bash 复制代码
gcc -g main.c calc.c -o calc_demo -Wall

二、make/makefile

1.make 是什么

make 是 Linux 下工程构建工具 。 当项目源码文件很多时,如果每次修改代码都手动敲很长的 gcc 编译命令,非常麻烦。 make 会读取 Makefile 文件,自动根据文件的修改时间,判断哪些文件需要重新编译,只编译改动过的文件,避免全部源码重复编译,提升编译效率。

make 本身不是编译器,它只是工具,底层依然调用 gcc/g++。

2.Makefile 是什么

Makefile 是make工具读取的配置脚本文件 (文件名固定:Makefile 或者 makefile)。 在这个文件里,我们写清楚工程的文件依赖关系、编译命令 。 在终端直接输入 make,make 就会自动解析 Makefile,执行对应的编译指令。

3.核心概念:依赖关系 & 依赖方法

1. 依赖关系

规则格式:

bash 复制代码
目标: 依赖文件列表
	依赖方法(命令,**必须Tab缩进,不能用空格**)

目标 :你想要生成的文件(比如可执行程序、.o目标文件)

依赖文件:生成这个目标,所需要的源码 / 头文件。如果依赖文件的修改时间比目标新,代表文件改动,需要重新执行命令。

依赖方法:生成目标要执行的 shell 命令(gcc 编译命令)

想要生成【目标】,必须保证【依赖文件】存在;依赖文件更新了,就执行【依赖方法】重新构建目标。

重要:Makefile 中命令行前面必须是 Tab 缩进,不能使用空格 ,否则会报missing separator语法错误。

示例:

bash 复制代码
calc_demo: main.o calc.o
	gcc main.o calc.o -o calc_demo -Wall

目标:calc_demo(最终可执行程序)

依赖:main.o 和 calc.o

依赖方法:gcc main.o calc.o -o calc_demo -Wall

含义:生成calc_demo需要main.o、calc.o;只要任意一个.o文件改动,就执行这条链接命令。

2. 多规则联动(Makefile 会自动递归推导依赖)

bash 复制代码
calc_demo: main.o calc.o
	gcc main.o calc.o -o calc_demo -Wall

main.o: main.c calc.h
	gcc -c main.c -Wall

calc.o: calc.c calc.h
	gcc -c calc.c -Wall

执行make时的逻辑:

  1. 找目标calc_demo,需要main.o、calc.o

  2. 发现main.o需要main.c calc.h,判断源码是否更新,更新就执行gcc -c编译生成 main.o

  3. 同理处理 calc.o

  4. 两个.o 全部准备好,执行链接命令,生成可执行程序

优势:只改动calc.c,只会重新编译calc.o,不会重新编译 main.o

4.伪目标 .PHONY

什么是伪目标

伪目标不是文件 ,只是一个标签,用来执行一组命令,不会生成对应的文件。 最常用的就是 clean:用来删除编译产物(.o、可执行程序)。

如果不声明伪目标: 如果你的目录里刚好存在一个名叫clean的文件,make 会认为 clean 文件已经存在,就不会执行 clean 对应的删除命令,出现 bug。

用 .PHONY 声明伪目标,告诉 make:clean不是文件,永远执行命令。

bash 复制代码
.PHONY: clean
clean:
	rm -f *.o calc_demo

使用命令:

bash 复制代码
make clean

执行效果:删除所有.o目标文件和可执行程序,清理工程。

5.Make 自动变量

表格

自动变量 含义
$@ 当前规则的目标文件
$^ 当前规则全部依赖文件
$< 当前规则第一个依赖文件
bash 复制代码
$(BIN): $(OBJ)
	$(CC) $(LFLAGS) $@ $^

$@ = proc.exe(目标)

$^ = myproc.o(所有依赖)

bash 复制代码
%.o : %.c
	$(CC) $(FLAGS) $<

%.o : %.c:模式规则,匹配任意 xxx.o,依赖同名 xxx.c

$<:代表对应的.c源文件

6.批量编译

为什么要批量编译?

不用手动写大量重复编译命令

如果项目有很多源码,比如 main.c calc.c func.c log.c。 不使用批量编译: 你需要手动一条一条写 gcc 编译指令,文件越多,Makefile 越长,新增源码就要新增一条规则。

如何批量编译?

bash 复制代码
BIN = proc.exe
CC = gcc
# SRC = $(wildcard *.c)
SRC = myproc.c  # SRC = $(shell ls *.c) 列出当前所有.c文件
OBJ = myproc.o  # OBJ = $(SRC:.c=.o) 替换,把.c改成.o

LFLAGS = -o
FLAGS = -c
RM = rm -f

$(BIN): $(OBJ)
	$(CC) $(LFLAGS) $@ $^

%.o : %.c
	$(CC) $(FLAGS) $<

.PHONY: clean
clean:
	$(RM) $(OBJ) $(BIN)

.PHONY: test
test:
	@echo $(SRC)
	@echo $(OBJ)

变量解析

bash 复制代码
BIN = proc.exe	# 最终生成的可执行程序名称
CC = gcc			# 指定编译器
SRC = myproc.c	# 源文件
OBJ = myproc.o	# 目标文件

LFLAGS = -o		# 链接参数,用来指定输出文件名
FLAGS = -c		# 编译参数:只编译,不链接,生成.o目标文件
RM = rm -f		# 删除命令,-f 强制删除,不提示

批量获取文件的两种写法

1. SRC = $(wildcard *.c)``wildcard是 Make 内置函数,自动扫描当前目录,获取所有后缀为.c 的源文件,实现批量搜集源码。

2. OBJ = $(SRC:.c=.o)后缀替换语法 ,把 SRC 列表中所有文件后缀.c批量替换为.o,自动生成目标文件列表。

开启批量编译:注释掉SRC = myproc.c、OBJ = myproc.o,启用下面两行:

复制代码
SRC = $(wildcard *.c)
OBJ = $(SRC:.c=.o)

开启后,目录下所有.c文件会被自动批量编译,新增 c 文件无需修改 Makefile。


三、总结

gcc/g++ :C/C++ 编译器,源码需要经过预处理、编译、汇编、链接四步生成可执行程序;掌握-o、-c、-Wall、-g等常用编译选项,支持一次性编译和分步编译两种方式。

make & Makefile :make是工程构建工具,底层调用 gcc,依靠文件时间戳实现增量编译,只编译改动过的文件;Makefile是配置脚本,核心由目标、依赖关系、依赖方法构成,命令行必须 Tab 缩进。

伪目标.PHONY:用来定义标签(如 clean 清理、test 调试),不是实体文件,避免和本地文件重名导致规则失效。

自动变量 :$@、$^、$<简化 Makefile 书写;借助wildcard和后缀替换语法可以实现批量编译,自动收集全部源码,新增 C 文件无需修改 Makefile,适合多文件项目。

相关推荐
Sayai1 小时前
Neo4j 社区版高可用实战:DRBD 块级复制 + Keepalived 虚拟 IP 冷备切换(附启停 SOP 与脑裂防范)
运维·neo4j·keepalived·高可用·drbd
爱吃辣椒的小豆豆1 小时前
Linux-sshd-实战-白名单与跳板机
linux·运维·服务器
气质、小青年!1 小时前
进程1学习
linux·学习
强哥聊算力2 小时前
《算力江湖·国产GPU突围:摩尔线程故事》第四集:从图形到AI:摩尔线程为什么必须冲进大模型赛道?
运维·服务器·人工智能·ai·gpu算力
blueSatchel2 小时前
framebuffer注册源码解读
linux·嵌入式
2401_862880822 小时前
嵌入式Linux系统移植与驱动开发完整指南
linux·c语言·驱动开发
坤坤子吖2 小时前
Linux 线程同步与互斥——互斥锁
linux·服务器·c++·笔记·学习
a5986522 小时前
ARM服务器部署Docker云手机,多开实例性能实测
服务器·arm开发·docker·云手机
TDTC_XQ2 小时前
Linux网络编程accept的讲解
linux·服务器·c++