PPT

情報処理Ⅱ
第10回
2004年12月14日(火)
本日学ぶこと①

いくつかの型



なぜ「型」にこだわるか?



typedef
列挙型(enum型)
プログラムを書きやすく
読みやすくする…定性的
なメリット
実行時のコスト(メモリ使用量な
ど)を見積もることができ,そこか
らプログラムの改善が期待できる
…定量的なメリット
処理対象のデータを効果的・効率的に取り扱える
既存の機能をもとに,新たに便利な機能を創れる
以下を読む上での注意


この色とこの色は型名を表す.
この色は変数名を表す.
2
typedef


独自に型(type)を定義(define)できる.
例





typedef signed char schar;
schar c; ⇒ signed char c; と同じ
typedef unsigned char uchar;
uchar c, *p; ⇒ unsigned char c, *p; と同じ
typedef char *String;
String s, t; ⇒ char *s, *t; と同じ
typedef char c5[5];
c5 x, y, z; ⇒ char x[5], y[5], z[5]; と同じ
構造体の型名定義によく用いられる.
3
typedefの考え方

変数の定義


char c5[5]; は,変数c5を定義し,その型はchar [5]型
とする.
型の定義

typedef char c5[5]; は,c5型を定義し,その型は
char [5]型と同一とみなす.
4
列挙型(enumeration)とは

「ラベル」を格納・参照・比較したいときに使う.



例


13
変数に格納される具体的な値には関心がない.
実際には,ラベルに整数値が割り当てられる.これを積極的に
活用することもある.
0
1
enum Boolean {FALSE, TRUE};
enum Boolean p = TRUE, q = FALSE, r = p||q;
enum month {Jan = 1, Feb, Mar, ..., Dec,
MONTH_END} mloop; int rain[100][MONTH_END];
for (mloop = Jan; mloop < MONTH_END;
mloop++) { rain[4][mloop] = ...; }
年月単位の降水量を格納する
5
汎整数型


整数型(char~long)と列挙型を合わせて,汎整数型
(integral type)という.
列挙型は,計算時にはint型とみなされる.

ただし,利用は代入,比較,範囲内の増分・減分にとどめておく.
6
独自の型はどこで定義する?


一般には,グローバル区間で定義し,複数の関数間で利用
できるようにする.
ブロックの中で定義してもよい.

有効範囲は,そのブロックの終わりまで.
7
まとめ

typedefと列挙型を活用することで,各変数の型がより明
確になる.
8
本日学ぶこと②


構造体
問題

「レジ(cache register)」ができる?
• 「500円,100円,100円,100円,50円,10円」で合計いく
ら?
• 「2,941円のお釣り」として,いくらの紙幣・硬貨を何枚出せ
ばいい?
• 650円の勘定に,「1000円,100円,50円」を出してきたら,
いくら返す?
• 650円の勘定に,「1000円,500円」を出してきたら?
9
構造体(structure)


複数のオブジェクトを取りまとめて一つのオブジェクトとして
扱うための機構
構造体で表現するとよいもの
(x,y)





(2次元,3次元などの)座標上の点
複素数
行列(行数と列数と各成分)
所持金
学生情報
500
100
100
100
50
10
学生番号:0001
氏名:あいうえお
情処Ⅰの点数:80
情処Ⅱの点数:75
修得単位数: 52
10
構造体の宣言と定義

構文: struct タグ名 { メンバ宣言子並び };


「struct タグ名」型が定義される.
例

struct point {
この x と y を,
double x;
point構造体の
double y;
メンバという.
};
p:
struct point p;
x=1
p.x = 1; p.y = -1;
セミコロンを忘れない
ように
•
•



y = -1
11
構造体の宣言と定義の例(1)

構造体宣言と変数の定義を同時に行う.

struct point {
double x;
double y;
} p;
•
•


構造体宣言と型の定義を同時に行う.

typedef struct point {
double x, y;
} point;
struct point型と
point p;
•


point型が定義される
12
構造体の宣言と定義の例(2)

行列(行数,列数とも10まで)

struct matrix {
int row, column;
double element[10][10];
};
•
•


row
column
element
…
行列(成分はポインタ)

struct matrix {
int row, column;
double **element;
};
row
•
column
•

element
13
構造体の宣言と定義の例(3)

自己参照構造体

struct node {
graph
int value;
struct node *next;
value = 1
};
next
struct node graph[100];
graph[0].value = 1;
graph[0].next = graph + 1;
graph[1].value = 99;
graph[1].next = NULL;
•
•




value = 99
…
next = NULL
すでに定義されている型で
あれば,それを
メンバの型に用いてもよい.
14
構造体の参照と代入(1)

前提

struct point {
double x, y;
};
struct point p, *pp = &p;
•



メンバへのアクセス方法


