COBOL 子程序与调用
本章讲解如何将程序拆分为可复用的子程序(Subprogram),以及如何调用 C 库函数。
目录
- 子程序概述
- 定义子程序
- 调用子程序(CALL)
- 参数传递(LINKAGE SECTION)
- 调用 C 库函数
- 编译与链接
- 实战:snake.cob 调用 usleep
1. 子程序概述
COBOL 支持模块化编程,一个程序可以调用另一个程序。子程序是独立编译的模块,通过 CALL 语句调用。
优势:
- 代码复用,避免重复
- 模块化,便于维护
- 可跨语言调用(如调用 C 函数)
两种形式:
- 内部子程序:同一源文件中的多个
PROGRAM-ID
- 外部子程序:独立编译为
.so 或 .o 模块
2. 定义子程序
内部子程序
一个源文件可包含多个程序,主程序在前,子程序在后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| IDENTIFICATION DIVISION. PROGRAM-ID. MAIN-PROG. DATA DIVISION. WORKING-STORAGE SECTION. 01 WS-RESULT PIC 9(5). PROCEDURE DIVISION. MAIN-PROCEDURE. CALL "ADD-NUMBERS" USING 10 20 WS-RESULT DISPLAY "结果: " WS-RESULT STOP RUN.
IDENTIFICATION DIVISION. PROGRAM-ID. ADD-NUMBERS. DATA DIVISION. LINKAGE SECTION. 01 L-A PIC 9(5). 01 L-B PIC 9(5). 01 L-SUM PIC 9(5). PROCEDURE DIVISION USING L-A L-B L-SUM. COMPUTE L-SUM = L-A + L-B EXIT PROGRAM.
|
外部子程序
独立文件 addnums.cob:
1 2 3 4 5 6 7 8 9 10
| IDENTIFICATION DIVISION. PROGRAM-ID. ADD-NUMBERS. DATA DIVISION. LINKAGE SECTION. 01 L-A PIC 9(5). 01 L-B PIC 9(5). 01 L-SUM PIC 9(5). PROCEDURE DIVISION USING L-A L-B L-SUM. COMPUTE L-SUM = L-A + L-B EXIT PROGRAM.
|
编译为模块:
3. 调用子程序(CALL)
基本调用
1 2
| CALL "SUB-PROG". CALL "SUB-PROG" USING 参数1 参数2.
|
带返回码
1 2 3
| CALL "SUB-PROG" USING WS-ARG RETURNING WS-RETURN-CODE END-CALL.
|
动态调用(变量指定程序名)
1 2
| 01 WS-PROG-NAME PIC X(20) VALUE "ADD-NUMBERS". CALL WS-PROG-NAME USING WS-A WS-B WS-SUM.
|
调用约定
1 2 3
| CALL "SUB-PROG" USING BY REFERENCE WS-A *> 传引用(默认) CALL "SUB-PROG" USING BY CONTENT WS-A *> 传值(只读) CALL "SUB-PROG" USING BY VALUE WS-A *> 传值(C 风格)
|
| 方式 |
说明 |
BY REFERENCE |
传地址,子程序可修改原变量(默认) |
BY CONTENT |
传副本,子程序不能修改原变量 |
BY VALUE |
传值,用于调用 C 函数 |
4. 参数传递(LINKAGE SECTION)
子程序用 LINKAGE SECTION 接收参数:
1 2 3 4 5 6 7 8 9 10
| IDENTIFICATION DIVISION. PROGRAM-ID. CALC-AREA. DATA DIVISION. LINKAGE SECTION. 01 L-WIDTH PIC 9(5). 01 L-HEIGHT PIC 9(5). 01 L-AREA PIC 9(10). PROCEDURE DIVISION USING L-WIDTH L-HEIGHT L-AREA. COMPUTE L-AREA = L-WIDTH * L-HEIGHT EXIT PROGRAM.
|
要点:
LINKAGE SECTION 中的变量不分配存储,只是映射调用方传入的内存
PROCEDURE DIVISION USING 列出参数,顺序必须与 CALL 一致
EXIT PROGRAM 返回调用方(STOP RUN 会终止整个程序)
参数数量可变
1 2
| PROCEDURE DIVISION USING L-A L-B L-C. *> 调用方可以只传部分参数,未传的为未定义
|
5. 调用 C 库函数
GnuCOBOL 可直接调用 C 标准库函数,这是其强大之处。
调用示例
1 2 3 4 5 6 7 8 9 10 11 12
| *> 调用 C 的 printf CALL "printf" USING BY REFERENCE "Hello %s\n" BY REFERENCE "COBOL" END-CALL.
*> 调用 C 的 usleep(微秒延时) CALL "usleep" USING BY VALUE WS-DELAY-US END-CALL.
*> 调用 C 的 system CALL "system" USING BY REFERENCE "ls -la" & x"00" END-CALL.
|
注意事项
- 字符串需以 NUL 结尾:C 字符串以
\x00 结尾,COBOL 的 PIC X 不会自动添加1
| 01 WS-CMD PIC X(20) VALUE "ls -la" & x"00".
|
- **数值用
BY VALUE**:C 函数通常按值接收数值参数
- **数值类型用
COMP-5**:确保二进制格式与 C 一致1
| 01 WS-DELAY-US PIC 9(6) COMP-5 VALUE 200000.
|
6. 编译与链接
编译主程序 + 子程序
1 2 3 4 5 6
| cobc -x -o main main.cob addnums.cob
cobc -m addnums.cob cobc -x -o main main.cob -L. -laddnums
|
链接 C 库
1
| cobc -x -o prog prog.cob -lm
|
运行时查找模块
外部子程序(.so)需在运行时能被找到:
1 2
| export COB_LIBRARY_PATH=.:$COB_LIBRARY_PATH ./main
|
7. 实战:snake.cob 调用 usleep
snake.cob 通过调用 C 库函数 usleep 实现游戏延时:
数据声明
1 2
| *> ---- 延时 (微秒) ---- 01 WS-DELAY-US PIC 9(6) COMP-5 VALUE 200000.
|
PIC 9(6):6 位数字,最大 999999 微秒
COMP-5:原生二进制格式,与 C 的 unsigned int 兼容
延时逻辑
1 2 3 4 5 6 7 8 9
| *> ---- 延时 (分数越高越快, 最低 60ms) ---- DELAY. IF WS-SCORE < 35 COMPUTE WS-DELAY-US = 200000 - WS-SCORE * 4000 ELSE MOVE 60000 TO WS-DELAY-US END-IF CALL "usleep" USING BY VALUE WS-DELAY-US END-CALL.
|
分析:
- 分数 < 35 时,延时 = 200000 - 分数 × 4000 微秒(分数越高越快)
- 分数 ≥ 35 时,固定 60000 微秒(60ms)
CALL "usleep" USING BY VALUE 调用 C 函数,BY VALUE 传递数值
为什么用 BY VALUE?
C 函数 usleep(useconds_t usec) 按值接收参数。若用默认的 BY REFERENCE,C 函数会收到指针而非数值,导致错误。
编译
1
| cobc -x -o build/snake snake.cob
|
usleep 属于标准 C 库,无需额外链接参数。
上一章: 文件处理
下一章: 贪吃蛇实战解析