在结构化文本(ST)编程中,;(分号)不是可选项,而是语法终结符。它标志着一条完整语句的结束。没有它,编译器无法确定指令边界,从而立即报错。这是电气自动化工程师——尤其是刚接触IEC 61131-3标准PLC编程的新手——最常遇到、最易忽视、也最耽误调试时间的错误。
一、为什么ST语言必须用分号?
ST(Structured Text)是IEC 61131-3定义的五种PLC编程语言之一,语法类似Pascal和C。它采用显式语句终止机制:每条可执行语句(赋值、调用、条件、循环等)必须以分号结尾。
这与梯形图(LD)或功能块图(FBD)不同——后两者靠图形连接隐含执行顺序;而ST是纯文本,依赖标点划分逻辑单元。编译器逐行扫描源码,遇到换行不等于语句结束,只看到分号才确认:“这一条指令到此为止”。
例如:
MotorSpeed := 1500
这段代码语法非法。编译器读完 1500 后继续向后查找,直到行末或文件尾仍未发现 ;,于是报错:
Error 214: Missing semicolon at end of statement
而加上分号后:
MotorSpeed := 1500;
编译器立刻识别为一条完整赋值语句,解析通过。
注意:分号仅终止语句,不终止声明。变量声明、函数声明、程序组织单元(POU)定义等使用 VAR ... END_VAR 或 FUNCTION_BLOCK ... END_FUNCTION_BLOCK 等成对关键字包裹,内部不需分号(但其内部语句仍需)。
二、哪些地方必须写分号?(逐类实操清单)
以下所有场景均以 ; 结尾,缺一不可。请对照你的代码逐项核对:
-
简单赋值语句
输入:=右侧是常量、变量、表达式时,整行必须加分号。
✅ 正确:Temperature := 25.3; PumpState := TRUE; CycleTime := T#5S; -
复合表达式赋值
即使含运算符、函数调用,只要是一条赋值,就需分号。
✅ 正确:OutputValue := (InputA * Gain) + Offset; MaxTemp := MAX(Temp1, Temp2, Temp3); TimerStart := NOT TimerDone AND StartButton; -
IF 条件语句的每个分支结尾
IF...THEN块内每条独立语句都要分号;ELSIF和ELSE后的语句同理。
✅ 正确:IF MotorOverload THEN AlarmCode := 101; SoundAlarm := TRUE; ELSIF MotorReady THEN MotorStart := TRUE; ResetFault := FALSE; ELSE MotorStart := FALSE; END_IF;⚠️ 注意:
END_IF后也要加分号(它是语句级关键字,不是块标记)。 -
FOR / WHILE 循环体内的每条语句
循环内部仍是普通语句序列,逐条终结。
✅ 正确:FOR i := 0 TO 9 DO DataBuffer[i] := SensorRead(i); DelayCounter[i] := DelayCounter[i] + 1; END_FOR; -
CASE 语句各分支中的语句
每个CASE ... OF分支下若含多条语句,每条都需分号;单条也需。
✅ 正确:CASE Mode OF 0: StateLED := OFF; 1: BEGIN StateLED := GREEN; Buzzer := FALSE; END; 2: StateLED := RED; END_CASE;⚠️
BEGIN...END是复合语句容器,其内部语句仍须分号;END_CASE后同样需要分号。 -
函数/功能块调用语句
调用本身是一条语句,必须分号。
✅ 正确:CalcPID(SETPOINT:=SP, PROCESSVAR:=PV, OUTPUT=>OutVal); SendCANMessage(ID:=100, DATA:=CanData); -
RETURN 语句
RETURN;是独立语句,不可省略分号。
✅ 正确:FUNCTION CheckLimit : BOOL VAR Input : REAL; END_VAR IF Input > 100.0 THEN CheckLimit := TRUE; RETURN; ELSE CheckLimit := FALSE; END_IF;
三、哪些地方绝对不加分号?(高频误加区)
错误加分号比漏加分号更隐蔽,可能引发“语法正确但逻辑异常”的问题。
-
VAR 声明区内部
变量声明用:分隔类型,用换行分隔不同变量,不加分号。
❌ 错误:VAR Speed : INT; Temp : REAL; Running : BOOL; END_VAR;✅ 正确:
VAR Speed : INT Temp : REAL Running : BOOL END_VAR -
TYPE 自定义类型定义中
类型字段声明同上,无分号。
✅ 正确:TYPE MOTOR_CONFIG : STRUCT MaxSpeed : INT MinVoltage : REAL EnablePin : BYTE END_STRUCT END_TYPE -
IF / FOR / CASE 等关键字之后
IF、FOR、CASE后跟的是条件表达式或值列表,不是语句,不加分号。
❌ 错误:IF Fault = TRUE; THEN // 多余分号!编译直接失败 FOR i := 0 TO n; DO // 错误:分号不能出现在 DO 前 -
数组索引、结构体成员访问等表达式内部
这些是语法成分,非语句。
❌ 错误:Buffer[0;] := 1; // [0;] 是非法语法 Motor.Status; := TRUE; // ; 不能插在 . 和标识符之间
四、典型报错信息与定位技巧
不同PLC品牌编译器提示略有差异,但核心关键词高度一致。遇到以下任一提示,请第一反应检查分号:
| 报错原文(示例) | 含义 | 定位建议 |
|---|---|---|
Expected ';' before token 'xxx' |
编译器在遇到 xxx(如 THEN, DO, END_IF)前,预期先看到分号 |
查看 xxx 前一行末尾是否漏掉 ; |
Unexpected end of file |
文件末尾未收尾,通常因最后一行语句缺分号 | 检查 .ST 文件最后一行是否以 ; 结束 |
Syntax error: missing semicolon |
明确指出缺失分号 | 光标通常停在出错行末或下一行开头 |
Invalid statement termination |
终止符非法(如用了中文分号、空格后加分号) | 检查是否误按了中文输入法下的 ;(Unicode U+FF1B),或 ;(分号后带空格再换行) |
💡 快速定位法:
- 在编辑器中启用“显示所有字符”(通常为 ¶ 图标),查看行尾是否有
¶(换行符)而无;; - 将光标置于疑似出错行末,按
Home→End观察是否跳转到;后——若直接停在行尾,说明无分号; - 使用编辑器“括号高亮”功能:将光标放在
END_IF、END_FOR等处,看对应IF、FOR是否被高亮——若不匹配,大概率是中间某条语句缺分号导致块解析错位。
五、预防策略:四步固化习惯
-
敲完等号
:=后,立即按分号;**
养成肌肉记忆::=和;是一对固定组合键入。不要思考,形成条件反射。 -
粘贴代码后,第一件事:扫视所有行尾
新增或复制代码段后,用鼠标拖选全部代码 → 按Ctrl+F输入;$`(正则模式开),搜索行尾分号。若结果数 < 你预估的语句数,立即补全。 3. **启用编辑器自动补全** 大多数专业PLC IDE(如TIA Portal、Codesys、Unity Pro)支持: - 输入 `:=` 后自动追加 `;`; - 输入 `IF` 后自动生成 `IF ... THEN\n\nEND_IF;` 框架; - 输入 `FOR` 自动生成 `FOR ... DO\n\nEND_FOR;`。 **务必开启这些选项,并信任它们**。 4. **提交前执行“分号审计”宏(推荐)** 若使用支持脚本的编辑器(如VS Code + Codesys插件),可配置一键命令: - 高亮所有以字母/数字结尾、后跟换行的行; - 或反向高亮所有以 `;` 结尾的行,对比总行数。 示例正则(VS Code搜索): `^[^;]*[a-zA-Z0-9]\s*$—— 匹配“行尾是字母数字且无分号”的行(需开启正则模式)。
六、真实故障案例复盘
现象:某包装线PLC程序编译失败,报错 Error 214 at line 87。该行内容为:
IF ProductCount >= BatchSize THEN
排查过程:
- 行号87是
IF行,但报错指向它——说明编译器在此卡住; - 实际问题在上一行:第86行为
BatchSize := 100,漏了分号; - 编译器将第86行与第87行合并解析为
BatchSize := 100 IF ProductCount ...,导致语法崩溃; - 修复:在第86行末添加
;,编译通过。
✅ 教训:报错行号往往是“问题暴露位置”,而非“问题发生位置”。分号缺失会导致编译器后续解析全面错乱,因此永远从报错行的上一行开始检查。
七、高级陷阱:分号与作用域嵌套
当嵌套多层结构时,分号位置稍有偏差,就会改变逻辑归属。
❌ 错误写法(意图:仅在 i=5 时置位 Flag):
FOR i := 1 TO 10 DO
IF i = 5 THEN
Flag := TRUE;
END_IF
END_FOR;
⚠️ 问题:END_IF 后无分号 → 编译器认为 END_IF 是 FOR 循环体的最后一条语句,但 END_FOR 前缺少分号,导致语法错误。
✅ 正确写法:
FOR i := 1 TO 10 DO
IF i = 5 THEN
Flag := TRUE;
END_IF;
END_FOR;
再看一个更隐蔽的案例:
❌ 错误:
IF SensorOK THEN
MotorRun := TRUE;
Timer(IN := MotorRun, PT := T#2S);
END_IF;
⚠️ 表面看无问题,但 Timer(...) 是功能块调用,必须以分号结尾。此处虽写了分号,却写在了括号内:Timer(...); → ✅ 正确;但若误写为 Timer(...)(无分号),或 Timer(...); 被意外删掉,则整条调用失效。
✅ 最终合规版本:
IF SensorOK THEN
MotorRun := TRUE;
Timer(IN := MotorRun, PT := T#2S);
END_IF;
分号不是装饰,是ST语言的呼吸节点。它不增加功能,但缺失即致死。每一次编译失败,都是编译器在提醒你:语句的边界,必须亲手划定。

暂无评论,快来抢沙发吧!