RPG

233. RPGプログラマーのためのポインター講座 (4)

我々はポインターは NULL-STOP の文字列に対して定義されることと
パラメータとしてポインターを定義するときは %ADDR で、そして戻り値として受け取り変数として
利用するときは BASED によってポインターを定義することを学んだ。

ここでは、ポインターのエラーについて学習しよう。
あなたは

    MCH3601 : ポインターが参照された位置に設定されていない

というメッセージを見たことはないだろうか ?
C言語の開発をしていると、この憎たらしいメッセージは頻繁にお目にかかることになる。
しかしポインターを使っていない RPGプログラムでも、このメッセージは出る。
それは値が渡されていないパラメータを RPGプログラムが参照しようとしたときにおこる。
例えば、

	C*----------------------------------------------------+        
	C     *ENTRY        PLIST                                      
	C                   PARM                    SRCF             10
	C                   PARM                    SRCFLIB          10
	C                   PARM                    SRCMBR           10
	C*----------------------------------------------------+ 
		:
	C                  MOVEL SRCMBR             PGMNAME

という RPGプログラムの演算に対して、このRPGプログラムを呼び出す CLP が

	CALL       PGM(ASNET.COM/CVTHTM60) PARM(&SRCF &SRCFLIB)

のようにしてパラメータ : SRCMBR を渡すのをプログラマーが忘れたとしよう。
このとき

	C                  MOVEL SRCMBR             PGMNAME

の実行時に MSH3601 のポインター・エラーが発生する。
もちろん、この演算が行われることがなく、SRCMBR を RPG の中で参照されることが
ないのであればエラーになることはない。
使用しようとして初めてエラーとなるのである。

ところで SRCMBR を渡すのであれば

	渡されていないパラメータを参照しようとした

のようなメッセージを出してもいいのではないかと思われる。
実はかつての古いOSリリースでは、このようなメッセージであったのだが
OS の改訂に伴って MSH3601 に変更されたためにユーザーにとっては
何が誤りなのかを簡単に想像できなくなってしまっている。

これは i5/OS が IBM の社内西洋開発言語から C言語による開発に変更されたせいもあるが
RPG のパラメータの性質に依存している。

さて長い間、RPG の開発を行っている方は、RPG のパラメータを呼び出されたRPG の側で変更すると
呼び出し側の CLP にも、その値が戻されることはご存知だろう。
上記の例では RPG で SRCFパラメータの値を変更すると RPG の終了後、CLP で &SRCF の変数も
書き換えられて変更されるていることは周知のとおりである。

何だ、そんなことは当たり前だと思われるかも知れないが当たり前ではないのである。
一般的な開発言語ではパラメータの値が呼び出し側のプログラムに戻ることはない。
これは理論的に当然なことであって、考えてみれば呼び出し側で変数は、定義されており
呼び出される側でも同じ名前ではあるが、その変数(パラメータ)はちゃんと別の場所に
定義されているのである。
つまり呼び出し側の変数が定義されているポインターと呼び出される側での変数のポインターは
異なる、つまりシステム空間上では別の異なる場所に定義されているので
一方が更新されたからといって、別の場所にある変数が更新されることは
コンピュータの構造からいってあり得ないし、起こらないのである。

このように別の異なる番地に記憶されている変数が演算命令も実行していないのに
突然、値がコピーされることはあり得ない。
同じ名前だからという反論があったとしてもそれは論理性がない。
なぜなら異なる名前で定義したとしても RPG の場合はパラメータの値が戻されるからである。

これはRPGのパラメータ定義とは変数をパラメータとしているのではなく
変数のポインターをパラメータにしている、と推測することができるし、
この推測は正しい。
これは VisualBASIC, C, C++, VC++, Java であっても同じことである。
パラメータの値が呼び出し側に戻されるのはパラメータがポインターである場合だけである。
パラメータがポインターであれば呼び出し側も呼び出される側も同じ変数を指していることに
なるので変数値を共有することができるのである。

今まで無意識に使用していたパラメータとは実はポインターだったのだ。

