第7章 向COBOL专家迈进
这一章讨论COBOL语言的几个语法成分,这些是为了提高COBOL本身所必需的熟练程度而包括进来的。我们完整地讨论了子程序相关的COBOL成分:CALL USING、PROCEDURE DIVISION USING、EXIT PROGRAM及LINKAGE SECTION。
接下来我们完整地讨论表处理。涉及OCCURS短语的基本定义及如何通过索引来处理多级表。讨论初始化表的各种方法及SEARCH、SET和SEARCH ALL短语的使用方法。
这一章讲解了这一语言的高级特性,这些高级特性包括子程序和表的处理。我们提供了几个例子,让读者可以清楚了解这些高级语言成分的使用方法。
7.1 主程序和子程序
对于某些需要重复执行的功能,比如银行的计算利息模块,可以编成一个子程序供多个主程序调用。子程序在执行完计息功能后,再返回到主程序中。这样的设计可以保持计息模块的代码的相对独立性,能在计息方法或利率发生变化时,不用维护多个主程序,而只要维护与计息相关的子程序就可以了。在COBOL语言中,主程序通过CALL语句调用子程序,而子程序在完成自己的功能后,通过EXIT PROGRAM命令返回到主程序。主程序和子程序之间通过USING短语传递参数,USING短语出现在主程序的CALL语句和子程序的PROCEDURE DIVISION头中,尽管参数的名字可以不同,但参数之间的顺序关系则需要是正确对应的。
7.1.1 主程序的CALL语句
下面是主程序调用子程序的CALL语句的语法。
CALL语句在同一个运行单元(Run Unit)内将控制权由一个程序转移到另一个程序中。带有CALL语句的程序我们称之为调用程序或主程序;程序名出现在CALL语句中的程序是子程序(Subprogram)。被调用程序也可以使用CALL语句调用其他的程序,但是被调用的程序不能直接或间接地调用它的主程序。如果想直接或间接调用自己的主程序(调用程序),就必须使用递归(RECURSIVE)短语。
在使用CALL语句时,你可以使用ProgNameIdentifier或ProgNameliteral来指定要调用的目标程序。ProgNameIdentifier必须是字母数字型的,使得它的值可以是程序名。要调用的目标程序名必须在常量ProgNameliteral或变量ProgNameidentifier的内容中指定。
当被调用的子程序不可用时,就会出现例外情况(Exception Condition),这时,程序可以采取下面两种方法中的一种来处理。
● 如果指定了ON EXCEPTION短语,控制就会转移去执行强制语句块StatementBlock1中的语句。如果语句块StatementBlock1中没有包含分支语句,则在语句块StatementBlock1中的语句执行完后,控制就会转去执行紧跟在CALL语句后面的语句,这时,如果你也指定了NOT ON EXCEPTION短语,就会忽略掉:如果在语句块StatementBlock1中指定了分支语句(如PERFORM或GO TO语句),则程序就会根据这些语句的逻辑继续执行,换句话说,程序可能不会返回到紧跟在CALL语句后面的语句中。
● 如果在CALL语句中没有指定ON EXCEPTION短语,则即使指定了NOT ON EXCEPTION短语,系统也会忽略它。
短语ON OVERFLOW用来检查执行CALL语句时是否出现了溢出(Overflow)的情况。所谓的溢出情况指的是系统没有足够的内存来装载被调用的子程序,如果出现这种情况,程序就会执行ON OVERFLOW和END-CALL之间的语句(StatementBlock2)。
COBOL的调用可以是静态调用(Static CALL),这时被调用的子程序必须与调用程序一起连接(link-Edited)起来形成一个完整的装载模块(Load Module),但子程序依然可以单独编译。这种方法会生成一个大的模块,同时也使得多个主程序调用同一个子程序时共享程序副本的愿望落空。
另外一种COBOL调用方法是动态调用(Dynamic CALL),这时被调用的子程序必须编译和连接成一个独立的装载模块(Load Module)。它可以与主程序一起放到同一个装载模块库中。当有多个主程序调用同一个子程序时,大家可以共享它的程序副本。
USING短语指定要传送给目标程序的参数,CALL语句传递参数的方法有下列3种,单个CALL语句可以同时使用它们传递参数:
● BY REFERENCE短语。
BY REFERENCE是参数传递的默认值,当没有明确指定参数的传递方式时,系统默认就是BY REFERENCE。如果指定BY REFERENCE短语传递参数,则调用程序对应的数据项(参数)与被调用程序使用同一块内存区域。变量ParamIdentifier可以是DATA DIVISION的WORKING STORAGE SECTION中任何层级的数据项,但不能是函数或系统日期字段。实际上,BY REFERENCE在子程序与主程序之间传递的是参数的地址,参数在主程序和子程序之间是双向流动的。
● BY CONTENT短语。
如果使用BY CONTENT短语传递参数,则被调用程序只能修改调用程序传递给它的值,但不能修改调用程序中对应的参数字段。实际上,BY CONTENT传送给子程序的只是参数的副本,数据的流动是单向的,即只能由主程序传递到子程序,反之则不可以。
● BY VALUE短语。
BY VALUE短语适用于跟在它后面的所有参数,除非使用BY REFERENCE或BY CONTENT短语覆盖。如果使用BY VALUE短语传送参数,传送的就是参数的值而不是数据项的地址。被调用程序可以修改BY VALUE变量对应的实际参数(Actual Parameter),但这种修改不会影响主程序中形式参数(Formal Parameter)的值,因为子程序存取的是主程序所传递数据项的临时副本。BY VALUE主要目的是用来在COBOL程序与非COBOL比如C语言程序之间传递数据,当然,COBOL与COBOL程序之间的调用也可以使用。这时,BY VALUE必须同时在主程序CALL USING短语的参数和主程序的PROCEDURE DIVISION USING短语对应的形式参数的前面指定。
数据部的链接节(Linkage Section)描述其他程序占住的内存空间。像WORKING STORAGE SECTION一样,链接节也可以有下面两种格式的数据项。
● 记录格式描述项。
● 数据项目描述项。
链接节中的记录格式描述或数据项描述只是描述数据的名字,但不占住系统内存空间,因为它们对应的数据项是在程序以外的地方的。链接节中的数据项可以使用任何描述数据的短语,但不能使用VALUE短语为数据项赋值。
用户的参数除了可以通过主程序的CALL语句来传送之外,也可以通过JCL的EXC语句的PARM短语来传送,在后面我们会在演示报表中给出例子加以说明。
7.1.2 子程序
子程序(或被调用程序)像其他COBOL程序一样,也包含COBOL程序必须的4个部。此外,在其数据部额外需要一个LINKAGE SECTION,用来接收来自主程序或返回给主程序(调用程序)的信息。同一个程序可以调用几个子程序,子程序本身又可以调用其他的子程序。
图7.1描述了主程序和子程序调用的骨干代码。主程序在过程部的某处有CALL语句,当执行CALL时,控制转移到子程序过程部的第1条可执行语句,子程序继续执行直到碰到EXIT PROGRAM语句。在这一点,控制返回到主程序紧跟在初始CALL语句之后的可执行语句。EXIT PROGRAM必须自己在一个独立的段中。
数据是通过USING短语在主程序和子程序之间传送的,它出现在主程序的CALL语句及子程序的过程部头中,作为参数传送的数据名定义在子程序的LINKAGE SECTION中。主程序的任何CALL语句都要有USING短语,用来传送子程序要操作的数据,例如,CALL ‘MYSUB1’USING WS-PARMA,WS-PARMB。子程序在其过程头中本身又包含USING短语,例如,PROCEDURE DIVISION USING WS-PARMC,WS-PARMD。
在主程序和子程序的USING短语中的数据名可以是不同的,但这些短语中数据名的顺序是严格的。主程序USING短语中的第1个项目WS-PARMA,对应于子程序USING短语中的第1个项目WS-PARMC,它们都定义为3位的数字字段。类似地,WS-PARMB对应于子程序的WS-PARMD。请注意,在使用CALL BY REFERENCE方法时,无论哪个程序修改被传送参数的值,另一个程序中的值也随之修改,因为系统为两个数据名只分配了单个存储单元,这一情形也说明在下图的上部中。
从图7.1的上部可以看出,主程序中的WS-PARMA和子程序中的WS-PARMC访问内存中的同一个地址。类似地,WS-PARMB和WS-PARMD也访问同一区域。COBOL编译给WS-PARMA和WS-PARMC或WS-PARMB和WS-PARMD分配相同的空间。
子程序的参数WS-PARMC和WS-PARMD,是定义在子程序的LINKAGE SECTION中的。换句话说,任何出现在LINKAGE SECTION中的数据名已经通过调用它的程序分配了空间。因此,WS-PARMC和WS-PARMD的空间是分别在主程序中预先分配给WS-PARMA和WS-PARMB的。两个程序中的USING短语建立数据名之间的对应关系。
所有传递的参数必须定义为01或77级项目,这些参数出现的顺序是严格的。按错误的顺序传递参数,在由几个程序组成的模块系统中是最常见的问题。
图7.1 主程序和子程序调用的骨干代码
7.1.3 主程序例子1(MAIN1)
我们依然使用程序例子的方法来介绍主程序与子程序之间的调用,以及各种参数传递方式的异同点。下面是一个主程序的例子,它会使用主机上COBOL提供的3种方法调用子程序MYSUB1。主程序要传递的参数定义在程序的第15行和第16行,分别定义为3位的数字和20字节的字符串。第17行和第18行定义了两个指针,这是我们后面要演示的如何使用CALL BY VALUE达到CALL BY REFERCE效果的关键所在。
程序的第23行到第33行演示的是CALL BY REFERENCE的例子,正如我们前面介绍的,子程序既能修改所传递的形式参数,也能修改实际参数,因为主程序和子程序的参数使用的是同一块内存地址。这样,我们就能在报表的第2行到第7行看到主程序和子程序之间完全一致互动的结果了。
程序的第35行到第45行演示的是CALL BY CONTENT的例子。主程序使用这种参数传递方法,将形式参数的内容(即实际参数)传送给子程序,而实际参数和形式参数使用的是不同的内存地址,这样,子程序所能修改的只是实际参数的内容,这样的修改是返回不到主程序的。报表的第8 行到第13 行说明了这一点。
程序的第47行到第59行演示的是CALL BY VALUE的例子,正常来讲,它跟CALL BY CONTENT具有相同的功能,甚至很多人直接将它们等价起来,在有的小型机的COBOL版本中根本就没有CALL BY VALUE这种参数传递方式。但在主机上,COBOL仍然提供了这种传递方法,说明有它的存在价值。
第49行和第50行的两条SET语句用来将我们的参数WS-PARMA和WS-PARMB的地址赋值给两个指针变量WS-POINTER-A和WS-POINTER-B中,并在第54行的CALL语句中作为参数传递给子程序,这样的结果是,形式参数与实际参数为“同一地址”的存储空间,实际上主程序在调用时是将实际参数的地址交给子程序的形式参数保存,在子程序执行时即可根据形式参数找到资料所在的位置(即主程序的实际参数)而直接取用,修改该资料,从而使得CALL BY VALUE达到CALL BY REFERENCE的效果,但CALL BY VALUE直接指向数据而CALL BY REFERENCE则是指向数据的地址,这就是为什么CALL BY VALUE比CALL BY REFERENCE更快的原因。报表的第14行到第19行显示了正确的结果。
下面是主程序MAIN1的完整代码,供你参考。
000001 IDENTIFICATION DIVISION. 000002 PROGRAM-ID. MAIN1. 000003 AUTHOR. NEWMAN LV. 000004 ENVIRONMENT DIVISION. 000005 * 000006 INPUT-OUTPUT SECTION. 000007 * 000008 DATA DIVISION. 000009 WORKING-STORAGE SECTION. 000010 01 RPT-HEADING. 000011 03 FILLER PIC X(20) VALUE SPACES. 000012 03 RPT-HEAD-MOVE PIC X(25) VALUE '*MAGIC REPORT(CALL)*'. 000013 03 FILLER PIC X(27) VALUE SPACES. 000014 01 WS-TEST-DATA. 000015 05 WS-PARMA PIC 9(03) VALUE ZEROS. 000016 05 WS-PARMB PIC X(20) VALUE SPACES. 000017 77 WS-POINTER-A USAGE IS POINTER. 000018 77 WS-POINTER-B USAGE IS POINTER. 000019 000020 PROCEDURE DIVISION. 000021 DISPLAY RPT-HEADING 000022 * PASS PARAMETER BY REFERENCE 000023 MOVE 123 TO WS-PARMA 000024 MOVE ALL '*' TO WS-PARMB 000025 DISPLAY 'IN MAIN1' ',PARMA IS:' WS-PARMA ',PARMB IS:' 000026- WS-PARMB 000027 DISPLAY 'IN MAIN1,START CALLING...' 000028 CALL 'MYSUB1' USING BY REFERENCE WS-PARMA, WS-PARMB 000029 DISPLAY 'BACK MAIN1 AFTER CALL MY SUB1' 000030 DISPLAY 'IN MAIN1' ',PARMA IS:' WS-PARMA ',PARMB IS:' 000031- WS-PARMB 000032 DISPLAY '*---- END OF CALL BY REFERENCE ----*' 000033 . 000034 * PASS PARAMETERBY BY CONTENT 000035 MOVE 123 TO WS-PARMA 000036 MOVE ALL '*' TO WS-PARMB 000037 DISPLAY 'IN MAIN1' ',PARMA IS:' WS-PARMA ',PARMB IS:' 000038- WS-PARMB 000039 DISPLAY 'IN MAIN1,START CALLING...' 000040 CALL 'MYSUB1' USING BY CONTENT WS-PARMA, WS-PARMB 000041 DISPLAY 'BACK MAIN1 AFTER CALL MY SUB1' 000042 DISPLAY 'IN MAIN1' ',PARMA IS:' WS-PARMA ',PARMB IS:' 000043- WS-PARMB 000044 DISPLAY '*---- END OF CALL BY CONTENT ----*' 000045 . 000046 * PASS PARAMETER BY VALUE 000047 MOVE 123 TO WS-PARMA 000048 MOVE ALL '*' TO WS-PARMB 000049 SET WS-POINTER-A TO ADDRESS OF WS-PARMA 000050 SET WS-POINTER-B TO ADDRESS OF WS-PARMB 000051 DISPLAY 'IN MAIN1' ',PARMA IS:' WS-PARMA ',PARMB IS:' 000052- WS-PARMB 000053 DISPLAY 'IN MAIN1,START CALLING...' 000054 CALL 'MYSUB1' USING BY VALUE WS-POINTER-A,WS-POINTER-B 000055 DISPLAY 'BACK MAIN1 AFTER CALL MY SUB1' 000056 DISPLAY 'IN MAIN1' ',PARMA IS:' WS-PARMA ',PARMB IS:' 000057- WS-PARMB 000058 DISPLAY '*---- END OF CALL BY VALUE ------*' 000059 . 000060 STOP RUN.
7.1.4 子程序例子1(MYSUB1)
下面是子程序MYSUB1的完整代码,请注意第18行的PROCEDURE DIVISION头上的USING短语及定义在第14行到第17行的LINKAGE SECTION中的参数变量,它们的类型和顺序必须与调用程序一致。第27行的EXIT PROGRAM语句必须写在一个单独的程序段中,它用来表明子程序就要返回到主程序中去了。
000001 ****************************************************************** 000002 * * 000003 * C A L L E D S U B P R O G R A M * 000004 * * 000005 ****************************************************************** 000006 IDENTIFICATION DIVISION. 000007 PROGRAM-ID. MYSUB1. 000008 AUTHOR. NEWMAN LV. 000009 ENVIRONMENT DIVISION. 000010 * 000011 INPUT-OUTPUT SECTION. 000012 * 000013 DATA DIVISION. 000014 LINKAGE SECTION. 000015 77 WS-PARMC PIC 9(03). 000016 77 WS-PARMD PIC X(20). 000017 * 000018 PROCEDURE DIVISION USING WS-PARMC,WS-PARMD. 000019 MOVE 999 TO WS-PARMC 000020 MOVE 'FROM MYSUB1 ...' TO WS-PARMD 000021 . 000022 DISPLAY 'IN MYSUB1 ...' 'WS-PARMC IS:' WS-PARMC 000023 - 'WS-PARMD IS:' WS-PARMD 000024 . 000025 * 000026 EXIT-PARAGRAPH. 000027 EXIT PROGRAM.
7.1.5 程序运行结果(MAIN1)
下面是程序运行的结果,对照程序来看,效果会更好。
000001 *MAGIC REPORT(CALL)* 000002 IN MAIN1,PARMA IS:123,PARMB IS:******************** 000003 IN MAIN1,START CALLING... 000004 IN MYSUB1 ...WS-PARMC IS:099WS-PARMD IS:FROM MYSUB1 ... 000005 BACK MAIN1 AFTER CALL MY SUB1 000006 IN MAIN1,PARMA IS:099,PARMB IS:FROM MYSUB1 ... 000007 *---- END OF CALL BY REFERENCE ----* 000008 IN MAIN1,PARMA IS:123,PARMB IS:******************** 000009 IN MAIN1,START CALLING... 000010 IN MYSUB1 ...WS-PARMC IS:099WS-PARMD IS:FROM MYSUB1 ... 000011 BACK MAIN1 AFTER CALL MY SUB1 000012 IN MAIN1,PARMA IS:123,PARMB IS:******************** 000013 *---- END OF CALL BY CONTENT ----* 000014 IN MAIN1,PARMA IS:123,PARMB IS:******************** 000015 IN MAIN1,START CALLING... 000016 IN MYSUB1 ...WS-PARMC IS:099WS-PARMD IS:FROM MYSUB1 ... 000017 BACK MAIN1 AFTER CALL MY SUB1 000018 IN MAIN1,PARMA IS:099,PARMB IS:FROM MYSUB1 ... 000019 *---- END OF CALL BY VALUE ------*
7.1.6 主程序例子2(MAIN2)
下面是程序MAIN1 修改后的版本,其主要的不同点是:在程序的第20 行定义了一个新的变量WS-PGM-ID,其值为我们的子程序名字MYSUB1,这里我们要演示的是如何通过在CALL语句中使用变量的方法来达到动态调用的结果。
子程序要能够既按名字调用(如MAIN1那样),又能够按变量名(如现在介绍的MAIN2)那样调用,必须在子程序编译时指定DYNAM,RESIDENT编译选项。这样,你就既能使用CALL Literal,也可以用CALL Identifier方法调用它们。当编译时指定DYNAM选项时,无论是CALL Literal或CALL Identifier都是使用动态调用(Dynamic CALL)的方法。当编译时指定NODYNAM参数时,CALL Literal就是静态调用(Static CALL),而CALL Identifier则总是动态调用的,不管你编译时指定的是DYNAM或NODYNAM选项。
从图7.2所示的装载模块的大小来看,它们有一点是不同的,其中是CALL Identifier的方法程序的装载模块要稍微小一些。从下面的运行结果来看,两种调用方法产生的效果是完全一样的,所以这里就不做进一步的讲解了。
图7.2 子程序装载模块的属性
下面是改版后的主程序,供你参考。
000001 IDENTIFICATION DIVISION. 000002 PROGRAM-ID. MAIN2. 000003 AUTHOR. NEWMAN LV. 000004 ENVIRONMENT DIVISION. 000005 * 000006 INPUT-OUTPUT SECTION. 000007 * 000008 DATA DIVISION. 000009 WORKING-STORAGE SECTION. 000010 01 RPT-HEADING. 000011 03 FILLER PIC X(20) VALUE SPACES. 000012 03 RPT-HEAD-MOVE PIC X(25) VALUE '*MAGIC REPORT(CALL)*'. 000013 03 FILLER PIC X(27) VALUE SPACES. 000014 01 WS-TEST-DATA. 000015 05 WS-PARMA PPIC IC 9(03) VALUE ZEROS. 000016 05 WS-PARMB PPIC IC X(20) VALUE SPACES. 000017 77 WS-POINTER-A USAGE PIC IS POINTER. 000018 77 WS-POINTER-B USAGE IS POINTER. 000019 * PIC 000020 77 WS-PGM-ID PPIC IC X(08) VALUE 'MYSUB1'. 000021 E IS 000022 PROCEDURE DIVISION. 000023 DISPLAY RPT-HEADING 000024 * PASS PARAMETER BY REFERENCE 000025 MOVE 123 TO WS-PARMA 000026 MOVE ALL '*' TO WS-PARMB 000027 DISPLAY 'IN MAIN2' ',PARMA IS:' WS-PARMA ',PARMB IS:' 000028- WS-PARMB 000029 DISPLAY 'IN MAIN2,START CALLING...' 000030 CALL WS-PGM-ID USING BY REFERENCE WS-PARMA, WS-PARMB 000031 DISPLAY 'BACK MAIN2 AFTER CALL MYSUB1' 000032 DISPLAY 'IN MAIN2' ',PARMA IS:' WS-PARMA ',PARMB IS:' 000033- WS-PARMB 000034 DISPLAY '*---- END OF CALL BY REFERENCE ----*' 000035 . 000036 * PASS PARAMETERBY BY CONTENT 000037 MOVE 123 TO WS-PARMA 000038 MOVE ALL '*' TO WS-PARMB 000039 DISPLAY 'IN MAIN2' ',PARMA IS:' WS-PARMA ',PARMB IS:' 000040- WS-PARMB 000041 DISPLAY 'IN MAIN2,START CALLING...' 000042 CALL WS-PGM-ID USING BY CONTENT WS-PARMA, WS-PARMB 000043 DISPLAY 'BACK MAIN2 AFTER CALL MYSUB1' 000044 DISPLAY 'IN MAIN2' ',PARMA IS:' WS-PARMA ',PARMB IS:' 000045- WS-PARMB 000046 DISPLAY '*---- END OF CALL BY CONTENT ----*' 000047 . 000048 * PASS PARAMETER BY VALUE 000049 MOVE 123 TO WS-PARMA 000050 MOVE ALL '*' TO WS-PARMB 000051 SET WS-POINTER-A TO ADDRESS OF WS-PARMA 000052 SET WS-POINTER-B TO ADDRESS OF WS-PARMB 000053 DISPLAY 'IN MAIN2' ',PARMA IS:' WS-PARMA ',PARMB IS:' 000054- WS-PARMB 000055 DISPLAY 'IN MAIN2,START CALLING...' 000056 CALL WS-PGM-ID USING BY VALUE WS-POINTER-A,WS-POINTER-B 000057 DISPLAY 'BACK MAIN2 AFTER CALL MYSUB1' 000058 DISPLAY 'IN MAIN2' ',PARMA IS:' WS-PARMA ',PARMB IS:' 000059- WS-PARMB 000060 DISPLAY '*---- END OF CALL BY VALUE ------*' 000061 . 000062 STOP RUN.
7.1.7 程序运行结果(MAIN2)
下面显示的是使用CALL Identifier方法的主程序MAIN2的完整代码,供你参考。
000001 *MAGIC REPORT(CALL)* 000002 IN MAIN2,PARMA IS:123,PARMB IS:******************** 000003 IN MAIN2,START CALLING... 000004 IN MYSUB1 ...WS-PARMC IS:999WS-PARMD IS:FROM MYSUB1 ... 000005 BACK MAIN2 AFTER CALL MYSUB1 000006 IN MAIN2,PARMA IS:999,PARMB IS:FROM MYSUB1 ... 000007 *---- END OF CALL BY REFERENCE ----* 000008 IN MAIN2,PARMA IS:123,PARMB IS:******************** 000009 IN MAIN2,START CALLING... 000010 IN MYSUB1 ...WS-PARMC IS:999WS-PARMD IS:FROM MYSUB1 ... 000011 BACK MAIN2 AFTER CALL MYSUB1 000012 IN MAIN2,PARMA IS:123,PARMB IS:******************** 000013 *---- END OF CALL BY CONTENT ----* 000014 IN MAIN2,PARMA IS:123,PARMB IS:******************** 000015 IN MAIN2,START CALLING... 000016 IN MYSUB1 ...WS-PARMC IS:999WS-PARMD IS:FROM MYSUB1 ... 000017 BACK MAIN2 AFTER CALL MYSUB1 000018 IN MAIN2,PARMA IS:999,PARMB IS:FROM MYSUB1 ... 000019 *---- END OF CALL BY VALUE ------*
7.1.8 子程序例子2(MYSUB2)
下面的子程序是根据MYSUB1修改的,目的是介绍如何通过JCL传递参数到子程序中。如果你比较细心,就会发现MYSUB2跟MYSUB1有如下几个明显不同的地方。
● 第14 行到第19 行的LINKAGE SECTION与原来不同了,原因是COBOL要求在子程序PROCEDURE DIVISION中的USING短语中使用的参数必须是用01或77级定义的。此外,当从JCL EXEC的PARM选项中向程序传递参数时,必须在参数的前面有一个长度字段,这也是为什么程序的第16行出现WS-LENG变量的原因。
● 第26行的EXIT PROGRAM语句变成了GOBACK语句,因为EXIT PROGRAM语句只适用于主程序CALL子程序时才能用,这里,由于我们是直接使用JCL调用子程序,所以必须改成GOBACK程序才能正确结束。
000001 ****************************************************************** 000002 * * 000003 * C A L L E D S U B P R O G R A M * 000004 * * 000005 ****************************************************************** 000006 IDENTIFICATION DIVISION. 000007 PROGRAM-ID. MYSUB2. 000008 AUTHOR. NEWMAN LV. 000009 ENVIRONMENT DIVISION. 000010 * 000011 INPUT-OUTPUT SECTION. 000012 * 000013 DATA DIVISION. 000014 LINKAGE SECTION. 000015 01 LINK-PARMS. 000016 03 WS-LENG PIC S9(02) COMP. 000017 03 WS-PARMC PIC 9(03). 000018 03 WS-PARMD PIC X(20). 000019 * 000020 PROCEDURE DIVISION USING LINK-PARMS. 000021 DISPLAY 'IN MYSUB2 ...' 'WS-PARMC IS:' WS-PARMC 000022 - 'WS-PARMD IS:' WS-PARMD 000023 . 000024 * 000025 EXIT-PARAGRAPH. 000026 GOBACK.
7.1.9 JCL调用例子(MYSUB2)
下面是调用子程序MYSUB2的JCL,主要的看点是第8行的PARM短语,它将等号(=)后面的参数传递给程序MYSUB2作为参数,其中,前面的3个数字123对应于MYSUB2中的参数WS-PARMC,而紧跟在后面的一串星号(*)则对应于参数WS-PARMD。从后面的结果报表来看,参数的传递是准确无误的。
000001 //IBMUSERG JOB ACCT#, 000002 // IBMUSER, **JOB STATEMENT GENERA 000003 // NOTIFY=IBMUSER, 000004 // MSGLEVEL=(1,1) 000005 //*----------------------------------------------------------------* 000006 //* CALL SUBPROGRAM MYSUB2 AND PASS PARAMETER TO MYSUB2 VIA JCL * 000007 //*----------------------------------------------------------------* 000008 //STEP1 EXEC PGM=MYSUB2,PARM='123********************' 000009 //STEPLIB DD DSN=IBMUSER.TEST.LOAD,DISP=SHR 000010 //SYSPRINT DD SYSOUT=(*) 000011 //
7.1.10 程序运行结果(MYSUB2)
下面是通过JCL传递参数后程序运行后的结果,说明程序正确地接收到了JCL传递过来的参数:
IN MYSUB2…WS-PARMC IS:123WS-PARMD IS:********************