Makefile变量冲突与包含关系解析
Nuttx makefile每层独立,除非显示的通过include的方式包含。
Makefile调试技巧
- 打印变量
$(info CSRCS=$(CSRCS))
- 查看变量赋值过程
make --debug=v
在 Makefile 中,变量的作用域和可见性取决于 包含关系(include
) 和 递归调用(make -C
) 的方式。以下是针对你的问题的详细分析:
1. 目录1 包含 目录2 和 目录3(通过 include
)
场景描述
- 目录1 的
Makefile
中直接include
目录2 和 目录3 的Makefile
:# 目录1/Makefile CSRCS = main.c include dir2/Makefile include dir3/Makefileall:@echo "CSRCS = $(CSRCS)"
变量行为
CSRCS
的值:- 目录1:初始为
main.c
。 - 目录2 和 目录3 的
Makefile
被直接合并到目录1 的Makefile
中,CSRCS
会按include
顺序叠加或覆盖:- 若目录2 的
Makefile
中有CSRCS += file2.c
,则CSRCS
变为main.c file2.c
。 - 若目录3 的
Makefile
中有CSRCS = file3.c
(使用=
覆盖),则最终CSRCS
为file3.c
。
- 若目录2 的
- 目录2 和 目录3 的
Makefile
中CSRCS
的值取决于它们的定义方式(是否使用+=
或=
)。
- 目录1:初始为
关键点
- 作用域合并:所有
CSRCS
共享同一作用域,后include
的文件可以修改之前的值。 - 覆盖规则:
=
或:=
会直接覆盖。+=
会追加。?=
仅在变量未定义时赋值。
2. 目录1 未包含 目录2 和 目录3(独立调用或递归调用)
场景描述
- 目录1 的
Makefile
通过make -C
递归调用子目录:# 目录1/Makefile CSRCS = main.call:$(MAKE) -C dir2$(MAKE) -C dir3@echo "CSRCS = $(CSRCS)" # 输出目录1的 CSRCS
变量行为
CSRCS
的值:- 目录1:始终为
main.c
(子目录的修改不影响父目录)。 - 目录2 和 目录3:各自独立,互不可见。
- 若目录2 的
Makefile
定义CSRCS = file2.c
,仅在该目录生效。 - 目录3 同理。
- 若目录2 的
- 目录1:始终为
关键点
- 作用域隔离:递归调用时,子进程的变量是独立的,除非显式传递:
- 通过
export
传递变量:export CSRCS # 目录1的 CSRCS 传递给子目录
- 通过命令行传递:
$(MAKE) -C dir2 CSRCS="$(CSRCS)"
- 通过
3. 目录2 和 目录3 之间的包含关系
场景 1:目录2 包含 目录3
# 目录2/Makefile
CSRCS = file2.c
include ../dir3/Makefileall:@echo "CSRCS = $(CSRCS)"
- 行为:
- 若目录3 的
Makefile
中有CSRCS += file3.c
,则CSRCS
变为file2.c file3.c
。 - 若目录3 使用
CSRCS = file3.c
,则CSRCS
被覆盖为file3.c
。
- 若目录3 的
场景 2:目录2 和 目录3 互不包含
- 行为:
- 两个目录的
CSRCS
完全独立,互不影响。
- 两个目录的
总结表格
场景 | 目录1 的 CSRCS | 目录2 的 CSRCS | 目录3 的 CSRCS |
---|---|---|---|
目录1 include 目录2、目录3 | 受目录2、3 修改影响 | 可能被目录1 或目录3 覆盖/追加 | 可能被目录1 或目录2 覆盖/追加 |
目录1 递归调用目录2、目录3 | 固定为 main.c | 独立(默认不共享) | 独立(默认不共享) |
目录2 include 目录3 | 不涉及 | 受目录3 影响 | 独立或影响目录2 |
目录2 和 目录3 互不包含 | 不涉及 | 独立 | 独立 |
最佳实践建议
-
避免变量污染:
- 使用唯一变量名(如
DIR2_CSRCS
和DIR3_CSRCS
)。 - 在
include
时谨慎使用+=
或?=
。
- 使用唯一变量名(如
-
显式控制作用域:
- 递归调用时通过
export
或命令行传递变量。 - 使用
override
防止变量被覆盖:override CSRCS = protected.c
- 递归调用时通过
通过理解这些规则,可以灵活管理多级 Makefile 中的变量冲突问题。
在 Makefile 中,?=
、:=
和 =
是三种不同的变量赋值方式,它们的行为有显著区别,主要体现在 赋值时机、展开时机 和 覆盖规则 上。以下是详细对比:
1. =
(延迟展开,递归赋值)
特点
- 延迟展开:变量的值在 使用时 才展开(例如被引用时)。
- 递归扩展:若变量的值中包含其他变量,这些变量会在最终展开时递归解析。
示例
FOO = Hello
BAR = $(FOO) World # BAR 的值是 "Hello World"(此时未展开)
FOO = Hiall:@echo $(BAR) # 输出 "Hi World"(使用时展开,FOO 取最新值)
- 输出:
Hi World
(因为$(FOO)
在echo
时才展开,取最新的FOO
值)。
适用场景
- 需要动态获取变量最新值的场景(如依赖其他变量的后续修改)。
2. :=
(立即展开,简单赋值)
特点
- 立即展开:变量的值在 定义时 就展开(后续变量变化不影响它)。
- 一次性扩展:值中的变量在赋值时被固定。
示例
FOO = Hello
BAR := $(FOO) World # BAR 的值立即展开为 "Hello World"
FOO = Hiall:@echo $(BAR) # 输出 "Hello World"(BAR 的值已固定)
- 输出:
Hello World
($(FOO)
在赋值时已展开为Hello
)。
适用场景
- 需要固定变量值的场景(避免后续变量修改的影响)。
- 提高性能(避免重复展开)。
3. ?=
(条件赋值)
特点
- 仅在变量未定义时赋值:如果变量已定义(包括空值),则忽略当前赋值。
- 通常用于提供默认值。
示例
FOO ?= Default # 若 FOO 未定义,则赋值为 "Default"
BAR = Existing
BAR ?= Override # BAR 已定义,此赋值无效all:@echo "FOO=$(FOO), BAR=$(BAR)"
- 输出:
FOO=Default, BAR=Existing
。
适用场景
- 允许用户通过环境变量或命令行覆盖默认值:
make FOO=Custom # 命令行覆盖 FOO
三者的对比表格
赋值方式 | 展开时机 | 是否递归扩展 | 覆盖规则 | 典型用途 |
---|---|---|---|---|
= | 使用时展开 | 是 | 总是覆盖 | 动态依赖其他变量的值 |
:= | 定义时展开 | 否 | 总是覆盖 | 固定值,避免后续变量变化影响 |
?= | 同 = 或 := | 取决于后续使用 | 仅在变量未定义时赋值 | 提供默认值 |
关键区别示例
案例 1:展开时机
VAR1 = $(NEW) # 延迟展开
VAR2 := $(NEW) # 立即展开
NEW = valueall:@echo "VAR1=$(VAR1), VAR2=$(VAR2)"
- 输出:
VAR1=value, VAR2=
(VAR2
在定义时NEW
还未定义,因此为空)。
案例 2:条件赋值
VAR ?= defaultall:@echo "VAR=$(VAR)"
- 若运行
make
:输出VAR=default
。 - 若运行
make VAR=custom
:输出VAR=custom
。
最佳实践建议
-
优先使用
:=
- 除非需要动态依赖其他变量,否则用
:=
避免意外展开问题(性能更好,行为更可预测)。
- 除非需要动态依赖其他变量,否则用
-
谨慎使用
=
- 递归扩展可能导致难以调试的问题(如循环依赖或意外值变化)。
-
?=
用于默认值- 方便用户通过命令行或环境变量覆盖默认值。
-
混合使用示例
CC := gcc # 固定编译器 CFLAGS ?= -O2 # 允许用户覆盖优化级别 DEPENDS = $(wildcard *.c) # 动态获取依赖文件
高级技巧
override
关键字:强制覆盖已定义的变量(即使用户通过命令行传递):override FOO = MustUseThisValue
+=
追加操作:行为取决于变量原始定义方式:- 若原始是
:=
,则立即展开追加。 - 若原始是
=
,则延迟展开追加。
- 若原始是
通过理解这些赋值的差异,可以更精准地控制 Makefile 的行为!
相关文章:
Makefile变量冲突与包含关系解析
Nuttx makefile每层独立,除非显示的通过include的方式包含。 Makefile调试技巧 打印变量 $(info CSRCS$(CSRCS))查看变量赋值过程 make --debugv在 Makefile 中,变量的作用域和可见性取决于 包含关系(include) 和 递归调用&…...
2025/517学习
对离群值怎么操作。这个就是拟合操作的。用更弯曲的曲线去拟合,如常见函数log 多元回归和单元回归 如题,如果我有多个自变量,来对一个因变量进行OLS回归,有没有operator可以做到?(ts_regression似乎只支持一个…...
浅谈前端架构设计与工程化
引言 在当今快速发展的Web开发领域,前端已经从简单的页面展示演变为复杂的应用程序开发。随着项目规模的扩大和团队协作的需求增加,良好的前端架构设计和工程化实践变得至关重要。本文将探讨如何构建可维护、可扩展的前端架构,并介绍现代前端…...
JMeter 教程:编写 POST 请求脚本访问百度
目录 ✅ 教程目的 🛠️ 环境要求 📄 实操步骤 第一步:启动 JMeter 第二步:添加测试计划和线程组 1.右键左侧 Test Plan(测试计划) 2.选择 Add → Threads (Users) → Thread Group(线程组…...
Typescript学习教程,从入门到精通,TypeScript 函数语法知识点及案例代码(5)
TypeScript 函数语法知识点及案例代码 TypeScript 提供了丰富的函数语法特性,使得函数定义更加灵活和强大。以下将详细介绍 TypeScript 中函数的相关语法,包括函数定义、可选参数、默认参数、剩余参数、重载函数、递归函数、匿名函数、箭头函数以及回调…...
【51单片机定时器/计数器】
目录 简介 定时器配置流程 1.配置定时器工作方式寄存器TMOD 2.配置中断寄存器TCON 3.定时时间计算公式 4.配置中断允许寄存器IE 5.使用中断函数完成中断 简介 定时器/计数器本质都是对脉冲信号进行计数,区别在于作为定时器时的脉冲信号来自于晶振12分频&…...
Oracle 的 ASSM 表空间
Oracle 的 ASSM(Automatic Segment Space Management)表空间 是一种自动管理段空间的技术,通过位图(Bitmap)机制跟踪数据块的使用情况,替代传统的手动管理(MSSM,即 Freelist 管理&am…...
C++学习:六个月从基础到就业——C++11/14:auto类型推导
C学习:六个月从基础到就业——C11/14:auto类型推导 本文是我C学习之旅系列的第四十一篇技术文章,也是第三阶段"现代C特性"的第三篇,主要介绍C11/14中的auto类型推导机制。查看完整系列目录了解更多内容。 引言 在现代C…...
select语句的书写顺序
一.MySQL SELECT语句的执行顺序 MySQL中SELECT语句的执行顺序与SQL语句的书写顺序不同,理解这个执行顺序对于编写高效查询非常重要。 1.标准SELECT语句的执行顺序 FROM子句(包括JOIN操作) 首先确定数据来源表执行表连接操作 WHERE子句 对F…...
OpenWebUI新突破,MCPO框架解锁MCP工具新玩法
大家好,Open WebUI 迎来重要更新,现已正式支持 MCP 工具服务器,但 MCP 工具服务器需由兼容 OpenAPI 的代理作为前端。mcpo 是一款实用代理,经测试,它能让开发者使用 MCP 服务器命令和标准 OpenAPI 服务器工具ÿ…...
【Day28】
总结: Python 通过缩进来定义代码块的结构。当解释器遇到像 def, class, if, for 这样的语句,并且后面跟着冒号 : 时,它就期望接下来会有一个或多个缩进的语句来构成这个代码块。如果它没有找到任何缩进的语句(即代码块是空的&am…...
STM32 | FreeRTOS 消息队列
01 一、概述 队列又称消息队列,是一种常用于任务间通信的数据结构,队列可以在任务与任务间、中断和任务间传递信息,实现了任务接收来自其他任务或中断的不固定长度的消息,任务能够从队列里面读取消息,当队列中的消…...
Vue-事件修饰符
事件修饰符 prevent (阻止默认事件) 超链接 点击事件 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>事件修饰符</title><!-- 引入Vue --><script …...
c++函数调用运算符及类型转换运算符重载
author: hjjdebug date: 2025年 05月 17日 星期六 14:44:48 CST descrip: c函数调用运算符及类型转换运算符重载 文章目录 0. 前言. 运算符包括以下运算符.1. 运算符重载语句一般格式:2. 函数调用运算符:3. 类型转换运算符: 例如 int(); double(); bool(…...
如何在 Windows 10 或 11 中安装 PowerShellGet 模块?
PowerShell 是微软在其 Windows 操作系统上提供的强大脚本语言,可用于通过命令行界面自动化各种任务,适用于 Windows 桌面或服务器环境。而 PowerShellGet 是 PowerShell 中的一个模块,提供了用于从各种来源发现、安装、更新和发布模块的 cmdlet。 本文将介绍如何在 PowerS…...
84.评论日记
原链接 这个视频我发了四五条评论。评论内容甚至和下面这个视频内的其他评论一样。 找了另外的账号也发了。 发现,无论是我这个账号,还是其他的账号,评论都无法看到。 我大胆猜测有一种机制,某些官号会被设置成一种高检测的等…...
一周学会Pandas2 Python数据处理与分析-Pandas2数据添加修改删除操作
锋哥原创的Pandas2 Python数据处理与分析 视频教程: 2025版 Pandas2 Python数据处理与分析 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 对数据的修改、增加和删除在数据整理过程中时常发生。修改的情况一般是修改错误,还有一种情况是格式转换…...
荷兰国旗问题 之 指针划分区间问题
文章目录 首先介绍一下什么是荷兰国旗问题?问题描述为:给定一个由红色、白色和蓝色三种颜色组成的无序数组,将数组元素按颜色排序,使得所有红色元素在前,白色元素居中,蓝色元素在后。这里的 “颜色” 通常用…...
冒泡排序-java
public class BubbleSort{ public static void bubbleSort(int[] arr) { int n arr.length; boolean swapped; // 外层循环控制遍历的轮数 for (int i 0; i < n - 1; i) { swapped false; for (int j 0; …...
进阶-数据结构部分:2、常用排序算法
飞书文档https://x509p6c8to.feishu.cn/wiki/FfpIwIPtviMMb4kAn3Sc40ABnUh 常用排序算法 这几种算法都是常见的排序算法,它们的优劣和适用场景如下: 冒泡排序(Bubble Sort):简单易懂,时间复杂度较高&…...
人工智能-自然语言与语音产品实现
一、语义相似度 (一)、文本向量化 1、文本向量化(Text Vectorization) 是自然语言处理(NLP)中的核心预处理步骤,旨在将人类语言的文本转换为计算机可处理的数值向量(数学表达&…...
阿里巴巴开源移动端多模态LLM工具——MNN
MNN 是一个高效且轻量级的深度学习框架。它支持深度学习模型的推理和训练,并在设备端的推理和训练方面具有行业领先的性能。目前,MNN 已集成到阿里巴巴集团的 30 多个应用中,如淘宝、天猫、优酷、钉钉、闲鱼等,覆盖了直播、短视频…...
SpringBootAdmin:全方位监控与管理SpringBoot应用
监控的意义 1. 监控服务状态是否宕机 2. 监控服务运行指标 (内存,虚拟机,线程,请求等) 3. 监控日志 4. 管理服务 (服务下线) 可视化监控平台 Spring Boot Admin, 开源社区项目, 用于管理和监控SpringBoot应用程序. 客户端注册到服务端, 通过HTTP请求方式, 服务端定期从客…...
SAP HCM 0008数据存储逻辑
0008信息类型:0008信息类型是存储员工基本薪酬的地方,因为很多企业都会都薪酬带宽,都会按岗定薪,所以在上线前为体现工资体系的标准化,都会在配置对应的薪酬关系,HCM叫间接评估,今天我们就分析下…...
【springcloud学习(dalston.sr1)】Config配置中心-ConfigServer端与Git通信(含源代码)(十三)
该系列项目整体介绍及源代码请参照前面写的一篇文章【springcloud学习(dalston.sr1)】项目整体介绍(含源代码)(一) springcloud学习(dalston.sr1)系统文章汇总如下: 【springcloud学习(dalston…...
2020CCPC河南省赛题解
A. 班委竞选 签到题,模拟。 #include <bits/stdc.h> #define x first #define y second #define int long long //#define double long doubleusing namespace std; typedef unsigned long long ULL ; typedef pair<int,int> PII ; typedef pair<d…...
C语言输入函数对比解析
目录 C语言输入函数全家福(和它们的秘密)fgetsgetsscanfgetcharfscanf函数对比表灵魂总结 哈哈,看来你正在和C语言的输入函数们玩“大家来找茬”!放心,我会用最接地气的方式给你讲明白,保证比看《甄嬛传》还…...
python四则运算计算器
python四则运算计算器 是谁说,python不好写计算器的,我亲自写个无ui的计算器功能,证明这是谣言 step1:C:\Users\wangrusheng\Downloads\num.txt 15 - 4 * 3 10 / 2(5 3) * 2 6 / 31/2 * 8 3/4 * 4 - 0.52.5 * (4 1.6) - 9 / 3-6 12 * (…...
BUUCTF——Nmap
BUUCTF——Nmap 进入靶场 类似于一个nmap的网站 尝试一下功能 没什么用 看看数据包 既然跟IP相关 伪造一个XXF看看 拼接了一下没什么用 果然没这么简单 尝试一下命令注入 构造payload 127.0.0.1 | ls 应该有过滤 加了个\ 直接构造个php木马上传试试 127.0.0.1 | <?…...
【Changer解码头详解及融入neck层数据的实验设计】
Changer解码头详解 ChangerEx中的 Changer 解码头(定义在 [changer.py](file://opencd\models\decode_heads\changer.py))是基于双时相输入的,用于遥感变化检测任务。下面我将详细解释: 🎯 一、解码头输入数据来源 输…...
深度学习推理引擎---OpenVINO
OpenVINO(Open Visual Inference & Neural Network Optimization Toolkit)是英特尔开发的开源工具套件,旨在优化和加速深度学习模型在英特尔硬件(CPU、GPU、VPU、FPGA等)上的推理性能,同时支持从训练到…...
JavaScript splice() 方法
1. JavaScript splice() 方法 1.1. 定义和用法 splice() 方法用于添加或删除数组中的元素。 注意:这种方法会改变原始数组。 返回值:如果删除一个元素,则返回一个元素的数组。 如果未删除任何元素,则返回空数组。 1.2. …...
数据库故障排查指南:解决常见问题,保障数据安全与稳定
数据库故障排查指南:解决常见问题,保障数据安全与稳定 📖 前言 数据库作为现代应用的核心组件,其稳定性直接影响业务连续性。本文总结六大常见数据库故障场景,提供快速排查思路与解决方案,助你化身"…...
gem5-gpu教程 第十章 关于topology 的Mesh network
问题一、L1和L2缓存之间的VI_hammer_fusion中指定了互连延迟,如何更改这些数字吗? 我已经实现了一个网格拓扑来连接cpu内核和GPU SM,并对VI_hammer*和网格文件进行了所有必要的更改。我的问题是: 1. There is interconnect latency specified in VI_hammer_fusion betwee…...
【C/C++】C++返回值优化:RVO与NRVO全解析
文章目录 C返回值优化:RVO与NRVO全解析1 简介2 RVO vs NRVO3 触发条件4 底层机制5 应用场景6 验证与限制7 性能影响8 补充说明9 总结 C返回值优化:RVO与NRVO全解析 返回值优化(Return Value Optimization, RVO)是编译器通过消除临…...
使用 Kaniko来构建镜像
使用 Kaniko来构建镜像 Kaniko 是一种专注于容器镜像构建的开源工具,其核心设计理念与 Docker 存在显著差异。以下从功能定位、技术实现和适用场景三方面进行对比分析: 一、Kaniko 的核心特性 无需 Docker 守护进程 Kaniko 直接在容器或 Kubernetes 集…...
2025.05.17淘天机考笔试真题第三题
📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围OJ 03. 奇偶平衡树分割问题 问题描述 K小姐是一位园林设计师,她设计了一个由多个花坛组成的树形公园。每个花坛中种植了不同数量的花…...
history模式:让URL更美观
🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 🍚 蓝桥云课签约作者、…...
算法:分治法
实验内容 在一个2kⅹ2k个方格组成的棋盘中,若恰有一个方格与其他方格不同,则称该方格为特殊方格,且称该棋盘为一特殊棋盘。 显然,特殊方格出现的位置有4k 种情况,即k>0,有4k 种不同的特殊棋盘 棋盘覆盖:…...
豆粕ETF投资逻辑整理归纳-20250511
目录 一、什么是豆粕 基本概念 豆粕上游生产国 豆粕下游消耗方 二、豆粕ETF 概念 策略 展期操作 超额收益 行情波动 豆粕资产的低相关性 三、展期收益 Contango升水结构和Backwardation贴水结构 豆粕的贴水逻辑 还有哪些品种拥有长期的展期收益 四、其他相关信…...
使用 Python 连接 Oracle 23ai 数据库完整指南
方法一:使用 oracledb 官方驱动(推荐) Oracle 官方维护的 oracledb 驱动(原 cx_Oracle)是最新推荐方案,支持 Thin/Thick 两种模式。 1. 环境准备 pip install oracledb2. 完整示例代码 import oracledb import getpass from typing import Unionclass Oracle23aiConn…...
构建集成差异化灵巧手和先进机器人控制技术的自动化系统
介绍程序 1.流程分析 通过流程分析审查应用机器人自动化的可行性。 2.系统设计 选择合适的机器人(机械臂、夹持器、视觉系统等),并通过详细的任务分析设计最佳系统。 3.内部测试 建立内部测试平台并解决任何问题。 4.现场测试 现场设…...
题单:汉诺塔问题
题目描述 如下图所示,设有 nn 个大小不等的中空圆盘,按照从小到大的顺序叠套在立柱 A 上,另有两根立柱 B 和 C 。 现在要求把全部圆盘从 A 柱(称为源柱)移到 C 柱(称为目标柱),移动…...
Unable to get end effector tips from jmg
这个错误信息表明在使用MoveIt2时,moveit_visual_tools无法从关节模型组(Joint Model Group,简称JMG)中获取末端执行器(End Effector,简称EE)的尖端信息。这通常是因为配置文件中缺少相关信息&a…...
flutter flutter run 运行项目卡在Running Gradle task ‘assembleDebug‘...
flutter run --verbose在运行flutter run 可以看到是卡在哪一步 最重要的就是自己查看日志,具体哪一步有问题flutter run --verbose使用这个,运行了项目会将错误信息放在控制台 可能原因 静态资源问题如果:图片、字体文件等没有在pubspec.yam…...
STM32烧录程序正常,但是运行异常
一、硬件配置问题 BOOT引脚设置错误 STM32的启动模式由BOOT0和BOOT1引脚决定。若设置为从RAM启动(BOOT01,BOOT10),程序在掉电后无法保存,导致复位后无法正常运行。应确保BOOT00(从Flash启动)15。…...
TTS:F5-TTS 带有 ConvNeXt V2 的扩散变换器
1,项目简介 F5-TTS 于英文生成领域表现卓越,发音标准程度在本次评测软件中独占鳌头。再者,官方预设的多角色生成模式独具匠心,能够配置多个角色,一次性为多角色、多情绪生成对话式语音,别出心裁。 最低配置…...
ecmascript 第6版特性 ECMA-262 ES6
https://blog.csdn.net/zlpzlpzyd/article/details/146125018 在之前写的文章基础上,ES6在export和import的基础外,还有如下特性 特性说明let/const块级作用域变量声明>箭头函数Promise异步编程...
2024 山东省ccpc省赛
目录 I(签到) 题目简述: 思路: 代码: A(二分答案) 题目简述: 思路: 代码: K(构造) 题目: 思路: 代…...
角点特征:从传统算法到深度学习算法演进
1 概述 图像特征是用来描述和分析图像内容的关键属性,通常包括颜色、纹理和形状等信息。颜色特征能够反映图像中不同颜色的分布,常通过 RGB 值或色彩直方图表示。纹理特征则关注图像表面的结构和细节,例如通过灰度共生矩阵或局部二值模式&…...