CMake基础之-控制流

上一节我们介绍了变量, 这一节来介绍控制流. CMake 支持如下的控制流命令: if, foreachwhile. 这三条命令使用起来跟其他语言类似, 但是也有一些特定的功能.

if()命令

if()命令的现代形式如下(可提供多个elseif()子句):

if(condition1)#commands...
elseif(condition2)#commands...
else()#commands...
endif()

早期版本的 CMake 要求在else()endif()子句中重复表达式作为参数, 但自 CMake 2.8.0 起不再有此要求.

CMake 中的条件语句有如下几种类型:

  1. 传统的布尔逻辑
  2. 文件系统测试命令
  3. 版本比较命令
  4. 其他一些测试命令

基本表达式

主要包含三种类型:

  1. 常量值 if(constant)
    • 如果constant是值为ON, YES, TRUE, 1, 不论是否带引号, 也不论大小写, 统一视为真(true).
    • 如果constant是值为OFF, NO, FALSE, N, IGNORE, NOTFOUND, 空字符串, 或以-NOTFOUND结尾, 不论是否带引号, 也不区分大小写, 统一视为假(false).
    • 如果constant是一个(可能是浮点型的)数字, 它将按照常规 C 语言规则转换为布尔值, 不过在这种情况下, 除了 01 之外的值很少使用.
  2. 变量名 if(variable): 当使用未带引号的变量名时, 会将变量的值与假常量进行比较. 如果没有匹配的值, 则表达式的结果为真. 未定义的变量将求值为空字符串, 这与其中一个假常量匹配, 因此结果为假.

    [!WARNING]
    请注意, 在本讨论中, 环境变量不被视为变量. 像if(ENV{some_var})这样的语句将始终求值为假, 无论名为some_var的环境变量是否存在.

  3. 字符串 if("string"): 注意是带引号的字符串.
    带引号的字符串会被视为假(false), 除非:
    • 在 CMake 3.1 及更高版本中, 这个字符串是一个未带引号的常量.
    • 在 CMake 3.1 之前, 如果字符串的值与现有变量的名称匹配, 则该带引号的字符串实际上会被该变量名(不带引号)替换, 然后重新进行测试. 这个跟程序员的直觉相反.
# 带引号和未带引号常量的示例
if("True") # 求值为真
if(TRUE) # 求值为真
if(yes) # 求值为真
if(0) # 求值为假# 这些也被视为未带引号的常量, 因为在if()看到这些值之前, 变量就已求值
set(A YES)
if(${A}) # 求值为真
set(B 0)
if(${B}) # 求值为假# 不匹配任何真假常量
if(anUnknownVariable) # 求值为假# 带引号的值不匹配任何真假常量, 因此同样作为变量名或字符串进行测试
if("anUnknownVariable") # 求值为假
if("A") # 求值为真

逻辑运算符

CMake 支持常见的AND, ORNOT逻辑运算符, 以及用于控制优先级的括号.

  • if(NOT expression)
  • if(NOT expression1 AND expression2)
  • if(expression1 OR expression2)
  • if((condition) AND (condition OR (condition))):

按照惯例, 括号内的表达式首先求值, 从最内层的括号开始.

比较测试

CMake 将比较测试分为不同类别:

数值字符串版本号路径
LESSSTRLESSVERSION_LESS-
GREATERSTRGREATERVERSION_GREATER-
EQUALSTREQUALVERSION_EQUALPATH_EQUAL
LESS_EQUALSTRLESS_EQUALVERSION_LESS_EQUAL-
GREATER_EQUALSTRGREATER_EQUALVERSION_GREATER_EQUAL-

数值比较按预期比较左右两边的值. 但请注意, 如果任何一个操作数不是数字, CMake 通常不会报错, 并且当值中包含非数字字符时, 其行为并不完全符合官方文档. 根据数字和非数字字符的组合情况, 表达式的结果可能为真或假.

# 有效的数值表达式, 均求值为真
if(2 GREATER 1)
if("23" EQUAL 23)
set(val 42)
if(${val} EQUAL 42)
if("${val}" EQUAL 42)# 无效表达式, 在某些CMake版本中求值为真. 请勿依赖此行为.
if("23a" EQUAL 23)

