Index

HOME > プログラムTOP > C++



C++の壺・配列篇

 C++の基本テクニックのTipsです。配列とは、同じデータ型の集合体です。配列篇では、配列の知識としくみ、基本的な使用方法を扱います。
[index] 配列の基礎 ... 配列に関する基礎知識
代入とコピー ... 配列の代入とコピー
配列と関数 ... 配列の引数と配列の戻り値
動的配列 ... 配列サイズの動的確保

sasaraan programming

Exposition

●配列の基礎

 配列は同じデータ型の要素を複数集めたものです。変数名の後ろに []をつけて宣言します。[]の中に要素数を入れます。 また、初期化するときは{}の中に、,で区切って要素を並べます。 この時、配列の要素は省略することもできます。不足する添え字の要素があれば、自動的に 0 が割り当てられます。
 また、各要素には 添え字(インデックス) を使ってアクセスします。添え字は必ず 0 から始まり、要素数ー1 で終わります。
int n[10] ;                                               // int型の要素を10個持った配列を宣言 
int a[10] = {10, 20, 30, 40, 50, 60, 70, 80} ;  // 配列の宣言と初期化(添え字は 0〜9)
int fig0 = a[0] ;                                    // = 10
int fig1 = a[1] ;                                    // = 20
int fig2 = a[7] ;                           // = 80
int fig3 = a[8] ;                                    // = 0 (添え字8と9の要素は自動的に0で初期化)
配 列int a[10] = {10, 20, 30, 40, 50, 60, 70,} ;
添え字0123456789
要 素a[0]a[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9]
要素の値10203040506070000
 配列の個数は省略することもできます。この時は、初期化の際の要素数をもとに自動的にサイズが調整されます。
 また、多次元にわたる配列も定義が可能です。array[2][3] とすると、要素数3の配列を2つ宣言することになります。初期化の際は、視認性をよくするために、次元グループごとに{}でくくることができます。要素数を省略する時は、先頭の要素数のみが省略可能です。
int m[] = {11, 22, 33, 44, 55, 66, 77} ;         // 要素数を省略して宣言 
int n[2][3] = { {10, 20, 30}, {70, 80, 90} } ;    // 二次元配列(要素数3個の配列を2個)
// → int n[2][3] = {10, 20, 30, 70, 80, 90} ; としても同じ)
int a[][3] = { {10, 20, 30}, {111 ,222 333} } ;  // 要素数を省略
int fig1 = a[0][0] ;                                // = 10
int fig2 = a[0][1] ;                                // = 20
int fig3 = a[1][0] ;                                // = 111
int fir4 = a[1][1] ;                                    // = 222 
二次元配列int a[2][4] = { {10, 20, 30, 40}, {60, 70, 80, 90} } ;
グループa[0][x] : {10, 20, 30, 40}a[1][x] : {60, 70, 80, 90}
要 素a[0][0]a[0][1]a[0][2]a[0][3]a[1][0]a[1][1]a[1][2]a[1][3]
要素の値1020304060708090
 配列の場合、添え字なしの配列名は先頭要素(添え字0の要素)のアドレスを指します。整数値を加算したり減算したりすることで、各要素の値やアドレスを表現することができます。
配 列
ポインタ
int a[7] = {11, 22, 33, 44, 55, 66, 77} ;
int *p = a ;
配列の要素a[0]a[1]a[2]a[3]a[4]a[5]a[6]
要素のアドレスaa+1a+2a+3a+4a+5a+6
ポインタで要素の表現*p*(p+1)*(p+2)*(p+3)*(p+4)*(p+5)*(p+6)
ポインタでアドレスの表現pp+1p+2p+3p+4p+5p+6

●代入とコピー

 配列は、初期化の際とは異なり、そのままでは代入はできません。要素一つ一つに代入する必要があります。コピーをする時も同様に各要素ごとに値をコピーする必要があります。
// 配列の代入
#include <conio.h>    // getch使用
int main() 
{
    int n[3];
    // n[3] = {11, 22, 33}; → 不可

    // 要素ごとに代入
    n[0] = 11;
    n[1] = 22;
    n[2] = 33;
    if (getch()) return 0;
} 	
// 配列のコピー
#include <conio.h>    // getch使用
int main() 
{
    int n[3] = {11, 22, 33};
    int a[3];    // a[3] = n[3]; → 不可

    int i;
    for (i=0; i<3; i++) {
        a[i] = n[i];  // 要素ごとにコピー
    }
    if (getch()) return 0;
}	
 配列のサイズに値を設定する時、変数や変数の混じった式を使用することはできません。また、0 以下の値も設定できません。値が 1 以上となる定数または定数式であることが必要です。
// サイズに記号定数を代入
#include <conio.h>
#define SIZE 2
int main() 
{
    int n[SIZE+1];    // 要素数=3
    n[0] = 11;
    n[1] = 22;
    n[2] = 33;
    if (getch()) return 0;
} 	
// サイズにconst定数を代入
#include <conio.h>
int main() 
{
    const int SIZE = 4;
    int n[SIZE-1];    // 要素数=3
    n[0] = 11;
    n[1] = 22;
    n[2] = 33;
    if (getch()) return 0;
}	

●配列と関数

【配列の引数】
 配列を関数で使用する際、配列を引数(ひきすう)として取ることができます。この時、[]を添えて配列として記述することも、ポインタを使って記述することも可能です。以下の二つは同じ内容となります。
