我々はポインターは 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 を修正し忘れると、たちまちエラーとなってしまう。
そのようなエラーを防ぐ方法を紹介しよう。
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 であればパラメータ SHCODE
と HNSCOD
の両方を受け取ったことを示す。
テストとしてこのプログラムを
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などによって引き起こされる場合が多い。