2.2.1. 固定サイズの1次元配列をグローバル変数として定義する例

この例ではグローバル変数 data1, data2 として配列を2つ割り当てています。

少しずつソースを見ていきます。

 1//
 2// s001 : fixed size array on static data area
 3//
 4
 5#include <iostream>
 6
 7const int N_SIZE = 10;
 8
 9// グローバル変数として定義された固定長の(コンパイル時点で値が確定する)配列
10double data1[N_SIZE];
11double data2[N_SIZE];

data1,data2はそれぞれ double の配列 になります。

data1[i] などと添え字をつけて普通は使いますが、C++では配列名の data1 単体で も意味(値)を持ちます。 data1 とだけ書くと配列の先頭アドレスを示す定数にな ります。配列の先頭アドレスが欲しかったら、 data1 と書けばいいわけです。

定数ですので data1 単体では、代入の左辺に来ることはできません。

この値は doubleのポインタ変数に代入できます。

12// 配列にダミーデータを設定する
13void set_dummy_data(double *d, int n)
14{
15    for (int i = 0; i < n; i++) {
16        d[i] = static_cast<double>(i) / static_cast<double>(n);
17    }
18    // static_cast<double>(x) について
19    // i や n は整数型で、C++では整数型同士の除算では計算結果も整数型になり
20    // 切り捨てられてしまう。
21    // 結果を少数で得るには double型(またはfloat型)に変換してから計算する必要がある。
22    // 古い文法では d[i] = ((double)i) / ((double) n); と書いたが現行の文法では上記の
23    // static_cast<型名>(値) を使う。
24}

最初の関数 set_dummy_data は、配列にダミーのデータを格納する関数です。

引数dの型は、doubleのポインタ型です。 ここには配列の先頭アドレスを渡すことができます。

27void copy_data(double *dest, const double *src, int n)
28{
29    // 引数の dest (destination) と src (source) では srcにだけ、const指定がついている
30    // constをつけた変数には代入はエラーになる。関数の中で、代入する必要性のない引数は const 
31    // 指定をつけることで、誤って変更してしまうことを防止できる。また、関数を使う人は、
32    // const指定を見ることで、その引数は改変されないことを確信できる。
33    for (int i = 0; i < n; i++) {
34        dest[i] = src[i]; // 左辺と右辺を入れ替えるとエラーになる。
35    }
36}

次の関数 copy_data は、配列から配列へ中身をコピーする関数です。

第二引数は const double *d として宣言されています。

こう書くと、ポインタdが指しているメモリの中身 *d が定数として扱われ、そこへの 代入が禁止され、エラーになります。d 単体には const がついていないので、 d は 変数であり、dへの代入は可能です。

もし const double * const d と書けば、*d にも d にも代入禁止になります。

38void print_data(const double *d, int n)
39{
40    std::cout << "[";
41    for (int i = 0; i < n; i++) {
42        std::cout << d[i];
43        if (i < n - 1) {
44            std::cout << ", ";
45        }
46    }
47    std::cout << "]\n";
48}

次の関数では配列の中身を印字しています。

49int main(int argc, const char *argv[])
50{
51    std::cout << "s001\n";
52
53    // 静的領域のメモリの初期値は 0
54    // ただし自分で初期値を入れることが推奨される。
55    std::cout << "Before initialization\n";
56    print_data(data1, N_SIZE);
57
58    // 配列の名前のデータ型は配列要素のアドレス。
59    // 受ける側のdoubleのポインタに代入できる。
60    set_dummy_data(data1, N_SIZE);
61    std::cout << "After initialization\n";
62    print_data(data1, N_SIZE);
63
64    copy_data(data2, data1, N_SIZE);
65    std::cout << "After array copy\n";
66    print_data(data2, N_SIZE);
67
68    return EXIT_SUCCESS; // exit status code 0
69}

最後に main 関数が書いてあります。

59行目を見ると、double のポインタを受け取る引数に、配列変数 data1 を渡してい ます。配列変数はこのようにポインタ変数に代入ができます。

全体のソースを以下に示します:

 1//
 2// s001 : fixed size array on static data area
 3//
 4
 5#include <iostream>
 6
 7const int N_SIZE = 10;
 8
 9// グローバル変数として定義された固定長の(コンパイル時点で値が確定する)配列
10double data1[N_SIZE];
11double data2[N_SIZE];
12
13// 配列にダミーデータを設定する
14void set_dummy_data(double *d, int n)
15{
16    for (int i = 0; i < n; i++) {
17        d[i] = static_cast<double>(i) / static_cast<double>(n);
18    }
19    // static_cast<double>(x) について
20    // i や n は整数型で、C++では整数型同士の除算では計算結果も整数型になり
21    // 切り捨てられてしまう。
22    // 結果を少数で得るには double型(またはfloat型)に変換してから計算する必要がある。
23    // 古い文法では d[i] = ((double)i) / ((double) n); と書いたが現行の文法では上記の
24    // static_cast<型名>(値) を使う。
25}
26
27void copy_data(double *dest, const double *src, int n)
28{
29    // 引数の dest (destination) と src (source) では srcにだけ、const指定がついている
30    // constをつけた変数には代入はエラーになる。関数の中で、代入する必要性のない引数は const 
31    // 指定をつけることで、誤って変更してしまうことを防止できる。また、関数を使う人は、
32    // const指定を見ることで、その引数は改変されないことを確信できる。
33    for (int i = 0; i < n; i++) {
34        dest[i] = src[i]; // 左辺と右辺を入れ替えるとエラーになる。
35    }
36}
37
38void print_data(const double *d, int n)
39{
40    std::cout << "[";
41    for (int i = 0; i < n; i++) {
42        std::cout << d[i];
43        if (i < n - 1) {
44            std::cout << ", ";
45        }
46    }
47    std::cout << "]\n";
48}
49
50int main(int argc, const char *argv[])
51{
52    std::cout << "s001\n";
53
54    // 静的領域のメモリの初期値は 0
55    // ただし自分で初期値を入れることが推奨される。
56    std::cout << "Before initialization\n";
57    print_data(data1, N_SIZE);
58
59    // 配列の名前のデータ型は配列要素のアドレス。
60    // 受ける側のdoubleのポインタに代入できる。
61    set_dummy_data(data1, N_SIZE);
62    std::cout << "After initialization\n";
63    print_data(data1, N_SIZE);
64
65    copy_data(data2, data1, N_SIZE);
66    std::cout << "After array copy\n";
67    print_data(data2, N_SIZE);
68
69    return EXIT_SUCCESS; // exit status code 0
70}

Download the source code

実行例を示します:

% ./s001
s001
Before initialization
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
After initialization
[0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]
After array copy
[0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]