当前位置: 首页 > news >正文

Makefile学习(三)- CFLAGS和LDFLAGS

一、简介

        在 Makefile 中,CFLAGS 和 LDFLAGS 是两个约定俗成的变量,分别用于传递编译阶段链接阶段的参数,二者分工明确,共同控制程序的构建过程。

二、CFLAGS

        CFLAGS(Compiler Flags),即编译阶段的参数。作用是给编译器(如 gccclang传递参数,控制 .c 源文件编译为 .o 目标文件的过程。

        常见参数如下:

-I/path指定头文件(.h)的搜索路径(非标准路径)
-Wall开启所有警告信息,仅提示警告,不影响编译过程
-Werror将警告视为错误,检测到警告会立即终止编译。通常与-Wall一起使用,如果单独使用通常不会报错(默认启用的警告极少)
-O2开启二级优化(平衡编译速度和程序性能
-g生成调试信息(用于 gdb 调试)
-c只编译不链接(生成 .o 文件,Makefile 中常用)
-D宏名定义宏(如 -DDEBUG 开启调试模式)

        -I

        用于指定头文件(.h)的搜索路径,告诉编译器在哪些目录中查找源代码中 #include 引用的头文件

        建设项目结构如下:

project/
├── a.c       
└── a.h      

        编写如下程序

### a.c#include <stdio.h>
#include "a.h"int main(void)
{printf("this is a\r\n");printf("A = %d\r\n",A);return 0;
}
#### a.h#ifndef A_H
#define A_H#define A 1#endif
### MakefileCC := gcc # 指定编译器
SRC := a.c # 指定源文件
OBJ := $(SRC:.c=.o) # 指定目标文件
.PHONY: allall: test @echo "所有目标构建完成"test: $(OBJ)@$(CC) -o $@ $^	%.o: %.c@$(CC) -o $@ -c $<clean:@rm -f $(OBJ) test

        此时make一下。没有问题。

        此时修改项目结构

project/
├── a.c       
└── include/└── a.h      

        直接make一下。

        提示.h文件未找到。此时在Makefile中增加CFLAGS -I参数。

CC := gcc # 指定编译器
SRC := a.c # 指定源文件
OBJ := $(SRC:.c=.o) # 指定目标文件
CFLAGS = -I./include # 指定头文件搜索路径为当前目录下的include文件
.PHONY: allall: test @echo "所有目标构建完成"test: $(OBJ)@$(CC) -o $@ $^	%.o: %.c@$(CC) $(CFLAGS) -o $@ -c $<clean:@rm -f $(OBJ) test

        重新make。正常。

        -Wall

        用于开启大部分常见的警告信息(虽然名字里有 "all",但并非所有警告,而是 GCC 认为最有用的一批警告)。它的主要作用是让编译器在编译过程中检查代码中潜在的问题,并给出警告提示,帮助开发者发现可能的错误、不规范的写法或逻辑漏洞,但不会阻止编译过程

        这里定义一个全局变量,但是不使用。

### a.c#include <stdio.h>static int aa;int main(void)
{printf("this is a\r\n");return 0;
}
CC := gcc # 指定编译器
SRC := a.c # 指定源文件
OBJ := $(SRC:.c=.o) # 指定目标文件
.PHONY: allall: test @echo "所有目标构建完成"test: $(OBJ)@$(CC) -o $@ $^	%.o: %.c@$(CC) $(CFLAGS) -o $@ -c $<clean:@rm -f $(OBJ) test

        make一下,并没有看到有报错。

        在makefile中加入-Wall参数。

CFLAGS = -Wall

        再次Make。可以看到,有报警提示aa未使用,但是还是继续完成了编译和链接的动作

        -Werror

        该参数是将所有已启动的警告,强制转化为错误,并立即终止编译过程不生成目标文件.o或可执行文件

        还是刚才的例子,在makefile中增加-werror参数

CFLAGS = -Wall -Werror

        可以看到,编译直接报错,并且停止了继续编译

        -O2

        -O2是一个优化级别选项,用于启动中等强度的代码优化,在编译速度和程序性能之间取得平衡,是实际开发中最常用的优化选项之一。

        -O2会让编译器对代码进行一系列优化处理,目标是提高程序的运行速度(减少执行时间)和降低内存占用,但会略微增加编译时间(因为优化过程需要更多计算)。

        与不优化(默认 -O0)相比,-O2 启用的典型优化包括:

  • 常量折叠:计算编译期可知的常量表达式(如 3+5 直接替换为 8
  • 循环优化:循环展开、循环变量递增优化、消除无效循环
  • 函数内联:将小型函数的代码直接嵌入调用处,减少函数调用开销
  • 指令重排:调整指令执行顺序,利用 CPU 流水线提高效率
  • 死代码消除:移除永远不会执行的代码(如 if (0) { ... } 中的内容)
  • 寄存器优化:更高效地使用 CPU 寄存器,减少内存访问

        GCC 提供多个优化级别,常用的有

-O0(默认)无优化,编译速度最快,适合调试(代码与源码对应性好)
-O0轻度优化,优化编译时间,对执行性能提升有限
-O0中度优化,平衡编译时间和运行性能(推荐在发布版本中使用)
-O0高度优化,启用更多激进优化(如循环向量化),可能导致程序体积增大或兼容性问题
-Os优化代码体积,适合嵌入式等存储空间有限的场景

        注意:启用 -O2 后,编译器会重排代码,可能导致调试时(如 gdb)源码行号与实际执行位置不匹配,因此调试阶段通常用 -O0 -g(无优化 + 调试信息)。

        -O2 是大多数生产环境的默认选择,既保证了性能提升,又避免了 -O3 可能带来的风险。

        -g

        -g 是用于生成调试信息的编译选项,这些信息会被嵌入到目标文件(.o)或可执行文件中,供调试工具(如 gdblldb 等)使用,帮助开发者定位代码中的错误。

        当使用 -g 选项时,编译器会在生成的二进制文件中加入:

  • 源代码的行号与二进制指令的映射关系
  • 变量名、函数名等符号信息
  • 数据类型、结构体定义等类型信息

        这些信息让调试工具能够将二进制指令 “翻译” 回对应的源代码,实现单步执行、变量查看、断点设置等调试功能。

        -c

        -c 是一个编译选项,用于指定只进行编译(Compile)过程,不执行链接(Link),最终生成目标文件(.o 文件)而非可执行文件。

        C/C++ 程序的构建通常分为两步:

  1. 编译(Compile):将 .c 源文件转换为 .o 目标文件(二进制中间文件,包含机器码和符号信息)。
  2. 链接(Link):将多个 .o 目标文件及依赖的库文件合并,生成最终的可执行文件。

  -c 的作用是只执行第一步,停止在生成 .o 文件的阶段,不进行后续的链接操作。

        -D

        -D 选项用于在编译时定义宏,相当于在代码中添加 #define 宏名 或 #define 宏名=值 的效果,但无需修改源代码,直接通过编译参数控制宏的定义。

CC := gcc # 指定编译器
SRC := a.c b.c # 指定源文件
# CFLAGS := -DRELEASE
CFLAGS := -DDEBUG
TARGET := testOBJ := $(SRC:.c=.o).PHONY: all cleanall: $(TARGET) @echo "所有目标构建完成"$(TARGET): $(OBJ)@$(CC) -o $@ $^	$(CFLAGS)%.o: %.c@$(CC) $(CFLAGS) -o $@ -c $<clean: @rm -f $(OBJ) $(TARGET)
int main(void)
{printf("this is a\r\n");#ifdef DEBUGprintf("DEBUG is defined\r\n");
#endif#ifdef RELEASEprintf("RELEASE is defined\r\n");
#endifreturn 0;
}

        通过修改CFLAGS := -DRELEASE或CFLAGS := -DDEBUG来切换a.c中程序运行的位置。

三、LDFLAGS

                LDFLAGS(Linker Flags),即链接阶段参数。作用是给链接器传递参数,控制 .o 目标文件链接为可执行文件或库的过程

        常见参数如下:

-L/path指定库文件(.so/.a)的搜索路径(非标准路径)
-l库名链接指定的库(如 -lm 链接数学库 libm
-Wl,-rpath=/path指定程序运行时的库搜索路径(解决动态库找不到的问题)
-static强制静态链接(生成不依赖动态库的独立程序)

        -L

        在 Makefile 中,LDFLAGS 是用于指定链接阶段参数的变量,而 -L 是其中一个重要的链接选项,用于指定库文件的搜索路径。全称是 "library search path",用于告诉链接器在哪些额外目录中搜索需要的库文件(包括静态库 .a 和动态库 .so)。

        默认情况下,链接器只会在系统标准库目录(如 /usr/lib/usr/local/lib)中搜索库文件。如果你的库文件放在自定义目录(如项目的 ./lib 文件夹),就需要用 -L 指定路径

        若有多个库目录,可通过多个 -L 指定

LDFLAGS := -L./lib -L./third_party/lib  # 同时搜索 ./lib 和 ./third_party/lib

        编译链接时,链接器会先在 -L 指定的目录中搜索库文件,再搜索系统标准目录,确保能找到自定义库文件,避免出现 cannot find -lxxx(找不到库)的错误。

        CFLAGS -I和LDFLAGS -L的区别

        -I./include编译阶段使用,指定头文件(.h)的搜索路径。

  -L./lib链接阶段使用,指定库文件(.a/.so)的搜索路径。

        -l

        -l(小写 L)是用于指定需要链接的库文件的选项,通常与 -L(指定库路径)配合使用,告诉链接器要将哪些库文件合并到最终的可执行程序中。

        -l 后面直接跟库名(需省略库文件的前缀 lib 和后缀 .a 或 .so):

  • 链接静态库 libmath.a → 用 -lmath
  • 链接动态库 liblog.so → 用 -llog

  链接器会根据平台默认规则优先选择动态库(.so),若找不到则尝试静态库(.a)。

        在 Makefile 中,通常将 -l 选项放在 LDLIBS 变量中(专门存放库名参数),与 LDFLAGS(存放路径参数 -L)分开管理,例如

# 库路径:告诉链接器去哪里找库(-L选项)
LDFLAGS := -L./lib  # 自定义库目录
# 库名:告诉链接器需要链接哪些库(-l选项)
LDLIBS := -lmath -llog  # 对应 libmath.a/libmath.so 和 liblog.a/liblog.so

        -WL

        在 GCC 编译链接中,-WL 是一个链接器参数传递选项,用于将其后的参数直接传递给底层的链接器(通常是 ld),而不是由 GCC 本身处理。它的核心作用是 “桥接” GCC 和链接器,让开发者能直接控制链接器的行为。

        GCC 作为编译器前端,会先处理编译逻辑(.c → .o),再调用链接器(ld)完成最终的链接。大部分链接相关参数(如 -L-l)GCC 能自动转发给链接器,但链接器特有的参数(如动态库路径配置、版本号控制)需要通过 -WL 显式传递,格式为:-WL,参数1,参数2,...(多个参数用逗号分隔,无空格)。

        -static

        在 GCC 编译中,-static 是一个链接选项,用于指定静态链接方式 —— 将程序依赖的所有库文件(包括系统库)全部打包到最终的可执行文件中,生成一个不依赖外部动态库(.so)的独立可执行程序。

四、总结

维度CFLAGSLDFLAGS
作用阶段编译(.c → .o链接(.o → 可执行文件)
处理对象源文件和头文件目标文件和库文件
核心参数-I(头文件路径)、-Wall(警告)-L(库路径)、-l(链接库)
依赖工具编译器(如 gcc 的编译阶段)链接器(如 ld 或 gcc 的链接阶段)

http://www.dtcms.com/a/391320.html

相关文章:

  • React 新闻发布系统 NewSandBox侧边栏与顶部栏布局
  • ppt视频极致压缩参数
  • 49.Mysql多实例部署
  • java 上传文件和下载/预览文件 包括音频调进度条
  • 部署你的 Next.js 应用:Vercel、Netlify 和自托管选项
  • 从产品经理视角:小智AI的产品介绍与分析
  • 解决:导包红色波浪线但正常运行及其后续问题
  • webrtc弱网-LinkCapacityEstimator类源码分析与算法原理
  • vue el-autocomplete输入框自动匹配优化,建议项按高度相似降序
  • 十分钟了解@Version注解
  • vue3+ts+uniapp H5微信小程序app有截止日期的日期date-pcicker组件
  • 设计模式-观察者模式详解
  • centos7--安装海量数据库Vastbase M100
  • Apache Commons DBCP连接池生产环境配置推荐
  • 【11/20】实时数据基础:WebSocket 在 Express 和 Vue 中的整合,实现简单聊天功能
  • 五传输层-TCP UDP慢启动-真题
  • ARM基础知识
  • 从零开始的指针(5)
  • TDMQ CKafka 版客户端实战指南系列之二:消费消息最佳实践
  • Comcast 没有对比就没有伤害
  • AI悬浮窗 1.0 | 快捷提取文字,总结信息,支持提取文字、理解屏幕上的图案、总结分析信息
  • MySQL、PostgreSQL、MongoDB和Redis全面对比
  • 隐私保护与数据安全合规(七)
  • 登录 双层拦截器+redis
  • TM56M152A (SOP16) HITENX海速芯 8位微控制器MCU 芯片深度解析
  • 理解元学习器 - 如何使用机器学习估计异质处理效应(四)
  • [数据结构] Map和Set
  • [Go类库分享]Go template模版库
  • 辅助搜题系统-基于模糊搜索,上传word题库后,可搜索答案
  • 【完整源码+数据集+部署教程】遥感农田森林岩石图像分割系统: yolov8-seg-C2f-DCNV2