サーチ…


前書き

配列は、隣接するメモリ位置に配置された同じ型の要素です。要素は、追加されたインデックスを持つ一意の識別子によって個別に参照できます。

これにより、特定の型の複数の変数値を宣言し、各値に変数を宣言することなく、個別にアクセスすることができます。

配列サイズ:コンパイル時に型を安全にします。

#include <stddef.h>     // size_t, ptrdiff_t

//----------------------------------- Machinery:

using Size = ptrdiff_t;

template< class Item, size_t n >
constexpr auto n_items( Item (&)[n] ) noexcept
    -> Size
{ return n; }


//----------------------------------- Usage:

#include <iostream>
using namespace std;
auto main()
    -> int
{
    int const   a[]     = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
    Size const  n       = n_items( a );
    int         b[n]    = {};       // An array of the same size as a.
    
    (void) b;
    cout << "Size = " << n << "\n";
}

配列サイズのsizeof(a)/sizeof(a[0])のCイディオムは、ポインタを引数として受け入れ、一般に間違った結果を生成します。

C ++ 11の場合

C ++ 11を使用すると、次のことができます。

std::extent<decltype(MyArray)>::value;

例:

char MyArray[] = { 'X','o','c','e' };
const auto n = std::extent<decltype(MyArray)>::value;
std::cout << n << "\n"; // Prints 4

C ++ 17まで(これからの記事)C ++には、配列のサイズを得るための組み込みのコア言語や標準ライブラリユーティリティはありませんでしたが、これは関数テンプレートへの参照によって配列を渡すことで実装できます。上に示しました。きめ細かい重要な点:テンプレートサイズのパラメータはsize_tで、署名されたSize関数の結果の型とやや矛盾しています。これは、時々テンプレートマッチングのためにsize_tを主張するg ++コンパイラに対応するためです。

C ++ 17以降では、代わりに配列に特化したstd::size使用することができます。

動的なサイズの生の配列

// Example of raw dynamic size array. It's generally better to use std::vector.
#include <algorithm>            // std::sort
#include <iostream>
using namespace std;

auto int_from( istream& in ) -> int { int x; in >> x; return x; }

auto main()
    -> int
{
    cout << "Sorting n integers provided by you.\n";
    cout << "n? ";
    int const   n   = int_from( cin );
    int*        a   = new int[n];       // ← Allocation of array of n items.
    
    for( int i = 1; i <= n; ++i )
    {
        cout << "The #" << i << " number, please: ";
        a[i-1] = int_from( cin );
    }

    sort( a, a + n );
    for( int i = 0; i < n; ++i ) { cout << a[i] << ' '; }
    cout << '\n';
    
    delete[] a;
}

配列T a[n];を宣言するプログラムT a[n]; nは実行時に決定され、C99 可変長配列 (VLA)を言語拡張としてサポートする特定のコンパイラでコンパイルできます。しかし、VLAは標準のC ++ではサポートされていません。この例は、 new[] -expressionを使用して動的サイズ配列を手動で割り当てる方法を示しています。

int*        a   = new int[n];       // ← Allocation of array of n items.

...それを使って、最後にdelete[] -expressionを使って割り当てを解除します:

delete[] a;

ここで割り当てられた配列には不確定な値がありますが、 new int[n]()ように空の括弧()追加するだけでゼロ初期化できます。より一般的には、任意の項目タイプに対して、これは値の初期化を実行します

呼び出し階層の関数の一部として、このコードは例外的に安全ではありません。これは、 delete[]式の前の例外(およびnew[]後)がメモリリークを引き起こすためです。その問題に対処する1つの方法は、例えばstd::unique_ptrスマートポインタを使ってクリーンアップを自動化することです。しかし、それに対処する一般的に良い方法は、単にstd::vector使うことです: std::vectorがそこにあります。

std :: vectorを使って動的なサイズの配列を展開する。

// Example of std::vector as an expanding dynamic size array.
#include <algorithm>            // std::sort
#include <iostream>
#include <vector>               // std::vector
using namespace std;

int int_from( std::istream& in ) { int x = 0; in >> x; return x; }

int main()
{
    cout << "Sorting integers provided by you.\n";
    cout << "You can indicate EOF via F6 in Windows or Ctrl+D in Unix-land.\n";
    vector<int> a;      // ← Zero size by default.

    while( cin )
    {
        cout << "One number, please, or indicate EOF: ";
        int const x = int_from( cin );
        if( !cin.fail() ) { a.push_back( x ); }  // Expands as necessary.
    }

    sort( a.begin(), a.end() );
    int const n = a.size();
    for( int i = 0; i < n; ++i ) { cout << a[i] << ' '; }
    cout << '\n';
}

