【C++Builder Starter チュートリアルシリーズ】シーズン2 第5回 ‟配列と構造体„ [JAPAN]

Posted by on in Blogs

C++Builder Starter Edition シーズン2 第5回 ‟配列と構造体„

2017年1月23日より 「Delphi / C++Builder Starter チュートリアルシリーズ」 シーズン2、全9回、3月27日まで、毎週月曜日、Delphiパートが 17時00分~17時20分、 リアルタイム放送スペシャルコンテンツが5分~10分、C++Builderパートは 17時30分~17時50分の時間割でお送りしています。

無料でダウンロード&利用できる開発環境のDelphi / C++Builder Starter エディションを使用して、プログラミング言語のDelphi (Object Pascal ), C++の基礎を学ぶオンラインセッションです。

https://community.embarcadero.com/blogs/entry/2-japan

サマリ

アジェンダ

  • 今日のねらい
    • 配列とは何か、またその使い方を知る
    • 構造体とは何か、またその使い方を知る
  • 実施内容
    • 配列の定義、宣言、使用例
    • 静的・動的配列の違いと参照型について学ぶ
    • 構造体の定義、宣言、使用例

スライド資料

http://www.slideshare.net/ssuser8bd3f7/cbuilder-starter-2-cbuilder-72387567

開発環境のインストール

開発環境のインストールについては、[Delphi / C++Builder Starter チュートリアルシリーズ] 第1回 ‟無料で始めよう アプリ作成„ にて丁寧な説明がありますので、そちらをご視聴の上でインストールを進めてください。

https://community.embarcadero.com/blogs?view=entry&id=9038

実施内容

「配列」とは

  • 辞書の定義では「順序をきめてならべること」
  • 例えば「クラスの生徒の体重の数値を、出席番号順に並べる」というのも配列である

  • 配列の例(体重を0.1kgの精度で計測)

出席番号 1 2 3 4 5 6 7 8 9 10
体重(kg) 67.1 73.4 60.6 58.2 62.1 70.9 51.7 64.1 74.2 65.2
  • 配列の例(体重をkg単位で計測)
出席番号 1 2 3 4 5 6 7 8 9 10
体重(kg) 67 73 61 58 62 71 52 64 74 65

C++における関数とは?

  • 一連の処理を再利用可能とするために名前を付けたもの
  • 関数を利用するには宣言と定義が必要
  • 定義は関数を実際に実装したもので、宣言を伴う
  • 関数宣言部だけを単独で書くこともできる(この目的は後述)

これを C の文法で書いてみると?

  • C では以下のように書くことができる
// 0.1kg単位の体重を保持する配列を宣言し、初期値を定義している例。
double weight_double[] = { 67.1, 73.4, 60.6, 58.2, 62.1, 70.9, 51.7, 64.1, 74.2, 65.2 };
添字 0 1 2 3 4 5 6 7 8 9
weight_double 67.1 73.4 60.6 58.2 62.1 70.9 51.7 64.1 74.2 65.2

10個分のdouble型の要素を確保して初期化されている

// 要素数5の配列を確保するだけ。初期値は設定しない例。
int no_initialValue[5];
添字 0 1 2 3 4
no_initialValue ?? ?? ?? ?? ??

5個分のint型の要素は確保済み。値は不定なので、値を別途設定せねばならない。

  • このように、要素数が事前に決まっている配列は「静的配列」という
  • では事前に要素数が決まっていない配列はどのように宣言すればよいか?

要素数が変動する配列の作り方

  • 要素数を事前に決めていない配列 = 「動的配列」
// 要素数が不定の配列を宣言する例1。変数の識別子に * をつけて宣言し、new で配列を作成する。
long *unknownSize;unknownSize = new long[5];

// new で作成した配列は使わなくなった時点で廃棄する
delete[] unknownSize;
// 要素数が不定の配列を宣言する例2。C++Builderには DynamicArray という型がある。
DynamicArray<bool> unknownSize;unknownSize.Length = 5;

// DynamicArray では Length を増やすこともできる
unknownSize.Length = 10;

// DynamicArray で作成した配列は Length = 0 とすると解放される
unknownSize.Length = 0;

静的配列の例

{
   // 指定された値までの素数を「エラトステネスのふるい」で探す

   // 静的配列を確保する
   const int CALCRANGE= 1000;
   bool primeNumber[CALCRANGE];

   // 配列の値を true で初期化
   for (int i = 0; i < CALCRANGE ; i++)
       primeNumber[i] = true;

   // 1 は除外して探す
   for (int i = 2; i < CALCRANGE ; i++) {
       if ( primeNumber[i] == true ) {
           // 見つかった素数を表示する
           Memo1->Lines->Insert(0, IntToStr(i) );

           // 素数の倍数は素数ではないので調査範囲から外すために false にする
           for (int j = 2; i * j < CALCRANGE ; j++)
               primeNumber[ i * j ] = false;
       }
   }
}
  • 静的配列の大きさはコンパイルのときに設定されるので、固定の要素数を明示せねばならない。
  • 配列変数の要素数はマクロ定義するか、または const int の変数を介して取り扱うようにすると可読性が良くなる。
  • 静的配列は他の変数と同様に、関数(またはブロック)の終了時に捨てられる。
  • 静的変数を関数の戻り値に使えないことを意味する。

動的配列の例

{
   // 指定された値までの素数を「エラトステネスのふるい」で探す

   // 素数を探す範囲をフォームから受け取る
   int calcRange = StrToInt(Edit1->Text);

   // 動的配列を確保する
   bool *primeNumber;
   primeNumber = new bool[calcRange];

   // 配列の値を true で初期化
   for (int i = 0 ; i < calcRange ; i++)
       primeNumber[i] = true;

   // 1 は除外して探す
   for (int i = 2; i < calcRange ; i++) {
       if ( primeNumber[i] == true ) {
           // 見つかった素数を表示する
           Memo1->Lines->Insert(0, IntToStr(i) );

           // 素数の倍数は素数ではないので調査範囲から外すために false にする
           for (int j = 2; i * j < calcRange ; j++)
               primeNumber[ i * j ] = false;
       }
   }

   // 使い終わったので解放しておく
   delete[] primeNumber;
}
  • bool型の配列 primeNumber を宣言し、大きさを動的に確保する。
  • 不要になった時点で明示的に解放する

静的配列と動的配列のデータ管理の違い

静的配列

  • 静的配列は「関数やブロックで管理するメモリ」を使う。
  • 静的配列の要素は「関数やブロック」を抜ける時に廃棄対象となる。このために静的配列は関数の戻り値には使えない。(今どきのコンパイラはこのような実装はエラーになる)

動的配列

  • 動的配列は new した時点で「アプリケーション内で共通に使えるメモリ」に確保される。delete すると破棄される。
  • delete を明示的に実行しない限りは、new で確保した領域はメモリ上に残る。
  • new を実行した処理を抜けても確保したメモリが残るので、実行結果を呼び出し側に渡すことができる。
  • delete を忘れると、メモリリーク(不要になったメモリの解放が行わず、アプリケーションが使用できるメモリが減る状況)の原因となる。

2次元配列(多次元配列)

  • 型 名前[要素数][要素数]; のように宣言すると、2次元の配列になる。
{
   const int MATRIX_SIZE = 10;
   int multipleChart[MATRIX_SIZE][MATRIX_SIZE];

   // 九九の表を作る
   for(int i = 1; i < MATRIX_SIZE; i++) {
       for(int j = 1; j < MATRIX_SIZE; j++) {
           multipleChart[i][j] = i * j;
       }
   }
}
  1 2 3 4 5 6 7 8 9
1 1 2 3 4 5 6 7 8 9
2 2 4 6 8 10 12 14 16 18
3 3 6 9 12 15 18 21 24 27
4 4 8 12 16 20 24 28 32 36
5 5 10 15 20 25 30 35 40 45
6 6 12 18 24 30 36 42 48 54
7 7 14 21 28 35 42 49 56 63
8 8 16 24 32 40 48 56 64 72
9 9 18 27 36 45 54 63 72 81
  • 型 名前[要素数][要素数][要素数]; のように2次元以上の配列も作成可能だが、必要となるデータ量に留意が必要。たとえば int[1000][1000][1000]の3次元配列では約4GBのメモリが必要。

配列のまとめ

  • 同じ種類のデータの集合を扱う場合に使う
  • 必要な要素数が分かっている場合は静的配列が使える
  • プログラムの実行状況によって要素数が変わる場合は動的配列を使う
  • 動的配列は不要になったら delete する
  • 多次元配列も作成可能だが、データ量に気をつける

複数の種類のデータを扱うには?

  • データ処理では、複数の種類のデータを一括で扱いたい場合がある

  • たとえば「郵便番号」

    • 「都道府県名、市区町村名、町域名」が紐づく
    • ただし同じ郵便番号が複数の町域に割り当てられている場合がある
郵便番号 都道府県名 市区町村名 町域名
1120001 東京都 文京区 白山
1120002 東京都 文京区 小石川
1120003 東京都 文京区 春日
1120004 東京都 文京区 後楽
1120005 東京都 文京区 水道
1120006 東京都 文京区 小日向
1120011 東京都 文京区 千石
9996837 山形県 酒田市 金谷
9996837 山形県 酒田市 新町
  • これを配列で管理しようとすると…
    • zip[n] = 1120001;
    • pref[n] = “東京都”;
    • city[n] = “文京区”;
    • area[n] = “後楽”;

構造体 (struct) を使えば、複数の種類のデータをまとめて扱える

  • 構造体は “struct 名前”で宣言、定義する
  • まとめて扱うデータ型と変数名を { } の中に列挙する。これをメンバまたはメンバ変数という。
