struct タグ名 {
型 メンバ 1;
型 メンバ 2;
型 メンバ 3;
・・・
型 メンバ N;
} 変数名;
struct AddressRecord {
char name[40];
short postal_code1;
short postal_code2;
char address[80];
char phone;
} card;
* struct AddressRecord は,int などと同じ型名であって変数ではない.
struct AddressRecord card;
ではなくて,
AddressRecord card;
でよく,typedef を使う必要もなくなった.
struct AddressRecord { // 新しい「型」の宣言だけしている.
char name[40]; // ヘッダファイルなどで宣言されることが多い
short postal_code1;
short postal_code2;
char address[80];
char phone_number[20];
};
は,ヘッダファイル(.h)などで宣言されることが多い.Cの場合> struct AddressRecord card; // cardという変数を宣言している. C++の場合> AddressRecord card;は,ソースコード(.cpp)の中で行われる.
| name 40バイト |
| postal_code1 2バイト |
| postal_code2 2バイト |
| address 80バイト |
| phone_number 20バイト |
card.postal_code1 = 214;
card.postal_code2 = 8571;
strcpy(card.name,"理工太郎");
strcpy(card.address,"川崎市多摩区東三田1-1-1");
strcpy(card.phone_number,"044-934-xxxx");
その他の変数の要素へのアクセス方法は,一般の変数と同じである.
scanf("%d",&card.postal_code1);
for(int i=0; card.name[i], i++)
printf("%c",card.name[i]);
struct AddressRecord card[100];
を宣言すればよい.このそれぞれの要素にアクセスするには,
card[i].postal_code1 = 101;
card[i].postal_code2 = 8301;
strcpy(card[i].name,"明治二郎");
strcpy(card[i].address,"東京都千代田区神田駿河台1-1");
strcpy(card[i].phone_number,"03-3296-xxxx");
などとすれば良い.
struct AddressRecord card[100]; struct AddressRecord *cardp = card;このポインタにアクセスするには,アロー演算子を用いる.
cardp->postal_code1 = 102;
cardp++; // ポインタをインクリメント(次のカードへ行く)
型 名前:サイズ;例えば以下の例は,学生の年組番号と出欠を2バイトで表した例である.
struct BitF {
unsigned grade: 3; // 1-4年生(最大8通り)
unsigned class: 4; // 5/6組 (最大16通り)
unsigned number:8; // 1-99番(最大256通り)
unsigned shusseki:1;// 1:出席,0:欠席
};
通常なら少なくとも4バイトは使っている(intを使えば16バイト)ところを,
非常にコンパクトに表すことができる.
ビットフィールドは更に,機械のスイットのOn/OFFなどを制御するのにも非常に役立つ.
union タグ名 {
型 メンバ1;
型 メンバ2;
型 メンバ3;
・・・
型 メンバN;
} 変数;
例えば以下のように用いる.
union SharedMemory {
long int li;
short s[2];
float f;
char c[4];
};
union SharedMemory shm;
この変数 shm は,サイズが4バイトであり,その同じ領域を,longとしても floatとしても,
また,shortを2つ,あるいはcharを4つとして利用することができる.
要素へのアクセス方法は、構造体と同じである。課題は,数多くのトレーニングを積むことで,個人の実力を養成する目的で出しているものです.その趣旨を理解し「自分の為」になるよう適切に実施しなさい.


- 成績レコード型の構造体を作成.main関数の中でインスタンス( =実体のこと.ここではdata[]) を作成して初期化している.構造体もこのように初期化できる.

- 成績リスト構造体とインスタンスの配列(grade_list[])も作って成績(A-F)と得点のマッピングを決めている. こうすることで,コードの中の if文 には直接数値を書かなくて良くなり, 後からマッピングの関係を変更することも容易になる(16-27行および41-42行).
- 構造体の配列の中身を初期化することもできる(21-27行)
- CSVファイルを書き出す関数と,画面に表示する関数は非常に似ていることがわかる. コードを上手に書くと,この2つの関数を一つに集約することもできる(11章参照). 是非トライしてみて欲しい.
// main関数の例
int main(void)
{
AddressBook data[] = {
"Aoyama Taro", "Tokyo", 20, 'M',
"Kawasaki Jiro", "Kanagawa", 21, 'M',
"Morioka Miki", "Iwate", 22, 'F',
"Kawagoe Shiro", "Saitama", 23, 'M',
"Narashino Goro", "Chiba", 24, 'M',
NULL, NULL, 0, 0};
print_data(data);
}

- アドレス帳の構造体を作成,main関数でインスタンス生成と初期化を行なっている. 実際はこの様なレコードデータはファイルにしておき(前の課題のCSVファイルなどがそれに当たる), ファイルから読み込むのが良い.
- レコードデータの最後に名前の無いダミーデータを挿入して,これをデータの終わりとして判断させている. ちょうど,文字列の最後が0で終わるのと同じ考えである. 【注】 !strcmp(ab[i].name,"") とする代わりに,単に !ab[i].name[0] でもよい (なぜなら空の文字列の場合,先頭には0が入っているからである).

- 前の課題12-3に手を加えて作成している.
- 名前や誕生地で検索できるようにし,対応するデータがなければ繰り返し尋ねるようにしている.
- ただし,このプログラムでは,同じ名前や同じ誕生地の人がいた場合,最初の人しか検索できない. プログラムを工夫して,同じ名前や同じ誕生地の人すべてが検索結果として表示されるようにしてみよ.
#include <stdio.h>
#include <stdlib.h>
struct point_t{
//ここに記述
};
void disp(point_t p);
void initialize(point_t *cloud, int n);
point_t calc_centroid(point_t *cloud, int n);
void shift(point_t *cloud, int n, double dx, double dy);
int main(int argc, const char * argv[])
{
const int N = 100;
point_t cloud[N]; // 全点群(ポイントクラウドといいます
// を格納する point_t型変数の配列
initialize(cloud,N); // 好きな値に初期化します
point_t centroid = calc_centroid(cloud,N);
// ここで全点群の重心を計算し結果を返します。
disp(centroid); // 座標を表示します。
shift(cloud, N, 2, 3); // 全点の座標を移動します。
centroid = calc_centroid(cloud,N);
disp(centroid);
return 0;
}
【実行例】
> 12-5
( 0.0257288, 0.0266681)
( 2.0257288, 3.0266681)

作成したプログラムを