その5

Table of Contents

やること

解説

数字でも文字列の場合でも,変数の塊を用意して使いたいと思うことがあります。 このようなときに使うのが配列です。 配列では要素数を指定する必要がありますが,vector は指定する必要がありません。

配列

5 つの要素 (elements) を持つ int 型の配列は以下のようにして作ります。

int numbers[5];

初期値を与えるには

int numbers[5] = { 7, 13, 0, 21, 100 };

のようにします。すべての要素を 0 にしたければ

int numbers[5] = {}

で十分です。

各要素にアクセスするには “[]” 演算子を使い

numbers[0]

のようにします。 番号が 1 ではなく 0 から始まることに注意しましょう[1]。 簡単な例を以下に示します。

#include <iostream>
using namespace std;

int main()
{

  // int values[3] = { 0, 1, 2 }; // 初期化するとき
  int values[3]; // 初期化しないとき, はじめに入っているのはめちゃくちゃな値

  for( int i=0; i<3; i++ )
    cout << i << " " << values[i] << endl;

  return 0;
}

初期化をしない場合,変数と同じように,初期値はめちゃくちゃな値が入っています。 上のプログラムで配列の初期化をやめ, “int values[3];” だけにしたら

[gnukazuk 10:39:46 temp] $ ./a.out
0 0
1 -415125024
2 32766

のようになりました。

また,定義した範囲外の要素へのアクセスをしてはいけません。 上のプログラムでは要素数を 3 つにしていますが,5 つまでアクセスし, cout で表示してから整数を代入してみたら

0 0
1 -484896280
2 32766
3 -1298071316
4 1371200947
Abort trap: 6

と強制終了してしまいました。

要素数を求める

配列の要素数は範囲外要素へのアクセスを防いだり,いろいろなことに使えます。 要素数を求めるには sizeof 演算子を使います。

sizeof( int );
sizeof( double );

のように型を与えると,型のサイズをバイト単位で返してくれます。 変数を与えると変数のサイズ,配列を与えると配列の確保している領域のサイズを返してくれます。

int numbers[5];
cout << "sizeof( numbers ) = "    << sizeof( numbers ) << endl;
cout << "sizeof( numbers[0] ) = " << sizeof( numbers[0] ) << endl;

したがって, (配列の確保している領域のサイズ)/(変数のサイズ) は配列の要素数になります。

cout << "number of elements = "   << sizeof(numbers) / sizeof( numbers[0] ) << endl;

For 文で利用してみましょう。

int numbers[5] = { 0, 1, 2, 3, 4};  
for( int i=0; i<sizeof( numbers ) / sizeof( numbers[0] ); i++ ) // ここでつかってる!
    cout << i << " " << numbers[i] << endl;

多次元配列

これまで扱ってきた配列は 1 次元的なもの ( [0], [1], … , [n] , … ) でしたが,多次元的な配列も作ることができます。 例えば 2*3 の 2 次元配列は

int numbers_2D[3][3];

のようにします。 これは「『int 型要素を 3 つ持つ配列』という要素を 2 つ持つ配列」と理解してもいいでしょう。 初期化は

int values[2][3] = {};
int values[2][3] = { {0, 1, 2} , {3, 4, 5} };

のようにします。

sizeof 演算子を使ってみると,

cout << sizeof( values )       << endl; // 全サイズ
cout << sizeof( values[0] )    << endl; // int 型要素を 3 つ持つ配列のサイズ
cout << sizeof( values[0][0] ) << endl; // int 型のサイズ

から

24
12
4

が得られます。

のように計算することで

for( int i=0; i < sizeof( values ) / sizeof( values[0] ); i++ )
    for( int j=0; j < sizeof( values[0] ) / sizeof( values[0][0] ); j++ )
        cout << i << "-" << j << " " << values[i][j] << endl;

なんてことができます。

関数での取り扱い,そしてポインタへ

ちょっとあとで

vector

vector は配列の要素数を動的に決められるもので,動的配列クラスというものだそうです。 使用例を見ながら使い方を学んでいきましょう。

作り方

vector ヘッダーを

#include <vector>

のようにインクルードし,基本的に

vector < 型 > 変数名;

のようにして作ります。 型は int や string といったもので,クラスでも OK です。 例えば

vector < double > values;
vector < double > values2(10); // はじめから 10 この要素数を確保しておく

のようにします。 1 行目は要素数を確保していませんが,2 行目は 10 こを確保しています。 確保している要素数は size() 関数で得ることができます。 例えば

#include <iostream>
#include <vector>
using namespace std;

int main()
{

    vector < int > values;  
    cout << values.size() << endl;

    vector < int > values2(10);
    cout << values2.size() << endl;

    return 0;
}

を実行すると

[gnukazuk 10:54:44 temp] $ ./a.out
0
10

となります。

要素数を確保しているときは

values2[1] = 100;

のように直接アクセスできます。

vector の vector

つくれます。

vector < vector < int > > numbers_2d; // これもできるが,要素数を確保しておいたほうがいろいろと便利なので・・・・
vector < vector < int > > numbers2_2d(3); // こうしたほうが楽

1 行目のようにしても問題ありませんが,アクセスするのは vector < int > 単位(行単位とか列単位)です。 2 行目のようにすると, int 単位(最小単位のこと)でアクセスでき直感的なのがいいのですが,vector のメリットである可変長という性質を有効に使えていないのが残念な点です。

要素を追加・削除する

push_back 関数を使うと要素を並びの一番うしろに追加することができます。 vector はクラスなので “.” 演算子を使って push_back 関数にアクセスし

vector < double > values;
values.push_back( 1.1 );
values.push_back( -0.1 );

cout << values[0] << endl; // 1.1 が表示される
cout << values[1] << endl; // -0.1 が表示される

cout << values.size() << endl; // 要素数 2 が表示される

のようにします。

並びの一番うしろにある要素を削除するには pop_back 関数を使います。 上の例に続けて

values.pop_back();
cout << values.size() << endl; // 要素数 1 が表示される
cout << values[0] << endl; // 1.1 が表示される

とします。

任意の要素を消したり,並びの中に新たな要素を割り込ませるにはイテレータを使う必要があります。

イテレータ

最大・最小値,合計を得る

私がよく使うので紹介します。

最大・最小は algorithm ヘッダーにある max_element, min_element 使い

*max_element( values.begin() , values.end() );
*min_element( values.begin() , values.end() );

のようにします。 1 つ目の引数は vector の検索する範囲のはじめのイテレータを与えます。 ここでは最初の要素のイテレータを返す begin() 関数を使っています。 2 つ目の引数は検索範囲の最後のイテレータで,end() 関数を使って最後の要素のイテレータを与えました。 max_element, min_element 関数はイテレータを返す関数なので,そのままでは値を得ることができません。 なので “*” を返ってくるイテレータにつけ,イテレータの指す値を取得します。

合計は numeric ヘッダーにある accumulate を使い

accumulate( values.begin(), values.end(), 0 );

のようにします。 第 1, 2 引数は max_element と同じです。 3 つ目の引数は初期値で,これに対してすべての要素が足し合わされます。 単純な合計を求めたいときは 0 を与えましょう。 この関数は値を返します。

簡単な使用例を示します。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{

    vector < double > values;
    values.push_back( 1.0 );
    values.push_back( 2.0 );
    values.push_back( 3.0 );

    cout << *max_element( values.begin() , values.end() ) << endl;
    cout << *min_element( values.begin() , values.end() ) << endl;

    return 0;
}

課題

課題1

配列

int numbers[5] = {-1, 100, 2, -30, 5};

から最大値,最小値を得る方法を考えましょう。

回答例

課題2

2 次元配列で以下のような正方行列

const int num;
double values[num][num];

を作ったとき,これの転置行列を作ってみましょう。 行列の中身は適当に決めてください。

回答例

課題3

与えられた数列に対して以下のものを求めましょう。

ここで \(N\) は要素数です。

3–1

数列 \(x_{i}=i\ \ (i=0, 1, ..., 9) \)

回答例

3–2

(課題を考え中・・・・・・)

数列 \(x_{i}=i\ \ (i=0, 1, ..., 9) \)
回答例


  1. Fortran のように 1 から始まる言語もあります。  ↩