std::vectorは、可変サイズ配列の概念を提供する標準ライブラリクラステンプレートです。すべてのメモリ管理を担当し、バッファは連続しているため、バッファへのポインタ(例: &v[0]またはv.data() )を生の配列を必要とするAPI関数に渡すことができます。例えば、アイテムを追加するpush_backメンバ関数を介して、実行時にvectorを展開することさえできます。

ベクトル拡張に含まれるコピーまたは移動を含むn push_back操作の複雑さは、償却されたO( n )である。 「償却額」:平均して。

内部的には、これは通常、より大きなバッファが必要なときに、そのバッファサイズ、その容量を2倍にするベクトルによって達成されます。例えば、サイズ1から始まり、 n = 17 push_backコールのために必要に応じて繰り返し倍増するバッファの場合、これは、1 + 2 + 4 + 8 + 16 = 31のコピー操作を含み、2× n = 34未満である。より一般的には、このシーケンスの合計は2× nを超えることはできません。

動的サイズの生の配列の例と比較して、このvectorベースのコードでは、ユーザーは前面の項目の数を指定(および認識)する必要はありません。代わりに、ベクトルは、ユーザーが指定した新しい項目値ごとに、必要に応じて拡張されます。

固定サイズの生の配列マトリックス(つまり、2Dの生の配列)。

// A fixed size raw array matrix (that is, a 2D raw array).
#include <iostream>
#include <iomanip>
using namespace std;

auto main() -> int
{
    int const   n_rows  = 3;
    int const   n_cols  = 7;
    int const   m[n_rows][n_cols] =             // A raw array matrix.
    {
        {  1,  2,  3,  4,  5,  6,  7 },
        {  8,  9, 10, 11, 12, 13, 14 },
        { 15, 16, 17, 18, 19, 20, 21 }
    };
    
    for( int y = 0; y < n_rows; ++y )
    {
        for( int x = 0; x < n_cols; ++x )
        {
            cout << setw( 4 ) << m[y][x];       // Note: do NOT use m[y,x]!
        }
        cout << '\n';
    }
}

出力:

   1   2   3   4   5   6   7
   8   9  10  11  12  13  14
  15  16  17  18  19  20  21

C ++では、多次元配列のインデックス作成のための特別な構文はサポートされていません。代わりに、そのような配列は配列の配列(おそらく配列など)とみなされ、通常の単一のインデックス表記[ i ]が各レベルで使用されます。上の例では、 m[y]myを参照します。ここで、 yは0から始まるインデックスです。この行は、次に、例えばで索引付けすることができるm[y][x]を指し、 xまたは列- -行の項目番目 y

つまり、最後のインデックスが最も速く変化します。宣言では、このインデックスの範囲は1行あたりの列数で、最後に指定された「最も内側」のサイズです。

C ++では、ダイナミックアロケーション以外のダイナミックサイズ配列のビルトインサポートが提供されていないため、動的サイズマトリックスはクラスとして実装されることがよくあります。次に、配列行列の索引付け表記法m[y][x]は、実装を公開すること(つまり、転置された行列のビューが実際に不可能になるようにすること)、または返すことによっていくらかのオーバーヘッドとわずかな不便さを追加することoperator[]代理オブジェクトです。したがって、そのような抽象化のための索引付け表記法は、ルック・アンド・フィールおよび索引の順番、例えばm(x,y)またはm.at(x,y)またはm.item(x,y)

格納用にstd :: vectorを使用する動的サイズ行列。

残念なことにC ++ 14の時点では、C ++標準ライブラリに動的なサイズの行列クラスはありません。しかし、動的サイズをサポートするMatrixクラスは、Boost Matrixライブラリ(Boostライブラリ内のサブライブラリ)を含む多くのサードパーティライブラリから入手できます。

Boostやその他のライブラリに依存したくない場合は、C ++の動的なサイズ行列の1つがまるで同じです

vector<vector<int>> m( 3, vector<int>( 7 ) );

... vectorstd::vectorです。ここで、 nは行数、ここでは3である行ベクトルをn回コピーすることによって作成されます。固定サイズの生の配列行列と同じm[y][x]インデックス表記法を提供するという利点がありますが、各行に動的な割り当てが含まれているため、非効率的です。不注意に行のサイズを変更する可能性があるため、安全ではありません。

より安全で効率的なアプローチは、行列の記憶域として単一のベクトルを使用し、そのベクトルの対応するインデックスにクライアントコードの( xy )をマッピングすることです。

// A dynamic size matrix using std::vector for storage.