× *pp.x
構造体オブジェクトpのメンバxは p.x と書いて
参照・代入ができる.
構造体オブジェクトへのポインタppについては,(*pp).x も
しくは pp->x と書けばよい.
• . と -> は演算子であり,最上位の優先順位を持つ.
15
構造体の参照と代入(2)

構造体オブジェクトの代入




struct matrix a, b; (aを初期化) b = a; とすると,
bにはaのコピーが格納される.
構造体の中に配列があっても,コピーされる.
構造体の中のポインタは,ポインタ値がコピーされる.
構造体渡し


関数の引数や戻り値の授受でも,構造体オブジェクトが代入
(コピー)される.
しかし,構造体はしばしばポインタ渡しにされる.
• 関数処理で,構造体オブジェクトの中の値を変えたいとき
• 大きなサイズの構造体オブジェクトをスタックに置きたくない
とき
16
sizeof(構造体)

sizeof(構造体) は,構造体の各メンバのサイズの合計
以上になる.

構造体オブジェクトの中で使用されない領域もあり得る.「パ
ディング」という.
s:
c
パディング
i
17
所持金問題の仕様(1)


「所持金(pocket)」をひとつの実体として,構造体を定義
する.
所持金に関する情報



一万円,五千円,千円の各紙幣(bill)の枚数
500円,100円,50円,10円,5円,1円の各硬貨(coin)の
枚数
所持金の総額(amount) ⇒ 構造体のメンバ?
500
50
100
5
10
1
18
所持金問題の仕様(2)

所持金に関して知りたい情報,行いたい操作




紙幣や硬貨がそれぞれ何枚あるかを知る
紙幣や硬貨の所持枚数を指定する
• 「特定の紙幣や硬貨を0枚にする」を含む
支払ったときのお釣り(change)を求める
• 支払えないときは,何らかのエラー情報を返す
両替する ⇒ 授業では扱わない
• 100円玉45枚を,(可能な限り)千円札に
• 千円札1枚を,100円玉に
19
所持金問題のプログラミング(1)

所持金構造体

struct pocket {
int bill_10000, bill_5000, bill_1000;
int coin_500, coin_100, coin_50, coin_10,
coin_5, coin_1;
};
•
•

20
所持金問題のプログラミング(2)

定義した関数




紙幣や硬貨の枚数を指定する
• set_bill, set_coin, set_pocket
• replace_pocket, create_pocket
所持金の総額を求める
• calc_amount
紙幣や硬貨がそれぞれ何枚あるかを知る
• 個々の値は,メンバに直接アクセス
• 全体の出力は,print_pocket
支払ったときのお釣りを求める
• calc_change
「calc」は
「calculate」の略
21
関数calc_changeの仕様
請求書
500
50
10
payment = 457 円也
poc_from
calc_change
poc_return
100
1
1
1
22
関数定義の注意点

所持金を操作する各関数について,第1引数は所持金構造
体のポインタとし,戻り値もその値としている.


メンバ名と変数名は,別々に(異なる名前空間で)管理される
識別子なので,重複してもよい.



戻り値をvoidにする(値を返さない)流儀もある.
関数set_bill, 関数set_coinを参照.
calc_change関数の処理中に「お釣りが支払えない」とわ
かったら,その時点で処理を終えてNULLを返す.
create_pocket関数の戻り値はポインタ
偽と評価される
ではなく構造体オブジェクトとして,構造体渡し ポインタ値
としなければならない.
23
構造体渡しでしてはいけないこと

× 関数内のauto変数のポインタ値を戻り値とする.

例
• struct pocket *create_pocket(int amount)
{
•
–
–
–
struct pocket poc_object;
replace_pocket(&poc_object, amount);
return &poc_object;
}
コンパイルエラーにはならないが,実行時に支障をきたす.
関数処理が終わると,poc_objectの領域が破棄される
⇒ *(&poc_object)が不定!
•


24
構造体とプログラミング

さまざまな種類の情報を持つ「実体」を一つのオブジェクトと
して処理したいとき,


まず,「何に処理をしたいか?」を考える.
• そのオブジェクトの型を構造体で定義する.
• 座標上の点,学生情報,ノード(節点),など.
次に,「何の処理をしたいか?」を考える.
• 構造体を引数にとって処理する関数を定義する.
• 引数・戻り値ともに,型は構造体そのものにすべきか,構造
体のポインタにすべきか,そのつど検討する.
25
まとめ


構造体により,複数のデータからなる「システム」を設計する
ことができる.
構造体の中身を操作するには,ポインタ渡しの関数を定義し
呼び出すとよい.
26
来週学ぶこと…プログラミングの例題3つ

行列式の計算


「ぷよ連結問題」の再帰的解法


学ぶこと:配列を含む構造体の利用,再帰呼び出し
学ぶこと:再帰呼び出し,多次元配列を引数とする関数
「ぷよ連結問題」の非再帰的解法

学ぶこと:構造体の利用,配列の写像化
1
2
3
4
5
6
7
8
9
=0
27