恐らくIBM は RPGプログラムを初めて開発するときにパラメータは変数にするより
値を戻すことができるポインターのほうがユーザーにとってわかりやすく使いやすいと
判断したのだろう。
RPGプログラムはプログラム全体の戻り値は存在しないので、RPG からの戻り値を取得できるように
するにはパラメータの値が戻るようにしなければならない。
従ってパラメータはポインターであるとしたのは、ごく自然な成り行きである。

ところで RPG のパラメータで必ずしもすべてのパラメータを指定しなくてもよい、との
柔軟なプログラムを作成したいとする。
あるいは RPG のパラメータを追加した場合、単純にパラメータを追加しただけでは
別の CLP を修正し忘れると、たちまちエラーとなってしまう。
そのようなエラーを防ぐ方法を紹介しよう。

【 TESTPRM2: パラメータを受け取るサンプル RPG 】
0001.00 H DFTNAME(TESTPRM2) DATEDIT(*YMD/)                                      
0002.00 F********** パラメータの受取りのテスト ******************************** 
0003.00 F********************************************************************** 
0004.00 C*----------------------------------------------------+                 
0005.00 C*    パラメータの受取り                                              
0006.00 C*----------------------------------------------------+                 
0007.00 C     *ENTRY        PLIST                                               
0008.00 C                   PARM                    SHCODE           10         
0009.00 C                   PARM                    HNSCOD            4         
0010.00 C*----------------------------------------------------+                 
0011.00 C                   IF        %PARMS = 2                                
0012.00 C     'HNSCOD='     CAT(P)    HNSCOD:0      DSP40            40         
0013.00 C     DSP40         DSPLY                   ANS               1         
0014.00 C                   ELSE                                                
0015.00 C     'NO ENTRY'    DSPLY                   ANS               1         
0016.00 C                   ENDIF                                               
0017.00 C                   SETON                                        LR     
0018.00 C                   RETURN                                              
0019.00 C     END           TAG                                                 
			

%PARMS は実際に受け取ったパラメータの数を示す数値である。
%PARM が 1 であれば、このプログラムはパラメータ SHOCDE しか受け取っておらず
%PARMS の値が 2 であればパラメータ SHCODEHNSCOD の両方を受け取ったことを示す。
テストとしてこのプログラムを

CALL PGM(TEST.COM/TESTPRM2) PARM('NV-CF1') で実行すれば「NO ENTRY」と表示され、

CALL PGM(TEST.COM/TESTPRM2) PARM('NV-CF1' '0001') で実行すれば

HNSCOD=0001」として表示されるはずである。

この %PARMS は一般のRPGプログラムの *ENTRY パラメータの検査だけでなく
プロシージャーのパラメータ数を検査するのに使用することもできる。
覚えておいて便利な機能である。

さて本題に戻って MCH3601 は定義されていないパラメータを参照しようとしたときにだけ
発生するエラーではなく、本来は

  • 実体のないポインター、つまり変数が定義されていないポインターを使用しようとした場合

に起こるエラーである。

たとえば

	D DSPRCD_P        S               *

のようにポインターだけ定義しておいて この DSPRCD_P というポインターが
%ADDR によって何か実体のある変数の基底ポインターとして関連づけられていない場合も
もちろんエラーとなってしまう。

つまり無意味なポインターを使用しようとした、ということであり

ポインターとして実体のある変数に関連づけられていないか、またはポインターとしては
意味をなさないポインターが使用された。

というエラーが MCH3601 である。
たいていの場合はプログラマーがポインターを定義したものの、変数と正しく関連づけすることを
忘れていたことに起因して発生するエラーである。
また実行中にポインターが増加して無意味は場所を指し示すことになってしまう場合もある。
これは想定外のLOOPなどによって引き起こされる場合が多い。

MCH3601 :ポインターのエラーは無意味なポインターが参照されようとしたことを示す
  • 必要なパラメータを渡すのが漏れている
  • ポインターを定義したがフィールドに関連づけされていない
  • 想定外のLOOPなどによる暴走で無意味な場所を参照しようとした