//--------------------------------------------- Machinery:
#include <algorithm>        // std::copy
#include <assert.h>         // assert
#include <initializer_list> // std::initializer_list
#include <vector>           // std::vector
#include <stddef.h>         // ptrdiff_t

namespace my {
    using Size = ptrdiff_t;
    using std::initializer_list;
    using std::vector;

    template< class Item >
    class Matrix
    {
    private:
        vector<Item>    items_;
        Size            n_cols_;
        
        auto index_for( Size const x, Size const y ) const
            -> Size
        { return y*n_cols_ + x; }

    public:
        auto n_rows() const -> Size { return items_.size()/n_cols_; }
        auto n_cols() const -> Size { return n_cols_; }

        auto item( Size const x, Size const y )
            -> Item&
        { return items_[index_for(x, y)]; }
        
        auto item( Size const x, Size const y ) const
            -> Item const&
        { return items_[index_for(x, y)]; }

        Matrix(): n_cols_( 0 ) {}

        Matrix( Size const n_cols, Size const n_rows )
            : items_( n_cols*n_rows )
            , n_cols_( n_cols )
        {}
        
        Matrix( initializer_list< initializer_list<Item> > const& values )
            : items_()
            , n_cols_( values.size() == 0? 0 : values.begin()->size() )
        {
            for( auto const& row : values )
            {
                assert( Size( row.size() ) == n_cols_ );
                items_.insert( items_.end(), row.begin(), row.end() );
            }
        }
    };
}  // namespace my

//--------------------------------------------- Usage:
using my::Matrix;

auto some_matrix()
    -> Matrix<int>
{
    return
    {
        {  1,  2,  3,  4,  5,  6,  7 },
        {  8,  9, 10, 11, 12, 13, 14 },
        { 15, 16, 17, 18, 19, 20, 21 }
    };
}

#include <iostream>
#include <iomanip>
using namespace std;
auto main() -> int
{
    Matrix<int> const m = some_matrix();
    assert( m.n_cols() == 7 );
    assert( m.n_rows() == 3 );
    for( int y = 0, y_end = m.n_rows(); y < y_end; ++y )
    {
        for( int x = 0, x_end = m.n_cols(); x < x_end; ++x )
        {
            cout << setw( 4 ) << m.item( x, y );        // ← Note: not `m[y][x]`!
        }
        cout << '\n';
    }
}

出力:

   1   2   3   4   5   6   7
   8   9  10  11  12  13  14
  15  16  17  18  19  20  21

上記のコードは工業用ではありません。基本的な原則を示し、C ++を学ぶ学生のニーズに応えるように設計されています。

たとえば、 operator()オーバーロードを定義して、索引付けの表記法を簡略化することができます。

配列の初期化

配列は、特定のタイプの変数のシーケンシャルなメモリ位置の単なるブロックです。配列は通常の変数と同じ方法で割り当てられますが、配列メモリに収まる要素の数を含む名前[]には角括弧が付加されています。

以下の配列の例は、typ int 、変数名arrayOfInts 、および配列にスペースがある要素数[5]を使用しています。

int arrayOfInts[5];

このように同時に配列を宣言して初期化することができます

int arrayOfInts[5] = {10, 20, 30, 40, 50};

すべてのメンバを列挙して配列を初期化するときは、角括弧の中に要素数を入れる必要はありません。コンパイラによって自動的に計算されます。次の例では、5:

int arrayOfInts[] = {10, 20, 30, 40, 50};

また、最初の要素だけを初期化し、より多くの領域を割り当てることもできます。この場合、括弧内に長さを定義することは必須です。以下では、部分的な初期化で長さ5の配列を割り当てます。コンパイラは、残りのすべての要素を要素型の標準値(この場合は0)で初期化します。

int arrayOfInts[5] = {10,20}; // means 10, 20, 0, 0, 0

他の基本データ型の配列も同じ方法で初期化することができます。

char arrayOfChars[5]; // declare the array and allocate the memory, don't initialize

char arrayOfChars[5] = { 'a', 'b', 'c', 'd', 'e' } ; //declare and initialize

double arrayOfDoubles[5] = {1.14159, 2.14159, 3.14159, 4.14159, 5.14159};

string arrayOfStrings[5] = { "C++", "is", "super", "duper", "great!"};

配列要素にアクセスするとき、配列の要素インデックス(または位置)は0から始まることに注意することも重要です。

int array[5] = { 10/*Element no.0*/, 20/*Element no.1*/, 30, 40, 50/*Element no.4*/};
std::cout << array[4]; //outputs 50
std::cout << array[0]; //outputs 10


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow