2.2.3. 割り当てた配列の情報を構造体に記録する例
今までは割り当てたメモリ領域の先頭アドレスだけをポインタ変数で保持していました が、配列領域の長さも変数で記録しておくと便利なことがあります。ポインタと長さを セットで覚えておくために、構造体かクラスを使うことができます。
この例では、歴史的に古くからある構造体を用いて、C言語風に実装しています。
1// s003: dynamically allocated array held on a struct
2// along with the data count.
3//
4#include <iostream>
5#include <cassert>
6
7const int N_SIZE = 10;
8
9struct Array {
10 double *pdata;
11 int n_size;
12};
まず構造体 Array
を定義しています。 Array
という型がこれで定義されるの
で、 Array
型の変数を定義できるようになります。
14// same functions as s001.cpp
15
16// function to set dummy data to the array
17void set_dummy_data_struct(Array *par)
18{
19 for (int i = 0; i < par->n_size; i++) {
20 par->pdata[i] = static_cast<double>(i) / static_cast<double>(par->n_size);
21 }
22}
ダミーデータを設定する set_dummy_data_struct
関数では Array型のポインタを受
け取ります。このポインタが指している Array
のデータ中に、配列データのポイン
タと、配列の要素数が記録されています。
par->n_size
のようにして、ポインタが指している構造体のメンバ変数にアクセスします。
24void copy_data_struct(Array *pdest, const Array *psrc)
25{
26 assert(pdest->n_size == psrc->n_size);
27 for (int i = 0; i < pdest->n_size; i++) {
28 pdest->pdata[i] = psrc->pdata[i];
29 }
30}
データをコピーする関数ではArray型のポインタを2つ受け取ります。
コピー元の psrc
には const がついています。そのため psrc
が指している構
造体のメンバ変数は代入禁止になります。ただし、そこからさらに先は代入禁止ではあり
ません。
psrc->n_size
: 代入禁止psrc->pdata
: 代入禁止psrc->pdata[i]
: 代入可能
これでは、constの意味が不完全です。本来は「コピー元の psrc
の中身は改変しな
い」という約束のためにconstを付けているのに、配列の中身のデータについては書き換
えることができてしまいます。
この点はC++の言語仕様で割り切られている点だと思います。これでも一定の意義は果た せます。
32void print_data_struct(const Array *par)
33{
34 std::cout << "[";
35 for (int i = 0; i < par->n_size; i++) {
36 std::cout << par->pdata[i];
37 if (i < par->n_size - 1) {
38 std::cout << ", ";
39 }
40 }
41 std::cout << "]\n";
42}
43
44void print_data(const double *d, int n)
45{
46 std::cout << "[";
47 for (int i = 0; i < n; i++) {
48 std::cout << d[i];
49 if (i < n - 1) {
50 std::cout << ", ";
51 }
52 }
53 std::cout << "]\n";
54}
配列の内容を印字する print_data
は、 Array
を受け取る版と、配列の先頭ア
ドレスを受け取る版の2つを用意しています。後でmain関数を見ると、それぞれを呼び出
す処理が登場します。
56// allocate an array.
57// returns true on success, false on failure.
58bool allocate_array_struct(int n, Array **ppArray) {
59 assert(ppArray != nullptr);
60 try {
61 Array *pArray = new Array;
62 pArray->pdata = new double[n];
63 pArray->n_size = n;
64 *ppArray = pArray;
65 return true;
66 } catch (const std::bad_alloc &e) {
67 // could not allocate memory
68 return false;
69 }
70}
71
72void free_array_struct(Array *pArray) {
73 delete [] pArray->pdata;
74 delete pArray;
75}
配列の割り当ては allocate_array_struct
関数で行っています。引数の
**ppArray
は Array
型のポインタのポインタです。処理は、 Array
のメモ
リ割り当てと、その先の配列データのメモリ割り当ての2段構えになっています。
free_array_struct
では2段構えでメモリを解放しています。
77int main(int argc, const char *argv[])
78{
79 std::cout << "s003\n";
80
81 // allocate array dynamically on the heap area.
82 Array *parray1;
83 Array *parray2;
84 if (! allocate_array_struct(N_SIZE, &parray1)) {
85 return EXIT_FAILURE;
86 }
87 if (! allocate_array_struct(N_SIZE, &parray2)) {
88 free_array_struct(parray2);
89 return EXIT_FAILURE;
90 }
main
関数では配列を割り当てた後に個々の関数を呼んでいます。
allocate_array_struct
関数の呼び出しにおいては Array
のポインタ変数
parray
に対して &parray
と書いて変数のアドレスを取得しています。「変数
parrayのアドレスはここだから、結果をこのアドレスに書き込んでください」というよう
に引数として渡しています。
92 // 静的領域のメモリの初期値は 0
93 // ただし自分で初期値を入れることが推奨される。
94 std::cout << "Before initialization\n";
95 print_data_struct(parray1);
96
97 // 配列を直接受け取る関数を呼ぶ例
98 print_data(parray1->pdata, parray1->n_size);
配列の先頭アドレスを要求する関数 print_data
を呼ぶときは、 Array
の先頭
アドレスではなくてその先の配列データの先頭アドレス parray1->parray
を渡して
います。
100 set_dummy_data_struct(parray1);
101 std::cout << "After initialization\n";
102 print_data_struct(parray1);
103
104 copy_data_struct(parray2, parray1);
105 std::cout << "After array copy\n";
106 print_data_struct(parray2);
107
108 // return allocated memory to OS
109 // new に成功した場合にのみ delete を呼ぶこと。
110 free_array_struct(parray1);
111 free_array_struct(parray2);
112
113 return EXIT_SUCCESS; // exit status code 0
114}
ソース全体は以下のようになります:
1// s003: dynamically allocated array held on a struct
2// along with the data count.
3//
4#include <iostream>
5#include <cassert>
6
7const int N_SIZE = 10;
8
9struct Array {
10 double *pdata;
11 int n_size;
12};
13
14// same functions as s001.cpp
15
16// function to set dummy data to the array
17void set_dummy_data_struct(Array *par)
18{
19 for (int i = 0; i < par->n_size; i++) {
20 par->pdata[i] = static_cast<double>(i) / static_cast<double>(par->n_size);
21 }
22}
23
24void copy_data_struct(Array *pdest, const Array *psrc)
25{
26 assert(pdest->n_size == psrc->n_size);
27 for (int i = 0; i < pdest->n_size; i++) {
28 pdest->pdata[i] = psrc->pdata[i];
29 }
30}
31
32void print_data_struct(const Array *par)
33{
34 std::cout << "[";
35 for (int i = 0; i < par->n_size; i++) {
36 std::cout << par->pdata[i];
37 if (i < par->n_size - 1) {
38 std::cout << ", ";
39 }
40 }
41 std::cout << "]\n";
42}
43
44void print_data(const double *d, int n)
45{
46 std::cout << "[";
47 for (int i = 0; i < n; i++) {
48 std::cout << d[i];
49 if (i < n - 1) {
50 std::cout << ", ";
51 }
52 }
53 std::cout << "]\n";
54}
55
56// allocate an array.
57// returns true on success, false on failure.
58bool allocate_array_struct(int n, Array **ppArray) {
59 assert(ppArray != nullptr);
60 try {
61 Array *pArray = new Array;
62 pArray->pdata = new double[n];
63 pArray->n_size = n;
64 *ppArray = pArray;
65 return true;
66 } catch (const std::bad_alloc &e) {
67 // could not allocate memory
68 return false;
69 }
70}
71
72void free_array_struct(Array *pArray) {
73 delete [] pArray->pdata;
74 delete pArray;
75}
76
77int main(int argc, const char *argv[])
78{
79 std::cout << "s003\n";
80
81 // allocate array dynamically on the heap area.
82 Array *parray1;
83 Array *parray2;
84 if (! allocate_array_struct(N_SIZE, &parray1)) {
85 return EXIT_FAILURE;
86 }
87 if (! allocate_array_struct(N_SIZE, &parray2)) {
88 free_array_struct(parray2);
89 return EXIT_FAILURE;
90 }
91
92 // 静的領域のメモリの初期値は 0
93 // ただし自分で初期値を入れることが推奨される。
94 std::cout << "Before initialization\n";
95 print_data_struct(parray1);
96
97 // 配列を直接受け取る関数を呼ぶ例
98 print_data(parray1->pdata, parray1->n_size);
99
100 set_dummy_data_struct(parray1);
101 std::cout << "After initialization\n";
102 print_data_struct(parray1);
103
104 copy_data_struct(parray2, parray1);
105 std::cout << "After array copy\n";
106 print_data_struct(parray2);
107
108 // return allocated memory to OS
109 // new に成功した場合にのみ delete を呼ぶこと。
110 free_array_struct(parray1);
111 free_array_struct(parray2);
112
113 return EXIT_SUCCESS; // exit status code 0
114}
実行例を示します:
% ./s003
s003
Before initialization
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[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]