📖 前言
CMake作为跨平台编译构建的核心工具,其流程控制语法是项目脚本开发的基石🌿。循环结构更是批量处理参数、遍历配置、解析文件、规整数据的核心利器,贯穿大型项目编译脚本的全流程。
CMake循环体系极简二分,唯有foreach 与while 两类,二者各司其职、互补长短:foreach擅固定维度、已知范围 的批量迭代,while精未知范围、动态条件 的逻辑循环。二者皆兼容 break 终止循环、continue 跳过本次的流程控制语法,逻辑贴近C/C++,上手门槛极低。
本文将以骈叙笔法,由浅入深、层层递进,拆解两类循环的语法精髓、迭代模式、代码实战、场景适配、避坑要点,附全套可编译运行代码,助力开发者吃透CMake循环核心,告别脚本冗余、逻辑混乱问题✅。
Bilibili 同步视频
🌊 一、CMake循环核心总览:双循环架构解析
CMake循环语法规整对称,结构严谨有序,无繁杂冗余语法,整体遵循「起始声明-逻辑执行-结尾闭合」的标准化范式,与if条件判断语法一脉相承、遥相呼应。
✅ foreach循环 :确定性迭代专属,适配数字区间、数组列表、固定参数遍历,迭代范围可控、逻辑简洁,是CMake开发中使用率最高的循环结构。
✅ while循环:动态性循环专属,适配未知数据量、文件遍历、JSON解析、动态条件判断场景,灵活度更高,但需手动管控循环终止条件,规避死循环风险。
二者语法兼容通用流程控制指令:break 一键终止全局循环、continue 跳过单次迭代,逻辑语义与高级编程语言完全对齐,学习成本极低💡。
📚 二、foreach循环:多维迭代语法与全套实战代码
foreach循环语法规整、迭代模式丰富,支持数字区间迭代、单/多数组遍历、同步并行遍历三大核心场景,适配90%以上的固定迭代需求,是CMake批量数据处理的核心工具。
🍃 2.1 基础语法范式
语法结构首尾呼应,格式固定统一,可读性极强:
cmake
# foreach标准语法
foreach(迭代变量 迭代规则)
# 循环执行逻辑
endforeach()
迭代规则涵盖四大模式:纯终止区间、起止区间、步长迭代、列表遍历、zip同步遍历,适配各类固定迭代场景。
🍃 2.2 数字区间迭代:精准可控的数值遍历
foreach依托RANGE关键字实现数值迭代,支持从零起始、自定义起止、自定义步长 三种用法,区间取值首尾双向包含,区别于C++左闭右开区间特性,是极易踩坑的细节要点⚠️。
(1)从零起始迭代
语法:RANGE 终止值,迭代范围:[0, 终止值],包含首尾数值。
cmake
# 从零遍历至5(包含0、5)
foreach(VR RANGE 5)
message(STATUS "迭代数值:${VR}")
endforeach()
# 输出结果:0 1 2 3 4 5
(2)自定义起止迭代
语法:RANGE 起始值 终止值,迭代范围:[起始值, 终止值],全量包含区间数值。
cmake
# 从1遍历至3(包含1、3)
foreach(VR RANGE 1 3)
message(STATUS "迭代数值:${VR}")
endforeach()
# 输出结果:1 2 3
(3)自定义步长迭代
语法:RANGE 起始值 终止值 步长,实现间隔式迭代,适配批量间隔取值场景。
cmake
# 从1遍历至100,步长为3
foreach(VR RANGE 1 100 3)
message(STATUS "步长迭代数值:${VR}")
endforeach()
# 输出结果:1 4 7 ... 97 100
💡 优化技巧:字符串拼接统一输出
原生逐行打印日志杂乱冗余,可通过string(APPEND)拼接迭代结果,单次输出全局数据,日志更整洁:
cmake
set(OUT "") # 初始化空字符串
foreach(VR RANGE 0 50 5)
string(APPEND OUT "${VR} ") # 拼接迭代结果,空格分隔
endforeach()
message(STATUS "统一输出结果:${OUT}")
🍃 2.3 数组列表迭代:三类遍历模式精讲
CMake开发中,列表(List)是核心数据载体,foreach针对列表迭代提供IN List、IN Items、ZIP List三种模式,适配单列表、多列表、同步配对遍历场景✨。
(1)IN List:通用多列表顺序遍历
最常用遍历方式,直接传入列表变量名,无需解包取值,支持同时遍历多个列表,执行逻辑为「遍历完第一个列表,再遍历第二个列表」,可快速实现多列表拼接。
cmake
# 定义两组列表
set(LIST_A A B C D E)
set(LIST_B 1 2 3 4 5)
# 遍历双列表,顺序迭代
foreach(VR IN LISTS LIST_A LIST_B)
string(APPEND OUT "${VR} ")
endforeach()
message(STATUS "多列表顺序遍历:${OUT}")
# 输出:A B C D E 1 2 3 4 5
(2)IN Items:固定值直接遍历
区别于IN List,Items模式不支持列表变量,仅接收具体数值、字符串常量,多用于少量固定参数遍历,使用场景相对局限。
cmake
# Items仅支持直接传值,不可传变量
foreach(VR IN Items 10 20 30 40)
message(STATUS "Items遍历:${VR}")
endforeach()
(3)ZIP List:多列表同步配对遍历(高版本特性)
该特性仅支持CMake3.17及以上版本 ,是高频实用的高阶语法,可实现多列表按索引同步配对遍历,无需嵌套循环,完美适配键值对、参数配对场景。
两种取值方式:单变量索引取值、多变量直接映射取值,灵活适配不同业务需求:
cmake
# 定义配对列表
set(NAME_LIST 张三 李四 王五)
set(AGE_LIST 22 24 26)
# 方式1:单变量 + 索引取值(-0、-1对应第1、2个列表)
foreach(VR IN ZIP_LISTS NAME_LIST AGE_LIST)
message(STATUS "姓名:${VR_0},年龄:${VR_1}")
endforeach()
# 方式2:多变量直接映射,语义更直观
foreach(NAME AGE IN ZIP_LISTS NAME_LIST AGE_LIST)
message(STATUS "姓名:${NAME},年龄:${AGE}")
endforeach()
🍃 2.4 foreach流程控制:break与continue实战
foreach循环完美继承高级语言流程控制逻辑,break终止全循环、continue跳过本次迭代,可精准筛选迭代数据,实现条件式数据处理。
(1)break:条件终止全局循环
满足指定条件时,直接跳出整个循环,终止所有后续迭代,适配阈值拦截场景。
cmake
# 0-100迭代,大于50立即终止
foreach(VR RANGE 0 100)
if(${VR} GREATER 50)
break # 终止全局循环
endif()
message(STATUS "有效数值:${VR}")
endforeach()
# 仅输出0-50数值
(2)continue:跳过单次迭代
满足条件时,跳过当前迭代剩余逻辑,直接进入下一次迭代,适配数据筛选场景。
cmake
# 仅输出3的倍数,其余数值跳过
foreach(VR RANGE 0 20)
math(EXPR RES "${VR} % 3") # 取余运算
if(NOT ${RES} EQUAL 0)
continue # 非3的倍数,跳过本次
endif()
message(STATUS "3的倍数:${VR}")
endforeach()
🍃 2.5 foreach实战核心准则
🎯 项目开发最优实践:优先使用IN List模式处理列表数据,尽量不在循环内编写复杂逻辑,优先通过list指令预处理数据(去重、排序、增删),简化循环逻辑,提升脚本可读性与执行效率。
🌿 三、while循环:动态条件循环与避坑实战
while循环无固定迭代范围,依托自定义条件表达式 驱动循环,灵活度远超foreach,专攻未知数据量、动态读取、条件判定场景,是文件遍历、JSON解析、动态参数处理的核心语法。
🍃 3.1 基础语法范式
语法简洁精炼,核心为条件表达式,条件为真持续循环,条件为假终止循环:
cmake
while(条件表达式)
# 循环执行逻辑
endwhile()
🍃 3.2 核心特性与死循环避坑指南
while循环最大隐患为死循环 ,因无固定迭代终止机制,若条件变量无动态更新,会导致循环永久执行、编译卡死⚠️。核心解决思路:动态更新条件变量、手动配置终止逻辑。
两种合法终止方式:
-
动态修改变量值,让条件表达式由真变假,自然终止循环;
-
通过break指令,满足阈值条件时强制终止循环。
实战代码:安全可控的while循环
cmake
set(VR 1) # 初始化循环变量
while(${VR} LESS EQUAL 100)
# 数值大于100,强制终止循环
if(${VR} GREATER 100)
break
endif()
message(STATUS "while迭代数值:${VR}")
# 动态更新变量,避免死循环
math(EXPR VR "${VR} + 1")
endwhile()
🍃 3.3 while结合continue实现精准数据筛选
依托continue跳过无效数据,精准筛选目标内容,以下示例实现「仅输出10的倍数」的筛选逻辑:
cmake
set(VR 1)
while(${VR} LESS EQUAL 50)
math(EXPR RES "${VR} % 10")
# 非10的倍数,跳过本次迭代
if(NOT ${RES} EQUAL 0)
math(EXPR VR "${VR} + 1")
continue
endif()
message(STATUS "10的倍数:${VR}")
math(EXPR VR "${VR} + 1")
endwhile()
# 输出:10 20 30 40 50
🍃 3.4 while循环适配场景总结
✅ 适配场景:未知长度文件逐行遍历、JSON动态数据解析、动态参数读取、不确定次数的条件校验
❌ 不适配场景:固定数值区间遍历、已知列表迭代(优先foreach,更简洁高效)
⚖️ 四、foreach与while核心差异与场景选型
为便于快速落地实战,现将双循环核心特性、适用场景凝练对比,一文厘清选型逻辑📝:
| 对比维度 | foreach循环 | while循环 |
|---|---|---|
| 迭代特性 | 固定范围、确定性迭代 | 动态条件、不确定性迭代 |
| 使用难度 | 低,无死循环风险 | 中,需手动管控终止条件 |
| 核心场景 | 数值遍历、列表迭代、固定参数处理 | 文件遍历、JSON解析、动态条件判断 |
| 版本依赖 | ZIP模式需3.17+,其余全版本兼容 | 全版本兼容,无版本限制 |
| 实战优先级 | ⭐⭐⭐⭐⭐(首选) | ⭐⭐⭐(按需使用) |
🎯 五、开发实战总结与最佳实践
1、固定迭代优先foreach:但凡已知迭代范围、固定列表数据,一律使用foreach,语法简洁、零死循环风险、可读性更强,是CMake循环的首选方案✅。
2、动态场景适配while:面对未知数据量、动态读取、条件可变的业务逻辑,选用while循环,灵活适配复杂场景,但务必做好变量更新与终止判断,杜绝编译卡死⚠️。
3、简化循环内部逻辑:尽量将数据预处理(去重、排序、拼接、截取)通过list、string指令实现,不在循环内堆砌复杂判断与运算,让脚本逻辑分层清晰、易于维护💡。
4、流程控制精准复用:break用于全局终止、continue用于单次跳过,二者搭配可实现绝大多数数据筛选与逻辑拦截需求,无需冗余嵌套if判断。
5、版本特性按需启用:ZIP List等高阶语法仅支持3.17+高版本CMake,跨平台项目需做好版本兼容适配,避免编译报错。
📌 文末寄语
CMake循环语法看似简约,实则暗藏章法,foreach守「规整有序」之态,while擅「灵活变通」之能,一静一动、相辅相成。深耕二者语法特性、精准匹配业务场景,摒弃生搬硬套的编码思维,方能写出简洁高效、稳健易维护的CMake构建脚本,为项目跨平台编译筑牢根基🌿。