// 配列コピー関数
// dest...コピー先、src...コピー元、len...長さ
void acopy(int dest[], int src[], int len)
{
    int i ;
    for (i=0; i<len; i++) {
        dest[i] = src[i] ;
    }
} 	
// 配列コピー関数
// dest...コピー先、src...コピー元、len...長さ
void acopy(int *dest, int *src, int len)
{
    int i ;
    for (i=0; i<len; i++) {
        *(dest+i) = *(src+i);
    }
}	
 上記のように、C/C++ の配列の引数は、先頭要素のアドレスを渡すだけなので、別途長さ( len の部分)を指定する必要があります。この時、文字列のヌル文字のように、配列の終端を定数定義すると簡潔になることがあります。定数には、要素の値として使われることのない値を用います。ただし、変数側では、配列のサイズを、少なくとも 要素数+1 分を確保する必要があります。
#include <stdio.h>   // printf使用
#include <conio.h>  // getch使用

// 配列の終端を定義
#define EOA -1  // End Of Array

// 配列のコピー
void acopy(int *dest, int *src)
{
    while (*src != EOA) {
        *dest++ = *src++;
    }
    *dest = EOA;  // 末尾にEOAを代入
} 
int main() 
{
    int n[4] = {11, 22, 33, EOA}; // コピー元
    int a[4];                    // コピー先
    int i;

    acopy(a, n);            // 配列のコピー
    // 表示
    for (i=0; a[i] != EOA; i++) { 
        printf("%d, ", a[i]);  // "11, 22, 33, "
    }	

    if (getch()) return 0;
} 
【多次元配列の引数】
 引数が多次元配列の場合は次のように記述することができます。以下は二次元配列の例ですが、配列の初期化の時と同様に、最初の[]内のサイズは省略することができません。
// 二次元配列のコピー
// a1...コピー先、a2...コピー元、len...長さ
void acopy(int a1[][3], int a2[][3], int len)
{
    int i, j;
    for (i=0; i<len; i++) {
        for (j=0; j<3; j++) {
            a1[i][j] = a2[i][j];    // コピー
        }
    }
}
// main関数
void main() 
{
    // コピー元
    int n[2][3] = {{11,22,33}, {44,55,66}};
    //コピー先
    int a[2][3];

    // コピーの実行
    acopy(a, n, 2);
}
【戻り値としての配列】
 関数では、配列を戻り値とする時も、アドレスを渡すことになります。この時、関数内の変数を返す時は、static キーワードをつけて静的変数にする必要があります。通常の変数だと、関数の終了と同時に破棄されてしまうためです。静的変数は、関数が終わっても保持され、通常は、プログラム終了時に破棄されます。
 あるいは、右側の例のように、引数に格納先 (*dest) を用意しておく方法もあります。
#include <stdio.h>
#include <conio.h>

// 配列をコピーして返す
// src...コピー元
int *acopy(int src[])
{
    static int dest[3];
    int i;
    for (i=0; i<3; i++) {
        dest[i] = src[i];
    }
    return dest;
} 

// main 関数
int main() 
{
    int n[3] = {11, 22, 33};
    int *p = NULL;
    p = acopy(n);

    int i;
    for (i=0; i<3; i++) {
        printf("%d, ", *(p+i));
    }
    if (getch()) return 0;
}  
#include <stdio.h>
#include <conio.h>

// 配列をコピーして返す
// dest...格納先、src...コピー元、len...長さ
 int *acopy(int *dest, int *src, int len)
{
    int i;
    for (i=0; i<len; i++) {
        *(dest+i) = *(src+i);
    }
    return dest;
} 

// main 関数
int main() 
{
    int n[3] = {11, 22, 33};
    int a[3];
    int *p;
    p = acopy(a, n, 3);

    int i;
    for (i=0; i<3; i++) {
        printf("%d, ", *(p+i));
    }
    if (getch()) return 0;
} 

●動的配列

 配列は、通常、定数値をサイズに指定しますが、動的に配列を作成する時は、変数値を代入することができます。これにより、メモリの節約や、より柔軟な配列の操作が可能となります。
 配列を動的に作成する時は、new 演算子を用います。また、new 演算子で作成されたオブジェクトは、自動的に破棄されません。 delete 演算子を用いて明示的に破棄しないと、メモリ上にデータが残ったままになってしまいますので注意が必要です。なお、配列の破棄の場合は、[] の記述が必要です。

 ・ 作成 : new データ型サイズ
 ・ 破棄 : delete [] データへのポインタ;

#include <stdio.h>
#include <conio.h>

// main 関数
int main() 
{
    int n = 10;              // サイズ
    int i;

    int *p;
    p = new int[n];    // 作成

    for (i=0; i<n; i++) {
        *(p+i) = i;            // 0...9
    }
    for (i=0; i<n; i++) {
        printf("%d, ", *(p+i));
    }

    delete [] p;          // 破棄

    if (getch()) return 0;
}  
#include <stdio.h>
#include <conio.h>

 // main 関数
int main() 
{
    int n = 26;                   // サイズ
    int i;

    char *p;
    p = new char[n+1];  // 作成

    for (i=0; i<n; i++) {
        *(p+i) = 'A' + i;         // 'A'...'Z'
    }
    *(p+i) = '\0';  // 末尾にヌル文字を追加

    printf("%s\n", p);

    delete [] p;              // 破棄

    if (getch()) return 0;
} 

www.sasaraan.net

(c) morijoh