モダンなCLプログラミング技法を試す パート3
このシリーズの前号「モダンな CL プログラミング技法を試す」と「モダンな CL プログラミング技法を試す パート 2」では、Retrieve Subsystem Information (QWDRSBSD) API で戻される情報にアクセスする場合に、*DEFINED ストレージと *BASED ストレージの使用方法をお話しました。実際、読者の多くはアプリケーション・セットで QWDRSBSD API を使用するとは思いませんが、過去の 2 つの記事に記載のサンプル・プログラムは、 CL プログラミング機能を強化できる技法をいくつか示す出発点となってくれました。
これらのうち最初の技法では、*DEFINED ストレージを使用して、他のフィールド内にサブフィールドを定義しています。通常この技法は、サブストリング組み込み命令の必要性をなくすことで、プログラムの文書化を改善します。2 番目の技法では、*BASED ストレージを使用して、ポインターを使用してフィールド定義をストレージに重ね合わせています。これにより、プログラムのフローが単純になり、合理化されやすくなります。
このシリーズのパート 3 では、引き続き QWDRSBSD API を使用して、これらの CL 機能拡張によりプロセスで発生する推量をどのようになくすことができるかを確認します。また、CL プログラムの開発時に遭遇する可能性がある推量の制限事項についても見てゆきます。
推量をなくす
私の言うところの推量は、特定の長さを持つある変数を宣言するときに発生します。前号のサンプル・プログラムで API 受信側変数 (&SBSI0100) の長さを 360 バイトと宣言しました。戻している基本サブシステム情報が 80 バイト、サブシステムに定義された各ストレージ・プールに戻している情報がそれぞれ 28 バイトで、最大 10 個のストレージ・プールをサブシステムに定義できると想定しています。こうした情報に基づいて上記の推量を行っていました。
10 個を超えるストレージ・プールをサブシステムに関連付けるという需要はさほどないと思われますので、この推量は最も安全であると思われます。しかし過去に、どのくらいのサイズの変数を宣言するかを判断するときに、かなり大雑把な推量をしたと思います。読者の方々も似たような状況にあったかと思います。特に、宣言された変数の長さの外で動作しない、サブストリングのような命令と組み合わせてこうした推量をする場合、アプリケーション内で制限 (最大数) がもたらされます。例えば、変数 &A を LEN(100) と宣言し、10 バイト値を &A 内に格納する場合、最大 10 個の値がサポートされているという結果になります。10 は適切な数字でしょうか。どうでしょう、あくまで推量ですから。
こうした推量をなくす方法の 1 つに、ランタイム時に変数に必要なストレージを動的に割り当て、それによってコンパイル時に適切な量のストレージを決定する必要性をなくすという方法があります。IBM i は、ストレージを割り当てる、以前に割り当てたストレージのサイズを変更する、プログラムがストレージを使用し終わった後に以前に割り当てたストレージを開放するといった場合に使用できる API をいくつか提供しています。現在のサンプル・プログラムでは、次のシステム API を使用します。これらは IBM インフォメーション・センターの指示に従って文書化されています。
- 最初にストレージを割り当てる Reserve Storage Block (malloc)
(publib.boulder.ibm.com/infocenter/iseries
/v7r1m0/topic/rtref/malloc.htm) - 以前のストレージのサイズを変更する Change Reserved Storage Block Size (realloc)
(publib.boulder.ibm.com/infocenter/iseries
/v7r1m0/topic/rtref/realloc.htm#realloc) - ストレージを割り当て解除する Release Storage Block (free)
(publib.boulder.ibm.com/infocenter/iseries
/v7r1m0/topic/rtref/free.htm#free)
プログラムの最初のバージョンでは、realloc API は使用しません。代わりに従来の受信側変数を使用します。このサイズは QWDRSBSD API が、指定のサブシステムのすべてのストレージ・プール情報にアクセスするのに、どれだけのストレージが必要かを十分示すことができるサイズです。この情報があれば、プログラムで次のことが可能になります。 - すべてのストレージ・プール情報にアクセスするために必要なストレージ量の割り当て
- 新たに割り当てられたこのストレージを受信側変数として使用して QWDRSBSD API を 2 回呼び出す
- 戻されたストレージ・プール情報の処理
- ストレージの開放
- 終了
割り当てと再割り当て
動的に割り当てられたストレージを QWDRSBSD 受信側変数専用に使用する 2 番目のプログラムを紹介します。この 2 番目のプログラムは割り当てと再割り当ての技法を示します。
最初のプログラムは図 1 (前半と後半) に示します。このプログラムとこのシリーズのパート 2 で使用したプログラムの違いは、変更部分を強調するため太字で示しています。以前のプログラムからソース行がいくつか削除されました。行われた変更の数は少ないです。それより重要な点は、変更が実際のデータ処理と関連していないということです。 IBM i 5.4 システムでプログラムを作成するには、次のコマンドを使用します。
CRTCLMOD MODULE(DSPPLSM)
CRTPGM PGM(DSPPLSM) BNDDIR(QC2LE)
5.4 システムでは、プログラムが使用する API をシステムが見つけることができるようにするため、 QC2LE バインディング・ディレクトリーを明示的に参照する必要があります。IBM i 6.1 以降、システムはこのバインディング・ディレクトリーを自動的に参照してシステム API を見つけます。したがって、次のコマンドを単独で使用できます。
CRTBNDCL PGM(DSPPLSM)
図 1 を見てください。(A)では、プログラムは最初に QWDRSBSD API を呼び出すときに使用される receiver-variable-related 変数を宣言しています。次の CL 変数が宣言されています。
- &LenPoolMin: 最初の受信側変数のサイズ
- &SBSI_Min: 8 バイト文字フィールドとして宣言された最初の受信側変数
- &SBS_BytAvl: 指定のサブシステムの API から利用できるバイト数 (以前宣言した &SBSI_Min 変数を再定義する 4 バイト整数として宣言され、&SBSI_Min の 5 バイト目から始まる)
注: &LenPoolMin 変数は 4 バイト整数として宣言され、値 8 に初期化されています。それによって受信側変数は 8 バイト長であることを示しています。これは QWDRSBSD API でサポートされている最小値で、API が受信側変数の Bytes リターン・フィールドと Bytes 使用可能フィールドを戻すことができるのに十分なサイズです。 Bytes 使用可能値は、プログラムがサブシステムに関するすべての SBSI0100 フォーマット関連情報を受信できるようにするため、受信側変数に必要なサイズを示しています。
(B) では、このシリーズの前号で使用した &SBSI0100 受信側変数に小規模な変更が行われています。&SBSI0100 を 360 バイト文字変数 (指定のサブシステムに関連付けられたすべてのストレージ・プール情報に対応してほしいと考える変数) として宣言するのではなく、プログラムは API 受信側変数の非可変部分に基づく 80 バイトの固定長文字ストリングを定義するだけです。つまり、推量する必要はないということです。このバージョンの &SBSI0100 受信側変数はまた、ポインター変数 &SBSInfPtr に基づいていると定義されています。
変数 &NullPtr の (C) における宣言は、ご使用のシステムのリリースにより任意です。 malloc API でプログラムが要求する量のストレージが割り当てられない場合、API は NULL ポインターを返します。malloc API を呼び出した後、サンプル・プログラムはリターン・ポインターと NULL ポインターを比較することで、NULL ポインターが戻されたかどうか判断します。5.4 システムでは、任意のアドレスに初期化されていないポインターを宣言することで、NULL ポインターを定義できます。つまり、DCL コマンドの ADDRESS パラメーターは使用されていません。6.1 OS では、NULL ポインター値を表す特殊 CL 値 *NULL が導入されました。したがって、6.1 以降のバージョンを実行している場合、&NullPtr at (C) の宣言を削除し、次の例のようにプログラムにおける &NullPtr へのすべての参照を特殊値 *NULL と置き換えることができます。
If Cond(&SBSInfPtr = *NULL) Then(Do)
(D) では、QWDRSBSD API を 2 回呼び出すようプログラムが変更されています。既に宣言された受信側変数 &SBSI_Min を渡す最初の呼び出しは、指定のサブシステム (&Sbs) について API から &SBS_BytAvl で定義された使用可能バイト数にアクセスするためだけに行われています。この呼び出しに続いて、&SBS_BytAvl バイトのストレージを割り当てるよう malloc API が呼び出されています。API で戻されたポインター値は、この割り当て済みストレージの開始アドレスを示し、&SBSInfPtr 変数に割り当てられています。
割り当て失敗
malloc が呼び出された後 &SBSInfPtr の値が NULL の場合、これは割り当て要求が失敗したことを示します。この場合、エラー・メッセージが送信され、プログラムが終了します。&SBSInfPtr が NULL でない場合、2 回目の QWDRSBSD API が呼び出されます。今回は、基底付き受信側変数 &SBSI0100 を使用し、&SBSI0100 受信側変数のサイズを &SBS_BytAvl と示して呼び出されます。このサイズ (SBS_&BytAvl) は正しいです。&SBSI0100 は malloc により割り当てられたストレージの開始アドレスに設定されている &SBSInfPtr ポインターに基づいているためです。これで QWDRSBSD API はすべての SBSI0100 フォーマット関連情報をプログラムに返すことができます。通常は、すべての情報を返すことができます。この記事の後半でこの規則の例外をお話します。
(E) では、ポインター &PoolInfPtr は、戻された受信側変数の開始アドレスであるポインター変数 &SBSInfPtr の値に設定されています。次に、ポインターを最初に戻されたストレージ・プール・エントリーの開始に設定するため、&PoolInfPtr へ 80 バイトが追加されます。最後に、プログラムは DOFOR グループに入り、&SBS_Pools 変数で示すストレージ・プール数を処理します。
すべてのストレージ・プールを処理した後、図 1 の (F)に示すように、フリー API を呼び出して &SBSI0100 受信側変数に割り当てられているストレージが開放されます。既に割り当てられているストレージを開放 (つまり割り当て解除) するには、ストレージの先頭バイトをアドレス指定するポインターを開放する必要があります。このため、プログラムはポインター変数 &SBSInfPtr を API に渡します。フリー API は、そのストレージ位置にある直近で割り当てられた、または再割り当てされた同じ数のバイト数を開放します。
フリー API を呼び出してストレージを割り当て解除するのを忘れても、この世の終わりではありません。ただし、プログラムが終了した後でもそのストレージは割り当てられたままになります。可能性として、さらにジョブが終了するまで割り当てられた状態になります。概念的には、プログラムが Allocate Object (ALCOBJ) コマンドを使用したところ、オブジェクトを使用し終わった後に Deallocate Object (DLCOBJ) コマンドを使用できなかったのと違いはありません。そのような場合、プログラムの終了後、また可能性としてジョブの終了までオブジェクトも割り当てられたままになります。
図 2 (前半と後半) のプログラムは最初のプログラムと似ていますが、CL 変数のデフォルト・ストレージである自動ストレージを使用することで従来の 8 バイト受信側変数 (図 1 の &SBSI_Min) を宣言していません。そうではなく、図 2 のプログラムは malloc API を使用して 8 バイト受信側変数を割り当てることで開始し、realloc API を使用して、QWDRSBSD API により戻された Bytes 使用可能フィールドに基づいて受信側変数に関連付けられたストレージを再割り当てします。図 2 で強調表示された部分は図 1 から変更された点です。プログラムを作成するには、図 1 のプログラムで以前使用された同じコマンドを使用します。
図 1 の初期の受信側変数 (&SBSI_Min) が使用されることはありませんが、依然としてプログラムは何バイトの情報が QWDRSBSD API から使用できるか認識する必要があります。したがって、小規模な変更を(A) に示します。この変更では &SBSI_Min の &SBS_BytAvl サブフィールドを &SBSI0100 に移動し、&SBS_BytAvl の DEFVAR 属性を &SBSI_Min から &SBSI0100 へ変更する必要があります。
(B) では、図 1 で行ったようにすぐに QWDRSBSD API を呼び出す代わりに、malloc API を呼び出して 8 バイトのストレージを割り当てることでプログラムが開始します。このストレージの開始アドレスは &SBSInfPtr 変数で戻されます。ストレージが問題なく割り当てられたことが確認されます。
malloc が成功すると、&SBSI0100 受信側変数を使用して QWDRSBSD API が呼び出されます。この変数は (B) で割り当てられた 8 バイトのストレージをポイントする &SBSInfPtr ポインター変数に基づいています。この呼び出しを行うと、「受信側変数の長さ」パラメーターが 8 (&LenPoolMin) に設定されます。これらの処理により API は SBSI0100 フォーマットの先頭 8 バイトを返します。このフォーマットには、指定のサブシステム記述 (&SBS_BytAvl) のストレージ・プール情報を受信するのに必要な (5 バイト目から始まる) ストレージが含まれています。
(C) では、realloc API が呼び出され、&SBSInfPtr ポインターと関連付けられているストレージを 8 バイトから新しいサイズである &SBS_BytAvl へ再割り当てするよう要求しています。realloc API は現在の割り当てサイズを保持します。プログラムが値 8 をパラメーターとして渡す必要がないのはこのためです。この再割り当てストレージの開始アドレスは &SBS_InfPtr 変数に再度格納されます。
プログラムの残り部分は図 1 と同じです。しかし、図 2 のプログラムについてはいくつかポイントを明確にする必要があります。
ストレージ割り当ての柔軟性
図 2 (前半と後半) で、&SBSI0100 変数の宣言により QWDRSBSD API で戻すことができる 80 全バイトの固定情報が定義されていても、malloc API で割り当てられているストレージはわずか 8 バイト (&LenPoolMin) である点に注目してください。変数の宣言で定義されたストレージ量より少ない量を割り当てるのは問題ありません。さらに言えば、より多くのストレージを定義しても問題ありません。これは事実上、プログラム後半で realloc API に行われる呼び出しの結果発生する内容です。プログラムが割り当てられた長さを超えて &SBSI0100 変数内の情報を使用しようとしない限り、要求された量と異なる量のストレージを割り当てることはできます。QWDRSBSD API が呼び出されると、&LenPoolMin が第 2 パラメーター (受信側変数の長さ) として渡されます。したがって、API は割り当て済みの 8 バイトを超えて書き込みません。QWDRSBSD を呼び出した後にメモリーを再割り当てする場合、図 2 のプログラムは &SBSI0100 内でのみ &SBS_BytAvl 変数を使用します。これは &SBSI0100 の先頭 8 バイト内で定義されています。割り当て済みの 8 バイトを超えてうっかりストレージを参照した場合、予期しない結果になる、つまり悪い結果になることが多いと安全に予測できます。つまり、たとえ予測できなくても、プログラムが予期しない動作をする、つまりエラーになる可能性が高いと予想できます。プログラムは、本質的にフォーマットが不正であると認識していないランダム・データを処理することになります。
この状況は、ある CL プログラム (ProgramA) が別の CL プログラム (ProgramB) を呼び出し、パラメーター (Parm1) を渡すものの、ProgramA がそのパラメーターを TYPE(*CHAR) LEN(8)、ProgramB がそのパラメーターを TYPE(*CHAR) LEN(80) と宣言した場合の結果と違いはありません。ProgramB が Parm1 の位置 9 から 80 のデータを検査すると、どのような値が検出されるかは誰にも分かりません。ProgramB が Parm1 の位置 9 から 80 のデータを変更すると、将来的にある時点で、ジョブ内で障害が発生する可能性があります。障害は呼び出し側プログラムである ProgramA、またはジョブ内でアクティブな他の任意のジョブの場合があります。
こうした障害を被ることを心配したくない場合、malloc API を使用して最初にストレージを割り当てる場合、また再度 QWDRSBSD API を最初に呼び出す場合、80 バイト (基底付き &SBSI0100 変数の定義長) の割り当てを要求するようプログラムを変更できます。こうすることで、プログラムで実際に必要とされているより 72 バイトを超えるストレージがプログラムで単純に割り当てられ、QWDRSBSD API はプログラムが現在使用しているより 72 バイトを超えるサブシステム情報を返します。
図 1 のプログラムについて前述の考察で、QWDRSBSD API への 2 番目の呼び出しは、必ずしも常に、というわけではありませんが、通常はサブシステムに関連付けられたすべてのストレージ・プール情報を返すとお話しました。図 2 のプログラムにも同じことが当てはまります。この理由は、QWDRSBSD API への最初の呼び出しと 2 番目の呼び出しの間には時間枠が存在するためです。これは、プログラムが必要なストレージを割り当てる時間です。短いかもしれませんがこの時間枠の中で、他人がサブシステムに別のストレージ・プールを追加できる可能性があります。したがって、&SBS_Poolsについては値 5 (サブシステムにストレージ・プールが 5 つ定義されていることを示す) および関連する &SBS_BytAvl 値 (5 つのストレージ・プールの情報を保持するのに必要なバイト数を示す) で API への最初の呼び出しが戻る場合がありますが、API への 2 番目の呼び出しは &SBS_Pools については値 6、&SBS_BytAvl についてはそれに応じてより大きな値で戻る場合があります。
現在プログラムは、プログラムがストレージを再割り当てしたときに有効だった &SBS_Pools 値ではなく、最後に取得した &SBS_Pools 値に基づくサブシステム・ストレージ・プール情報を表示しているため、API が 5 つのプールのみの情報を返しても 6 つのストレージ・プールを処理しようとします。QWDRSBSD API が 2 回目に呼び出されたとき、「受信側変数の長さ」パラメーターが 5 つのプールに対応できるだけのストレージしか指定しなかったため、5 つのプールの情報のみ戻されました。したがってプログラムにはすべてのサブシステム情報があるわけではありません。プログラムが現在コーディングされている方法を考えると、API がこの情報をあたかも提供したかのように、6 番目のストレージ・プールがうっかり処理されてしまいます。
DOUNTIL グループ
現在使用できるすべての情報を処理することが重要ならば、図 3 に示すような単純なチェックを行うことができます。図 2 からの変更点は太字で示しています。プログラムがサブシステムを分析しているのと同じタイミングで、サブシステムを変更させてしまうような時間枠があるためにこの問題が発生している点を思い出してください。そして通常これは、ほとんどのアプリケーション・プログラムでは問題になりません。
図 3 の (A) で、&SBSI0100 の &SBS_BytRtn フィールドに DCL が追加されています。この変数は QWDRSBSD API により戻された情報のバイト数を表します。
図 3 の (B) で、QWDRSBSD API (&SBS_BytAvl) から使用できるバイト数が API (&SBS_BytRtn) で戻されるバイト数以下かどうかを判断する DOUNTIL チェックが導入されています。&SBS_BytAvl 値が &SBS_BytRtn 値を超える場合、他人がサブシステム・プールを追加したことを意味し、プログラムは DOUNTIL グループに残ります。&SBS_BytAvl 値が &SBS_BytRtn 値以下になるまで、プログラムは引き続きストレージを再割り当てし、QWDRSBSD を呼び出します。この時点で、使用できるすべてのストレージ・プール情報は戻されることになり、同じ受信側変数で同時に使用できます。プログラムは DOUNTIL を終了し、現在の受信側変数でストレージ・プール情報を処理します。これら小規模な変更を行った後、システムへのそれ以降の変更を動的に変更できます。
DOUNTIL グループは、OS の最近のリリースで CL に行われた別の機能拡張を表します。DOUNTIL グループでは、複数の CL コマンドが、グループが繰り返されるごとにテストされる論理条件 (この場合は &SBS_BytAvl <= &SBS_BytRtn) に基づいて複数回実行されます。この動作は DOWHILE グループの動作と対照をなしています。このグループでは、CL コマンドの関連グループが、グループの反復ごとの前にテストされる論理条件に基づいて、ゼロ回または複数回実行されます。realloc API には最低でも 1 回の呼び出しが必要なため、プログラムが &SBS_BytAvl の初期値を取得した後に DOUNTIL を使用するのは極めて適切です。
realloc API には、プログラムでストレージ・ブロックの割り当てサイズを簡単に変更できる点に加えて、別の機能があります。必要な場合、realloc はまた以前のストレージ位置から新しい位置へデータをコピーします。例えば、在庫部品番号のリストを構築するプログラムで、現在 10 個の部品番号が割り当てに含まれているとします。このシナリオで、ユーザーが 11 番目の部品番号を追加しようとしています。プログラムはユーザーにエラー・メッセージを表示するのではなく、realloc API を呼び出して 20 個分の部品番号のスペースを再割り当てできます。realloc で戻されるポインターは、20 個の部品番号を保持できるストレージ位置をポイントし、元の 10 個の部品番号はこのストレージ位置で既に使用できます。realloc で実際に戻されるポインター値は、元のポインター値と同じでない場合があります。この値は、realloc が追加の 10 個の部品番号について元の 10 個の部品番号に連続スペースを見つけることができたかどうかによって異なります。
realloc を使用する場合、同じポインターを使用して以前に再割り当てされたストレージ・ブロックのフリー API を呼び出す必要はありません。realloc API は再割り当てされたストレージを暗黙的に開放します。例えば、図 3 のプログラムは malloc API により割り当てられているストレージを開放するため、フリー API を呼び出す必要はありません。realloc API に malloc で戻されるポインターが渡された後、必要に応じてこのストレージが開放されます。例えば、部品番号に関する前述の考察を考えてください。realloc API は、20 個分の部品番号のスペースを確保するために、データを新しい位置にコピーしなければならない場合、元の部品番号に関連付けられているストレージを開放します。realloc に新しいストレージ・サイズ 0 が渡されると、NULL に設定されるポインター値を返します。これは、プログラムで使用する必要がなくなった場合にポインター値をクリアまたはリセットする 1 つの方法です。
変数を再定義する
すべての情報にアクセスする点に関する前述の言及について、現在使用できるすべての情報にアクセスすることが重要でない場合、図 4 に示すような単純な変更を行うことができる点を覚えておいてください。図 2 からの変更点は太字で示しています。
図 4 の (A) では、&LenPoolMin の DCL が LEN(8) から LEN(80) に変更されています。新しい CL 変数 &Sav_Pools も定義されています。
図 4 の (B) では、プログラムは最初に QWDRSBSD API を呼び出しました。現在はサブシステム (&SBS_Pools) で定義されたストレージ・プールの数を新しい &Sav_Pools 変数に保存しています。&SBSI0100 の定義を確認すると、&SBS_Pools は位置 77 で始まる 4 バイト整数であることがわかります。前述のように、図 2 のプログラムでは、&SBS_BytAvl の値にアクセスするには &SBSI0100 の先頭 8 バイトに最初にアクセスしさえすればよいのです。最初のプール数を保存することは、プログラムで &SBSI0100 の先頭 80 バイトにアクセスする必要があることを意味します。&SBSI0100 の 77 バイトから 80 バイトにアクセスできるよう &LenPoolMin への LEN 変更 (8 から 80へ) が行われました。
図 4 の (C) では、&SBS_Pools の現在値と以前保存した値 (&Sav_Pools) が比較されます。&SBS_Pools 値が &Sav_Pools 値より大きい場合、サブシステム・プールを追加した人がおり、&SBS_Pools が &Sav_Pools に変更されています。ストレージ・プール情報を処理する DOFOR グループは &SBS_Pools で条件付けられているためこの変更が必要で、実際に戻されたストレージ・プールの数は &Sav_Pools 値で表されています。これは、QWDRSBSD API が呼び出されたときに提供された受信側変数が &Sav_Pools ストレージ・プールに十分対応できるサイズであったためです。&SBS_Pools を &Sav_Pools に変更するのに加えて、&More インジケーターの使用も再実装される可能性があります。この機能はシリーズのパート 2 で説明しました。このシナリオでは、サブシステムに追加のストレージ・プールが存在しているとユーザーには通知されています。
&SBS_Pools 値が &Sav_Pools 値と等しい場合、変更は必要なく、ストレージ・プール情報は処理できます。&SBS_Pools 値が &Sav_Pools 未満の場合、ストレージ・プールを削除した人がいることを示しており、&SBS_Pools の現在値は処理するストレージ・プール数という点で正しいです。
また当然、どれだけのサイズの変数を宣言するか、あくまで最も妥当な推測をし続けるという選択肢もあります。ただし、図 3 に示すような可変ストレージ割り当ての動的なサイズ変更は、処理するデータ量が著しく変化する場合に、極めて柔軟性のあるプログラムを提供します。これは毎日使用する可能性がある機能ではありませんが、コンパイル時ではなく、ランタイム時に変数のストレージを割り当てるというこのアプローチにより、アプリケーション・プログラムの制限を回避するのに役立つ場合があります。これらの制限は、量的にかなりばらつきのあるデータの格納に使用している、宣言済みの変数長が原因で発生しています。このデータはさまざまな形式で見られる場合があります。例えば、サンプル・プログラムにあるようなストレージ・プール、照会に含める支店のリスト、メモリーに読み込もうとしているファイル (16 メガバイトより小さいファイル。他の API は使用できるもののそれが malloc API の制限値であるため)、またはその中間に位置するようなものとして存在する場合があります。
基底付き変数のメリット
基底付き変数を使用することで、アプリケーションの実際のロジックへの影響を最小限に抑えることができるという点は、読者の方も賛成していただけると思います。プログラムで実行される処理ではなく、プログラムが実行する環境の設定で主に変更が行われています。
したがって、次に変数でどの程度の長さを宣言するかを推量する事態になったときは、常にその推量をランタイムまで遅らせることができることを思い出してください。ランタイムになったら、推量する必要はありません。単に基底付き変数と動的ストレージ割り当てを使用してください。