版本号比较有点像增强版的数值比较. 版本号假定为major[.minor[.patch[.tweak]]]的形式, 其中每个部分都应为非负整数. 比较两个版本号时, 首先比较主版本号部分. 只有当主版本号部分相等时, 才会比较次版本号部分(如果存在), 依此类推. 缺失的部分视为零.

#在以下所有示例中, 表达式求值为真:
if(1.2 VERSION_EQUAL 1.2.0)
if(1.2 VERSION_LESS 1.2.3)
if(1.2.3 VERSION_GREATER 1.2)
if(2.0.1 VERSION_GREATER 1.9.7)
if(1.8.2 VERSION_LESS 2)

版本号比较与数值比较一样, 存在稳健性方面的注意事项. 每个版本号部分都应是整数, 但如果不满足此限制, 比较结果基本上是未定义的.

对于字符串, 值按字典序进行比较. 但是, 字符串比较函数的参数可以是变量名或字符串, 这又可能会造成混淆. 以下示例展示了一个常见的陷阱:

# 未定义名为"a"的变量, 因此它被视为值为"a"的字符串
if(a STREQUAL "there")message("We do not get here")
elseif(a STREQUAL "")message("We do not get here either")
endif()
set(a there)
# 现在有一个名为"a"的变量, 因此使用其值.
if(a STREQUAL "there")message("We DO get here")
endif()

可以遵循以下准则来避免意外行为:

  • 始终确保将策略CMP0054设置为NEW. 这可防止带引号的值被视为除字符串以外的其他内容.
  • 仅在确保存在同名变量时, 才使用未带引号的参数.

PATH_EQUAL运算符很像STREQUAL的特殊情况. 操作数假定为 CMake 原生路径形式(目录分隔符为正斜杠). PATH_EQUAL的一个关键区别是它使用逐组件比较. 多个连续的目录分隔符会被合并为单个分隔符, 这是与STREQUAL的主要实际区别. 以下来自官方 CMake 文档的示例展示了这种差异:

if("/a//b/c" PATH_EQUAL "/a/b/c") # true# We DO get here...
endif()
if("/a//b/c" STREQUAL "/a/b/c") # false# We do NOT get here....
endif()

除了上述运算符形式外, 还可以使用正则表达式测试字符串:

if(value MATCHES regex)

value同样遵循上述变量或字符串规则, 并与regex正则表达式进行比较. 如果匹配, 则表达式求值为真. 虽然 CMake 文档未定义if()命令支持的正则表达式语法, 但在其他命令的文档(如string()命令文档)中进行了定义. 本质上, CMake 仅支持基本的正则表达式语法.

括号可用于捕获匹配值的部分内容. 该命令将设置名为CMAKE_MATCH_<n>的变量, 其中<n>是要匹配的组号. 整个匹配的字符串存储在组 0 中.

if("Hi from ${who}" MATCHES "Hi from (Fred|Barney).*")message("${CMAKE_MATCH_1} says hello")
endif()

文件系统测试

CMake 还包含一组可用于查询文件系统的测试:

if(EXISTS pathToFileOrDir)
if(IS_READABLE pathToFileOrDir) # CMake 3.29或更高版本
if(IS_WRITABLE pathToFileOrDir) # CMake 3.29或更高版本
if(IS_EXECUTABLE pathToFileOrDir) # CMake 3.29或更高版本
if(IS_DIRECTORY pathToDir)
if(IS_SYMLINK fileName)
if(IS_ABSOLUTE path)
if(file1 IS_NEWER_THAN file2)

与大多数其他if()表达式不同, 上述任何运算符在没有$()的情况下都不会执行任何变量/字符串替换, 无论是否带引号.