struct 名前{
   型 名前;
   型 名前;
   ……
   ……
};
  • 先ほどの郵便番号の情報を構造体にすると?
// 構造体 SZipCode の型宣言
struct SZipCode {
   int Zipcode;
   String Pref;
   String City;
   String Area;
};

構造体の使用例

// 構造体 zipCode の型を定義
struct SZipCode{
   int Zipcode;
   String Pref;
   String City;
   String Area;
};

// 変数の作成
SZipCode data;

// 値の代入1
data.Zipcode=1120004;
data.Pref=L"東京都";
data.City=L"文京区";
data.Area=L"後楽";

// 値の代入2
data = { 1120004, L"東京都", L"文京区", L"後楽" };

// 値の参照
Memo1->Lines->Insert(0, L"郵便番号:"    + data.Zipcode );
Memo1->Lines->Insert(0, L"都道府県:"    + data.Pref );
Memo1->Lines->Insert(0, L"市区町村名:" + data.City );
Memo1->Lines->Insert(0, L"町域名:"       + data.Area );

配列で構造体を使う

// 構造体 zipCode の型を定義
struct SZipCode{
   int Zipcode;
   String Pref;
   String City;
   String Area;
};

// 構造体の配列変数を作成 
SZipCode *dataArray;
dataArray = new SZipCode[100];

// 値の代入
dataArray[0] = { 1120004, L"東京都", L"文京区", L"後楽" };

// 値の参照
Memo1->Lines->Insert(0, L"郵便番号:"    + dataArray[0].Zipcode );
Memo1->Lines->Insert(0, L"都道府県:"    + dataArray[0].Pref );
Memo1->Lines->Insert(0, L"市区町村名:" + dataArray[0].City );
Memo1->Lines->Insert(0, L"町域名:"       + dataArray[0].Area );

// 不要になったら削除
delete[] dataArray;

関数とのやりとりに構造体を使うと…

// SZipCode を引数にとる関数の例
void doPrint( SZipCode: &value ) {
   // 値の参照
   Memo1->Lines->Insert(0, L"郵便番号:"    + value.Zipcode );
   Memo1->Lines->Insert(0, L"都道府県:"    + value.Pref );
   Memo1->Lines->Insert(0, L"市区町村名:" + value.City );
   Memo1->Lines->Insert(0, L"町域名:"       + value.Area );
}

// 変数の作成
SZipCode data;

// 値の代入
data = { 1120004, L"東京都", L"文京区", L"後楽" };

// 関数呼び出し(構造体)
doPrint( data );

構造体を使わないと……

// パラメータを個別に渡す関数の例
void doPrint( int Zipcode, String Pref, City, Area ) {
   // 値の参照
   Memo1->Lines->Insert(0, L"郵便番号:"    + Zipcode );
   Memo1->Lines->Insert(0, L"都道府県:"    + Pref );
   Memo1->Lines->Insert(0, L"市区町村名:" + City );
   Memo1->Lines->Insert(0, L"町域名:"       + Area );
}

// 変数の作成
SZipCode data;

// 値の代入
data = { 1120004, L"東京都", L"文京区", L"後楽" };

// 関数呼び出し(個別パラメータ指定)
doPrint( data.Zipcode, data.Pref, data.City, data.Area );

構造体の中に構造体を入れる

  • 構造体の中で指定する型は任意の型を指定できる
  • 事前に宣言定義された構造体を別の構造体で用いることもできる
// 郵便番号に対応する住所情報の定義
struct SZipAddress {
   String Pref;
   String City;
   String Area;
};

struct SZipCode{
   int Zipcode;

   SZipAddress ZipJPN; // 住所情報の日本語標記
   SZipAddress ZipENG; // 住所情報の英語語標記
};

構造体のまとめ

  • 複数の種類のデータをまとめて扱える
  • 構造体を配列にすることもできる
  • 構造体は参照渡しで扱う
  • 構造体のメンバの型に制限はなく、別の構造体を使うこともできる

セミナー動画

チュートリアルセミナー視聴ページにて、シーズン2の放送分を視聴できるようにしています。チュートリアルシリーズに申し込み済みの方はメールにてご案内済みの視聴ページよりご視聴ください。まだ申し込んでいない方は下記アドレスよりお申込みいただくと視聴できます。

http://forms.embarcadero.com/starter-tutorial-webinar

また、シーズン1の放送分は youtube の下記ページよりご視聴いただけます。

https://www.youtube.com/playlist?list=PLoQxxVNY10oEmNSjiPdYPV_E9IL9ipico

シーズン1のまとめ記事もございますので、こちらも併せてお読みください。

https://community.embarcadero.com/blogs/entry/delphi-c-builder-starter-japan

■次回のC++Builderパートは2月27日(月) 17:30頃より、“文字列とオブジェクト„をお送りいたします。

 


About
Gold User, No rank,
Sales Consultant at Embarcadero Technologies, in Japan.

Comments

Check out more tips and tricks in this development video: