返回首页

COBOL 子程序与调用

COBOL 子程序与调用

本章讲解如何将程序拆分为可复用的子程序(Subprogram),以及如何调用 C 库函数。

目录

  1. 子程序概述
  2. 定义子程序
  3. 调用子程序(CALL)
  4. 参数传递(LINKAGE SECTION)
  5. 调用 C 库函数
  6. 编译与链接
  7. 实战:snake.cob 调用 usleep

1. 子程序概述

COBOL 支持模块化编程,一个程序可以调用另一个程序。子程序是独立编译的模块,通过 CALL 语句调用。

优势:

  • 代码复用,避免重复
  • 模块化,便于维护
  • 可跨语言调用(如调用 C 函数)

两种形式:

  1. 内部子程序:同一源文件中的多个 PROGRAM-ID
  2. 外部子程序:独立编译为 .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.

编译为模块:

1
cobc -m addnums.cob        # 生成 addnums.so

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.

注意事项

  1. 字符串需以 NUL 结尾:C 字符串以 \x00 结尾,COBOL 的 PIC X 不会自动添加
    1
    01 WS-CMD PIC X(20) VALUE "ls -la" & x"00".
  2. **数值用 BY VALUE**:C 函数通常按值接收数值参数
  3. **数值类型用 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 # 生成 addnums.so
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.

分析:

  1. 分数 < 35 时,延时 = 200000 - 分数 × 4000 微秒(分数越高越快)
  2. 分数 ≥ 35 时,固定 60000 微秒(60ms)
  3. 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 库,无需额外链接参数。


上一章: 文件处理
下一章: 贪吃蛇实战解析