IS_DIRECTORY, IS_SYMLINKIS_ABSOLUTE运算符应该很容易理解. 也许不太直观的是, EXISTS运算符检查的不仅仅是其参数是否存在. 它还要求指定的文件或目录是可读的, 不过这种行为在未来版本的 CMake 中可能会改变. 在大多数情况下, 较新的IS_READABLE, IS_WRITABLEIS_EXECUTABLE运算符能更好地表达应检查的实际条件. 因此, 只有在项目需要支持 CMake 3.28 或更早版本, 或者指定的文件或目录是否可读, 可写或可执行并不重要时, 才使用EXISTS.

不幸的是, IS_NEWER_THAN这个名称并不准确描述该运算符的实际功能. 当两个文件具有相同的时间戳时, 它也会返回真, 而不仅仅是当file1的时间戳比file2新时. 在时间戳分辨率仅为一秒的文件系统(如 macOS 10.12 及更早版本上的 HFS+文件系统)上, 这一点尤为重要. 在这样的系统中, 即使文件是由不同命令创建的, 具有相同时间戳的情况也很常见. 另一个不太直观的行为是, 当其中任何一个文件缺失时, 它也会返回真. 此外, 如果任何一个文件未指定为绝对路径, 其行为是未定义的. 因此, 为了获得所需的条件, 通常需要以否定的方式使用IS_NEWER_THAN.

考虑这样一个场景: secondFile是由firstfile生成的. 如果firstfile被更新或secondfile缺失, 则需要重新创建secondFile. 如果firstfile不存在, 则应视为致命错误. 这样的逻辑需要这样表达:

set(firstFile "/full/path/to/somewhere")
set(secondFile "/full/path/to/another/file")
if(NOT EXISTS ${firstFile})message(FATAL_ERROR "${firstFile} is missing")
elseif(NOT EXISTS ${secondFile} OR NOT ${secondFile} IS_NEWER_THAN ${firstfile})#...commands to recreate secondFile
endif()

有人可能会天真地认为可以这样表达条件:

# WARNING:Very Likely to be wrong
if(${firstFile} IS_NEWER_THAN ${secondFile})#...commands to recreate secondFile
endif()

虽然这种表述可能看似表达了所需的条件, 但实际上并非如此, 因为当两个文件具有相同时间戳时, 它也会返回真. 如果重新创建secondFile的操作很快, 并且文件系统的时间戳分辨率仅为秒, 那么每次运行 CMake 时, secondFile很可能都会被重新创建. 如果构建步骤依赖于secondFile, 那么每次运行 CMake 后, 构建也会重新构建相关内容.

7.1.5 存在测试

if()表达式的最后一类支持测试各种 CMake 实体是否存在.

if(DEFINED name)
if(COMMAND name)
if(POLICY name)
if(TARGET name)
if(TEST name) # 自CMake 3.4起可用

上述所有测试在if()命令执行时, 若指定名称的实体存在, 则返回真.

if(value IN_LIST ListVar) # 自CMake 3.3起可用
  • DEFINED: 如果指定名称的变量存在, 则返回真. 变量的值无关紧要, 仅测试其是否存在. 该变量可以是常规的 CMake 变量, 也可以是缓存变量. 从 CMake 3.14 起, 可以使用CACHE{name}形式仅检查缓存变量是否存在. 所有 CMake 版本也支持使用ENV{name}形式测试环境变量是否存在, 尽管直到 CMake 3.13 才正式记录此支持.
if(DEFINED CACHE{SOMEVAR}) # 检查CMake缓存变量
if(DEFINED SOMEVAR) # 检查CMake变量(常规或缓存)
if(DEFINED ENV{SOMEVAR}) # 检查环境变量
  • COMMAND: 测试指定名称的 CMake 命令, 函数或宏是否存在. 这在尝试使用某个内容之前检查其是否已定义时很有用. 对于 CMake 提供的命令, 建议优先测试 CMake 版本, 但对于项目提供的函数和宏(详见第 9 章"函数和宏"), 这是一种合适的检查方式. 如果某个命令作为第三方包中的技术预览版提供, 这种检查尤其有用. 在这种情况下, 基于版本的逻辑不太合适, 因为该命令可能在未来版本中消失.
