コンピュータプログラミングにおいて、可変長配列(VLA)は、可変サイズ配列または実行時サイズ配列とも呼ばれ、コンパイル時ではなく実行時に長さが決定される配列データ構造です。[1] C言語では、VLAは値に応じて可変に変更されるデータ型を持つと言われています(依存型を参照)。
VLA の主な目的は、数値アルゴリズムのプログラミングを簡素化することです。
VLAをサポートするプログラミング言語には、Ada、ALGOL 68(非フレキシブル行用)、APL、C#(非セーフモードのスタック割り当て配列として)、COBOL、Fortran 90、J、およびObject Pascal ( DelphiおよびLazarusで使用される言語で、 FPC を使用する)などがあります。 C99 ではVLA のサポートが導入されましたが、その後C11では条件付き機能に格下げされ、実装でサポートする必要はありません。[2] [3]一部のプラットフォームでは、以前は または同様の関数を使用して VLA を実装できましたalloca()。
拡張可能配列(動的配列とも呼ばれる)は、一般的にVLAよりも便利です。動的配列はVLAができることすべてを実行でき、実行時に配列を拡張することもサポートしているからです。このため、多くのプログラミング言語(JavaScript、Java、Python、Rなど)は拡張可能配列のみをサポートしています。可変長配列をサポートする言語であっても、(スタックベースの)可変長配列の使用を避け、代わりに(ヒープベースの)動的配列を使用することが推奨されることが多いです。 [4]
メモリ
割り当て
- C用のGNUコンパイラコレクション(GCC)は、スタック上の自動ストレージ期間を使用してVLAにメモリを割り当てます。[5]これはヒープ割り当てに比べて高速で簡単なオプションであり、ほとんどのコンパイラで使用されています。
- VLA はヒープ上に割り当てられ、このブロックへのポインターを使用して内部的にアクセスすることもできます。
実装
C99
次のC99関数は、指定されたサイズの可変長配列を割り当て、浮動小数点値で埋め、別の関数に渡して処理します。配列は自動変数として宣言されているため、read_and_process()戻り値でその有効期間は終了します。
float read_and_process ( int n ) { float vals [ n ];
( int i = 0 ; i < n ; ++ i )の場合、vals [ i ] = read_val ();
プロセス( n 、vals )を返します。
C99 では、関数呼び出しにおいて長さパラメータは可変長配列パラメータの前に来なければなりません。[1] C11 では、__STDC_NO_VLA__VLA がサポートされていない場合はマクロが定義されます。[6] C23 標準では、VLA 型が再び必須になりました。自動ストレージ期間を持つ VLA オブジェクトの作成のみがオプションです。[7] GCC には C99 より前に拡張機能として VLA があり、C++ 方言にも拡張されています。
Linus Torvalds は以前、事前に決められた小さなサイズの配列に VLA を使用するとアセンブリ コードの品質が低下するため、不満を表明していました。[8] Linux 4.20 カーネルでは、Linux カーネルは事実上 VLA フリーです。[9]
C11ではVLAのサイズ制限を明示的に指定していませんが、他のすべてのオブジェクトと同じ最大サイズ、つまりSIZE_MAXバイトを持つべきだと考える人もいます。[10]ただし、これは、環境とプラットフォームの制限のより広い文脈で理解する必要があります。たとえば、一般的なスタックガードページサイズは4KiBで、これはSIZE_MAXよりも桁違いに小さいです。
配列へのポインターを使用することで、動的ストレージを備えた VLA オブジェクトを作成できます。
float read_and_process ( int n ) { float ( * vals )[ n ] = malloc ( sizeof ( float [ n ] ));
( int i = 0 ; i < n ; ++ i ) ( * vals ) [ i ] = read_val ();
float ret = process ( n , * vals ); free ( vals ); return ret ; }
エイダ
以下はAdaでの同じ例です。Ada 配列には境界が保持されるため、 Process 関数に長さを渡す必要はありません。
型 Vals_Typeは Floatの配列(正の範囲<>)です 。
function Read_And_Process ( N : Integer ) return Float is
Vals : Vals_Type ( 1..N ) ; begin for I in 1..N loop Vals ( I ) : = Read_Val ; end loop ; return Process ( Vals ) ; end Read_And_Process ;
フォートラン90
同等のFortran 90関数は
関数read_and_process ( n )結果( o )整数、意図( in ) :: n実数:: o
実数、次元( n ) ::値
整数:: i
i = 1 , n vals ( i ) = read_val ()を実行します。o = process ( vals )を実行します。read_and_processを実行します。
コンパイル時にプロシージャ インターフェイスをチェックする Fortran 90 機能を使用する場合。一方、関数が Fortran 90 以前の呼び出しインターフェイスを使用する場合は、(外部) 関数を最初に宣言し、配列の長さを引数として明示的に渡す必要があります (C の場合と同様)。
関数read_and_process ( n )結果( o )整数、意図( in ) :: n実数:: o
実数、次元( n ) :: vals
実数:: read_val 、整数の処理:: i
i = 1 、n vals ( i ) = read_val ()を実行します。o = process ( vals 、n )を実行します。read_and_process関数を実行します。
コボル
次のCOBOLDEPT-PERSONフラグメントは、の値によって指定された長さ (メンバー数) を持つレコードの可変長配列を宣言しますPEOPLE-CNT。
データ部.作業領域セクション. 01 DEPT-PEOPLE . 05 PEOPLE-CNT PIC S9(4) 2進数. 05 DEPT-PERSON はPEOPLE-CNTに応じて0 回から20回発生します. 10 PERSON-NAME PIC X(20) . 10 PERSON-WAGE PIC S9(7)V99パック 10 進数.
COBOL VLAは、ここで説明した他の言語とは異なり、最大配列サイズの指定が必要なため安全です。この例では、DEPT-PERSONの値に関係なく、 は 20 を超える項目を持つことはできませんPEOPLE-CNT。
C#
次のC#フラグメントは、整数の可変長配列を宣言します。C# バージョン 7.2 より前では、配列へのポインターが必要であり、"unsafe" コンテキストが必要です。"unsafe" キーワードでは、このコードを含むアセンブリを unsafe としてマークする必要があります。
安全でないvoid DeclareStackBasedArrayUnsafe ( int size ) { int * pArray = stackalloc int [ size ]; pArray [ 0 ] = 123 ; }
C#バージョン7.2以降では、Span機能を使用することで、「unsafe」キーワードなしで配列を割り当てることができます。[11]
void DeclareStackBasedArraySafe ( int size ) { Span < int > stackArray = stackalloc int [ size ]; stackArray [ 0 ] = 123 ; }
オブジェクトパスカル
Object Pascalの動的配列はヒープ上に割り当てられます。[12]
この言語では、これは動的配列と呼ばれます。このような変数の宣言は静的配列の宣言に似ていますが、サイズを指定しません。配列のサイズは使用時に指定されます。
プログラムCreateDynamicArrayOfNumbers (サイズ: Integer ) ; var NumberArray : LongWordの配列; begin SetLength ( NumberArray 、Size ) ; NumberArray [ 0 ] := 2020 ; end .
動的配列の内容を削除するには、配列にサイズ 0 を割り当てます。
...
SetLength ( NumberArray , 0 ) ; ...
参考文献
- ^ ab 「可変長配列」。2018年1月26日時点のオリジナルよりアーカイブ。
- ^ 「可変長 - GNU コンパイラ コレクション (GCC) の使用」。
- ^ ISO 9899:2011 プログラミング言語 – C 6.7.6.2 4.
- ^ Raymond, Eric S. (2000). 「Raymond ソフトウェア リリース プラクティス ハウツー: 6. 優れた開発プラクティス」. Linux ドキュメンテーション プロジェクト。
- ^ 「コード生成オプション - GNU Fortran コンパイラ」。
- ^ C11規格の§6.10.8.3(n1570.pdf)
- ^ C23 規格の § 6.10.9.3 (n3054.pdf)
- ^ Torvalds, Linus (2018 年 3 月 7 日). 「LKML: Linus Torvalds: Re: VLA 削除 (以前は Re: [RFC 2/2] lustre: VLA_SAFE を使用)」. Linux カーネル(メーリング リスト).
- ^ 「Linux カーネルは VLA フリーになりました: セキュリティの面での勝利、オーバーヘッドの削減、Clang にとってのメリット - Phoronix」www.phoronix.com。
- ^ C11 規格の §6.5.3.4 および §7.20.3 (n1570.pdf)
- ^ 「stackalloc 演算子 (C# リファレンス)」。Microsoft。2024 年 7 月 10 日。
- ^ Michaël Van Canneyt. 「Free Pascal リファレンス ガイド: 動的配列」.
