You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

COBOL调用RAND模块意外修改无关变量问题排查求助

COBOL子程序调用导致未传入变量被意外修改的问题分析与解决

问题描述

本人接触COBOL仅一周,编写了石头剪刀布(RPS)游戏,将随机数生成逻辑独立为RAND模块通过CALL调用。但调用该模块时,未传入的USER-WIN、COMP-WIN变量会被意外修改。已尝试调整变量层级无效,将随机数逻辑放回主程序则运行正常;将RAND模块中UPPER-BOUND、RAND-VAL的长度改为9(15)及以下时,问题也会消失。

使用GnuCOBOL编译器,编译命令为:

cobc -m -free RAND.COBOL
cobc -x -free RPS.COBOL

运行环境为Linux Mint Xfce。

原因分析

核心问题是主程序与子程序的参数长度不匹配,导致内存越界写入:

  • 主程序中UPPER-BOUND和RAND-VAL定义为PIC 9,仅占用1字节内存;
  • 但RAND模块的LINKAGE SECTION中,这两个参数被定义为PIC 9(38),每个参数占用38字节内存。

当调用子程序时,COBOL会按照子程序的参数定义来读写内存:

  1. 子程序处理第一个参数UPPER-BOUND时,会试图读取38字节的数据,而主程序只传入了1字节,超出的37字节会读取后续内存的垃圾数据;
  2. 处理第二个参数RAND-VAL时,子程序会写入38字节的数据,而主程序仅为该变量分配了1字节,超出的37字节会直接覆盖后续内存区域;
  3. 在主程序的内存布局中,USER-WIN、COMP-WIN刚好位于RAND变量之后,因此被越界写入的内容篡改。

修改变量长度生效的原理

当把RAND模块的参数改为9(15)及以下时,GnuCOBOL会将这些数值型变量映射为64位整数(占用8字节内存)。此时虽然主程序的参数(1字节)和子程序的参数(8字节)仍然不匹配,但越界写入的内存范围从37字节缩小到7字节,刚好没有覆盖到USER-WIN和COMP-WIN所在的内存区域,因此问题暂时消失。但这只是巧合,并非根本解决方法,后续仍可能出现其他内存越界问题。

预防措施

  • 严格匹配参数定义:主程序调用时传入的变量,必须与子程序LINKAGE SECTION中定义的参数在数据类型、长度、精度上完全一致;
  • 使用COPYBOOK统一参数结构:将需要传递的参数定义在COPYBOOK文件中,主程序和子程序都引用该文件,避免手动定义导致的不一致;
  • 启用编译警告:编译时添加-Wall参数(如cobc -Wall -m -free RAND.COBOL),GnuCOBOL会检测到参数长度不匹配的问题并给出警告;
  • 使用内存调试工具:借助valgrind等工具运行程序,能快速定位内存越界的具体位置;
  • 按需选择数值长度:根据实际需求选择合适的数值类型长度,比如本场景中只需要0-2的随机数,使用PIC 9完全足够,无需9(38)。

附程序代码

主程序(RPS.COBOL)

IDENTIFICATION DIVISION.
PROGRAM-ID. ADVENT-RPS.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 CHOICE.
    03 USER-CHOICE PIC A(1).
    03 COMP-CHOICE PIC A(1).
01 WIN.
    03 COMP-WIN PIC 9 VALUE 0.
    03 USER-WIN PIC 9 VALUE 0.
01 RAND.
    03 UPPER-BOUND PIC 9 VALUE 3.
    03 RAND-VAL PIC 9.
PROCEDURE DIVISION.
    DISPLAY "Choose R, P, or S"
    PERFORM UNTIL 1 = 0
*>      ACCEPT USER INPUT FROM TERMINAL AND CHECK FOR CORRECTNESS
        PERFORM USER-PIC
*>      GENERATES RANDOM COMPUTER CHOICE  
        PERFORM COMP-PICK
*>      CHECKS WHO WON THE ROUND AND IF SOMEONE WON THE GAME 
        PERFORM CHECK-WIN
    END-PERFORM.
    STOP RUN.
USER-PIC.
    ACCEPT USER-CHOICE FROM CONSOLE.
    IF (USER-CHOICE NOT = "R") AND
       (USER-CHOICE NOT = "P") AND
       (USER-CHOICE NOT = "S")
       DISPLAY "Please choose a valid input (R, P, S)."
       PERFORM USER-PIC
    END-IF.
COMP-PICK.
*>  CAUSING USER-WIN AND COMP-WIN TO CHANGE UNCONTROLLABLY 
    CALL "RAND" USING UPPER-BOUND, RAND-VAL.
    IF RAND-VAL = 0
       MOVE "R" TO COMP-CHOICE
    ELSE
       IF RAND-VAL = 1
          MOVE "P" TO COMP-CHOICE
       ELSE
          MOVE "S" TO COMP-CHOICE
       END-IF
    END-IF.
    DISPLAY "Computer chose: " COMP-CHOICE.
CHECK-WIN.
    IF USER-CHOICE = COMP-CHOICE
        DISPLAY "It's a tie!"
        EXIT PARAGRAPH
    ELSE
        IF (USER-CHOICE = "R" AND COMP-CHOICE = "P") OR
           (USER-CHOICE = "P" AND COMP-CHOICE = "S") OR
           (USER-CHOICE = "S" AND COMP-CHOICE = "R")
            DISPLAY "Computer beat you this round!"
            COMPUTE COMP-WIN = COMP-WIN + 1
        ELSE
            DISPLAY "You beat the computer this round!"
            COMPUTE USER-WIN = USER-WIN + 1
        END-IF
    END-IF.

    IF USER-WIN = 2
        DISPLAY "You won!"
        STOP RUN.
    IF COMP-WIN = 2
        DISPLAY "Computer won!"
        STOP RUN.
END PROGRAM ADVENT-RPS.

RAND模块(RAND.COBOL)

IDENTIFICATION DIVISION.
PROGRAM-ID. RAND.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SEED-STRING PIC X(16).
01 WS-SEED-INT PIC 9(16).
LINKAGE SECTION.
01 UPPER-BOUND PIC 9(38).
01 RAND-VAL PIC 9(38).
PROCEDURE DIVISION USING UPPER-BOUND, RAND-VAL.
    MOVE FUNCTION CURRENT-DATE TO WS-SEED-STRING.
    MOVE FUNCTION NUMVAL(WS-SEED-STRING) TO WS-SEED-INT.
    COMPUTE RAND-VAL = FUNCTION RANDOM(WS-SEED-INT) * UPPER-BOUND.
EXIT PROGRAM.

内容的提问来源于stack exchange,提问作者Ethan Krug

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 05:27:56