# "specialNewThing"作为技术预览版提供, 但它可能尚未得到正式支持.
if(COMMAND specialNewThing)specialNewThing(...)
else()# 回退到其他方式...
endif()
  • POLICY: 测试 CMake 是否知晓某个特定策略. 策略名称通常为CMPxxx的形式, 其中xxxx是一个四位数. 详见第 13 章"策略"了解此主题的详细信息. 此测试常用于仅在支持特定策略

7.1.5 存在性测试(续)

  • TARGET: 用于检查是否存在指定名称的目标. 目标可以是可执行文件, 静态库, 共享库等. 在尝试对目标进行操作之前, 先检查目标是否存在是一种良好的实践. 例如, 在添加依赖项或属性之前:
if(TARGET MyExecutable)target_link_libraries(AnotherTarget PRIVATE MyExecutable)
endif()
  • TEST: (从 CMake 3.4 版本开始可用)检查是否存在指定名称的测试用例. 这在需要动态控制测试执行或者在测试脚本中引用其他测试时非常有用. 例如:
if(TEST MySpecialTest)set_tests_properties(MySpecialTest PROPERTIES TIMEOUT 30)
endif()
  • value IN_LIST ListVar: (从 CMake 3.3 版本开始可用)检查指定的值是否存在于给定的列表变量中. 这是一种简洁的方式来检查某个元素是否属于一个列表. 例如:
set(MyList "apple" "banana" "cherry")
if("banana" IN_LIST MyList)message("Found banana in the list!")
endif()

7.1.6 常见示例和错误

在使用 if() 命令时, 有一些常见的模式和错误需要注意. 以下是一些示例, 展示了正确和错误的使用方式.

错误示例

一个常见的错误是尝试根据平台相关的变量来做出决策, 但没有正确考虑到这些变量的实际含义. 例如, 在某些情况下, 开发人员可能会错误地使用 CMAKE_SYSTEM_NAME 变量来判断是否为 Windows 系统:

# 错误示例: 这种写法可能会导致在非Windows系统上出现意外行为
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")# 执行一些Windows特定的操作
endif()

这种写法的问题在于, CMAKE_SYSTEM_NAME 变量的值在不同的 CMake 生成器和平台上可能会有所不同. 在某些情况下, 即使在 Windows 系统上, 它的值也可能不是 "Windows". 更好的做法是使用 WIN32MSVC 变量来判断是否为 Windows 系统:

# 正确示例: 使用WIN32变量来判断是否为Windows系统
if(WIN32)# 执行一些Windows特定的操作
endif()
正确示例

另一个常见的场景是根据 CMake 选项有条件地包含目标. 假设项目中有一个选项 ENABLE_FEATURE_X, 用于决定是否启用某个特定的功能. 可以这样使用 if() 命令:

option(ENABLE_FEATURE_X "Enable feature X" OFF)if(ENABLE_FEATURE_X)add_executable(FeatureXExecutable feature_x_source.cpp)target_link_libraries(FeatureXExecutable PRIVATE SomeLibrary)
endif()

在这个示例中, 只有当 ENABLE_FEATURE_X 选项被设置为 ON 时, 才会创建 FeatureXExecutable 目标并链接到 SomeLibrary.

7.2 循环

CMake 提供了两种主要的循环结构: foreach()while().

7.2.1 foreach() 循环

foreach() 循环用于遍历一组项目或值. 它有几种不同的形式, 下面将分别介绍.

基本形式

最基本的形式是直接列出要遍历的值:

foreach(LoopVar arg1 arg2 arg3 ...)# 循环体, 使用 ${LoopVar} 访问当前值message("Current value: ${LoopVar}")
endforeach()

在这个示例中, LoopVar 是循环变量, 它会依次取 arg1, arg2, arg3 等的值. 循环体中的命令会针对每个值执行一次.

从列表变量中遍历

可以使用 IN LISTS 语法从一个或多个列表变量中遍历值:

set(MyList "apple" "banana" "cherry")
set(AnotherList "date" "elderberry")foreach(LoopVar IN LISTS MyList AnotherList)message("Current value: ${LoopVar}")
endforeach()

在这个示例中, LoopVar 会依次取 MyListAnotherList 中的所有值.

从项目列表中遍历

也可以使用 IN ITEMS 语法直接列出要遍历的项目:

foreach(LoopVar IN ITEMS "dog" "cat" "bird")message("Current value: ${LoopVar}")
endforeach()

这种形式与基本形式类似, 但更明确地表明是在遍历项目列表.

遍历数值范围

从 CMake 3.0 版本开始, 可以使用 RANGE 关键字来遍历一个数值范围:

foreach(LoopVar RANGE 1 5)message("Current number: ${LoopVar}")
endforeach()

在这个示例中, LoopVar 会从 1 到 5 依次取值. 还可以指定步长:

foreach(LoopVar RANGE 1 10 2)message("Current number: ${LoopVar}")
endforeach()

这里的步长为 2, LoopVar 会依次取 1, 3, 5, 7, 9.

遍历多个列表

从 CMake 3.16 版本开始, 可以同时遍历多个列表. 每个列表中的对应元素会在每次迭代中组合在一起:

set(Fruits "apple" "banana" "cherry")
set(Colors "red" "yellow" "red")foreach(Fruit IN LISTS Fruits Color IN LISTS Colors)message("The ${Fruit} is ${Color}.")
endforeach()

在这个示例中, FruitColor 会分别从 FruitsColors 列表中取对应的值.

7.2.2 while() 循环

while() 循环会在条件为真时重复执行一组命令. 其基本形式如下:

while(condition)# 循环体message("Still in the loop...")
endwhile()

condition 是一个 if() 表达式, 与 if() 命令中的表达式规则相同. 只要 condition 为真, 循环体就会继续执行. 例如:

set(Counter 0)
while(Counter LESS 5)message("Counter value: ${Counter}")math(EXPR Counter "${Counter} + 1")
endwhile()

在这个示例中, Counter 变量从 0 开始, 每次循环增加 1, 直到 Counter 不再小于 5 为止.

7.2.3 中断循环

在循环中, 可以使用 break()continue() 命令来控制循环的执行流程.

break() 命令

break() 命令用于提前退出循环. 例如:

foreach(LoopVar RANGE 1 10)if(LoopVar EQUAL 5)break()endif()message("Current value: ${LoopVar}")
endforeach()

在这个示例中, 当 LoopVar 等于 5 时, break() 命令会被执行, 循环会立即终止.

continue() 命令

continue() 命令用于跳过当前迭代的剩余部分, 直接进入下一次迭代. 例如:

foreach(LoopVar RANGE 1 10)if(LoopVar EQUAL 5)continue()endif()message("Current value: ${LoopVar}")
endforeach()

在这个示例中, 当 LoopVar 等于 5 时, continue() 命令会被执行, 当前迭代的 message() 命令会被跳过, 循环会继续进行下一次迭代.

7.3 推荐实践

在使用 if(), foreach()while() 命令时, 有一些推荐的实践可以帮助避免常见的错误和混淆.

避免字符串歧义

如前所述, 在 if() 命令中, 带引号的字符串可能会有歧义, 特别是在 CMake 3.1 之前的版本中. 为了避免这种情况, 建议始终将策略 CMP0054 设置为 NEW, 并仅在确保存在同名变量时才使用未带引号的参数. 在 foreach()while() 命令中, 也应尽量避免使用可能被误解释为变量的字符串.

及时存储正则表达式匹配结果

在使用 if() 命令的 MATCHES 运算符时, 如果需要使用正则表达式匹配的结果, 建议及时将其存储在变量中. 因为 CMAKE_MATCH_<n> 变量在后续的命令执行过程中可能会被覆盖. 例如:

if("Some text with a number 123" MATCHES "number ([0-9]+)")set(MatchedNumber ${CMAKE_MATCH_1})message("Matched number: ${MatchedNumber}")
endif()

避免模糊的循环命令

在使用 foreach()while() 命令时, 应尽量避免使用模糊或可能误导读者的代码. 例如, 在使用 RANGE 关键字时, 应明确指定范围的起始值, 结束值和步长(如果需要), 以提高代码的可读性. 同时, 在循环体中, 应尽量保持逻辑清晰, 避免使用过于复杂的嵌套条件和操作.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/35856.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

糊涂人寄信

1.糊涂人寄信 - 蓝桥云课 糊涂人寄信 题目描述 有一个糊涂人&#xff0c;写了 n 封信和 n 个信封&#xff0c;到了邮寄的时候&#xff0c;把所有的信都装错了信封。求装错信封可能的种类数。 输入描述 有多行读入&#xff0c;每行输入一个正整数 n&#xff0c;表示一种情况…

华为ISC+战略规划项目数字化转型驱动的智慧供应链革新(169页PPT)(文末有下载方式)

资料解读&#xff1a;华为ISC战略规划项目数字化转型驱动的智慧供应链革新 详细资料请看本解读文章的最后内容。 华为的ISC战略规划项目是其供应链数字化转型的核心&#xff0c;旨在通过智慧供应链的革新&#xff0c;提升企业的竞争力和运营效率。本文将从多个维度详细解读这…

深度学习框架PyTorch——从入门到精通(5)自动微分

使用torch.autograd自动微分 张量、函数和计算图计算梯度禁用梯度追踪关于计算图的更多信息张量梯度和雅可比乘积 在训练神经网络时&#xff0c;最常用的算法是反向传播。在该算法中&#xff0c;参数&#xff08;模型权重&#xff09;根据损失函数的梯度相对于给定参数进行调整…

Mobile-Agent-V:通过视频引导的多智体协作学习移动设备操作

25年2月来自北京交大和阿里巴巴公司的论文“Mobile-Agent-V: Learning Mobile Device Operation Through Video-Guided Multi-Agent Collaboration”。 移动设备使用量的快速增长&#xff0c;迫切需要改进自动化以实现无缝任务管理。然而&#xff0c;因缺乏操作知识&#xff0…

单片机开发资源分析的实战——以STM32F103C8T6为例子的单片机资源分析

目录 第一点&#xff1a;为什么叫STM32F103C8T6 从资源手册拿到我们的对STM32F103C8T6的资源描述 第二件事情&#xff0c;关心我们的GPIO引脚输出 第三件事情&#xff1a;去找对应外设的说明部分 前言 本文章隶属于项目&#xff1a; Charliechen114514/BetterATK: This is…

《基于Spring Boot+Vue的智慧养老系统的设计与实现》开题报告

个人主页:@大数据蟒行探索者 一、研究背景及国内外研究现状 1.研究背景 根据1982年老龄问题世界大会联合国制定的标准,如果一个国家中超过65岁的老人占全国总人口的7%以上,或者超过60岁的老人占全国总人口的10%以上,那么这个国家将被定义为“老龄化社会”[1]。 随着国…

微软OneNote无法同步解决方案

目录 前言原因UWP特性 解决方案C***h注册表 参考链接 前言 假设有多台Windows电脑&#xff0c;最方便且免费的多设备笔记同步方案就是微软自家的OneNote&#xff0c;使用OneDrive自带的5G云存储。 但是在国内大陆的OneNote&#xff0c;经常会出现无法同步、同步失败&#xff1…

硬件设计抽象级别详解:门级、RTL级、行为级与HLS

硬件设计抽象级别详解&#xff1a;门级、RTL级、行为级与HLS 引言 在数字系统设计领域&#xff0c;硬件描述语言(HDL)提供了多种抽象级别来描述电路功能和结构。从最底层的门级描述到高层的行为级描述&#xff0c;每一种抽象级别都有其特定的用途和优势。理解这些不同级别以及…

WPF程序使用AutoUpdate实现自动更新

AutoUpdate.NET使用 一、AutoUpdater.NET 简介 AutoUpdater.NET 是一个开源库&#xff0c;支持从各种源&#xff08;如GitHub、FTP、HTTP服务器等&#xff09;下载并安装更新。它提供了灵活的配置选项&#xff0c;允许开发者根据需求定制更新检查逻辑和用户体验。 二、安装 …

Qwen2-Audio:通义千问音频大模型技术解读

引言:从llm到mlm(audio) 大型语言模型(LLM)的发展日新月异,它们在文本理解、生成、推理等方面展现出惊人的能力。然而,交互模态不仅仅依赖于文字,语音、语调、环境音等听觉信息同样承载着丰富的内容。阿里巴巴通义千问团队,推出了 Qwen-Audio 系列模型,这里我们一起…

问题 | ACOS(X) 与 ACOSD(X)的区别

github&#xff1a;https://github.com/MichaelBeechan CSDN&#xff1a;https://blog.csdn.net/u011344545 [TOC](ACOS(X) 与 ACOSD(X)的区别) ACOSD(X) 是反余弦函数&#xff0c;结果以角度形式表示。ACOS(X) 用于计算 X 中每个元素的反余弦值。当 X 为复数时&#xff0c;结…

两款软件助力图片视频去水印及图像编辑

今天给大家分享两款呼声很高的软件&#xff0c;它们都能处理图片和视频去水印相关的问题。其中一款软件在去水印的同时&#xff0c;图像编辑功能也十分出色&#xff1b;另一款软件专注于图片和视频去水印&#xff0c;去除效果好且支持批量处理。下面就来详细了解一下。 Remover…

Hessian矩阵详解与应用

前言 本文隶属于专栏《机器学习数学通关指南》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见《机器学习数学通关指南》 ima 知识库 知识库广场搜索&#…

【软件系统架构】单体架构

一、引言 在软件开发的漫长历程中&#xff0c;架构的选择一直是至关重要的决策。单体架构作为一种经典的架构模式&#xff0c;曾经在许多项目中发挥着不可替代的作用。虽然如今微服务等架构逐渐流行&#xff0c;但理解单体架构对于深入掌握软件架构体系仍然有着重要意义。 二、…

[C++初阶] :从C到C++

目录 C发展史&#xff0c;C语言的特性C新增关键字namespace关键字C语言的命名缺陷&#xff08;重定义现象&#xff09;域与指定访问操作符 “::”命名空间域详解namespace std C的输入与输出函数重载什么是重载,重载的几种常见形态重载的作用注意不构成重载的情况 缺省参数1.全…

[快乐学坊management_1] With Cursor | Mysql设计 | 服务接口设计与开发

目录 数据库设计流程 三张表 测试 接口设计 部门管理接口文档 1. 查询所有部门 2. 新增部门 ⭕3. 根据ID查询部门 4. 修改部门 5. 删除部门 &#xff08;部门分页条件查询&#xff09; 错误响应示例 接口设计规范 服务端开发 接口开发 数据库设计流程 01 明确业…

实用插件推荐 -------- 一个可以将任意语言(python、C/C++、go、java等)的程序转换为汇编语言的小插件

链接为&#xff1a; Compiler Explorer 界面&#xff1a; 参考自&#xff1a;如何获取虚函数表及内存分析_com的虚函数表怎么寻找-CSDN博客

vue学习八

十七 组件通信方式 1 props 父传子 //父组件 <script setup>//book来源省略import Subview1 from ./Subview1.vue;function updatebook(updatetimes){book.value.updatetimes updatetimes} </script> <template><Subview1 :book"book" :upd…

51单片机的寻址方式(完整)

目录 一、立即数寻址 二、直接寻址 三、寄存器寻址 四、寄存器间接寻址 五、变址寻址 六、位寻址 七、指令寻址 &#xff08;一&#xff09;绝对寻址 &#xff08;二&#xff09;相对寻址 在 51 单片机中&#xff0c;寻址方式是指在执行指令时&#xff0c;CPU 寻找操作…

每日一题:动态规划

如题&#xff08;基础题&#xff09;&#xff1a; 经典的爬楼梯问题&#xff0c;先从递归想起&#xff1b; class Solution { public:int climbStairs(int n) {if(n1)return 1;if(n2)return 2;return climbStairs(n-1)climbStairs(n-2);} }; 之后可以想办法&#xff08;如哈希…