情報処理・演習II --- 第7回: 構造体へのポインタ --情報科学科 井口幸洋 [email protected] 2015/10/1 情報処理・演習II 1 構造体を指すポインタの宣言と使用法 変数 p と q は,struct complex型 struct complex { を指す. つまり, struct complex型 double re, im; が入っているアドレスを入れる為 }; の変数. struct complex *p, *q; p q 100番地 144.5 100 200番地 200 2015/10/1 re re 76.5 情報処理・演習II im 3.14 im 43.2 2 ??? p = q; を行うとどうなりますか? 実行前 p q 100番地 144.5 100 200番地 200 2015/10/1 re re 76.5 情報処理・演習II im 3.14 im 43.2 3 答え p = q; /* q の値200をpに入れるのだから p の値は200に なる. これは, p が q と同じ所を指し示しているこ とを表している */ p q 100番地 144.5 200 200番地 200 2015/10/1 re re 76.5 情報処理・演習II im 実行後 3.14 im 43.2 4 ??? *p = *q; を行うとどうなりますか? 実行前 p q 100番地 144.5 100 200番地 200 2015/10/1 re re 76.5 情報処理・演習II im 3.14 im 43.2 5 答え *p = *q; /* q が指し示している内容を p が指し示している所 に代入するという意味である. 構造体丸ごとのコピー が行われる. */ p q 100番地 76.5 100 200番地 200 2015/10/1 re re 76.5 情報処理・演習II im 実行後 43.2 im 43.2 6 ??? • qが指し示すreal partを,pが指し示すreal partに, qが指し示すimaginary partの符号を反転したも のを, pが指し示すimaginary partに代入したい. • どう書けば良いか? 実行前 im 100番地 re p 100 144.5 3.14 q 200番地 200 2015/10/1 re 76.5 情報処理・演習II im 43.2 7 ??? • qが指し示すreal partを,pが指し示すreal partに, • qが指し示すimaginary partの符号を反転したも のを, pが指し示すimaginary partに代入したい. • どう書けば良いか? 実行後 im 100番地 re p 100 76.5 -43.2 q 200番地 200 2015/10/1 re 76.5 情報処理・演習II im 43.2 8 答え (*p).re = (*q).re; (*p).im = -(*q).im; 注意: *p.re と書くと *(p.re) というように解釈されてしまう! p q 100番地 76.5 100 200番地 200 2015/10/1 re re 76.5 情報処理・演習II im 実行後 -43.2 im 43.2 9 答え(矢印演算子を使った書き方:推 奨) (*p).re = (*q).re; (*p).im = -(*q).im; p -> re = q -> re; p -> im = -(q -> im); 2015/10/1 情報処理・演習II 10 自己参照構造体 • 今日のポイント! • 構造体の中に同じ構造体を指すポインタをメン バーの一つとして持たせた構造体を言う. • これを使うと リスト構造, ツリー構造などの有 用なデータ構造(データを覚える構造:これが優 れたものであると効率アップが望める.)を作れる. 2015/10/1 情報処理・演習II 11 リスト構造とは • 次のようなときに威力を発揮! – 1万人のデータが入っている名簿. – 会員番号順のデータ. – 配列で実現可能だが,次のような場合ちょっとこまる. • 退会する人のデータ削除:削除するデータの後ろのデータを一つづつ 前に詰める操作が必要.×印をつける方法もある. • データを途中に追加:それ以後のデータを1つづつずらさなければなら ない.例えば5番目に新しいデータを挿入する時、それ以後の9996 人分のデータを1個づつずらす必要あり. • 配列を最初に2万人分用意したとする.2万人以上にデータが増えたら 使えない.プログラムを書き換える必要あり. 2015/10/1 情報処理・演習II 12 リスト構造を使ったデータの集まりの実現 • 一連のデータ 2, 3, 5, 7 をリスト構造で表現. 番地が入る head 50 300 5 50 2 100 100 3 2015/10/1 300 情報処理・演習II 200 200 7 0 0は終了 を示す. NULL. 13 リスト構造をC言語で実現 • 一つのセルを表すデータを構造体で表せばよい. • セルには,格納したいデータと次のデータの格 納アドレスを記憶させればよい. head 50 300 5 50 2 100 100 3 2015/10/1 200 200 7 0 300 情報処理・演習II 14 リスト構造をC言語で実現 struct cell { int value; struct cell *next; }; 2015/10/1 情報処理・演習II 5 200 15 リストの走査 • リストを先頭 から順に調べ る操作 head 50 50 2 void print_list(struct cell *p) { while (p != NULL) { printf("%3d ", p -> value); p = p -> next; } printf("\n"); } p 100 100 3 2015/10/1 300 5 300 情報処理・演習II print_list(head); 200 200 0 7 16 リストの走査 実行例(1) void print_list(struct cell *p) { while (p != NULL) { printf("%3d ", p -> value); p = p -> next; } printf("\n"); } struct cell *head; ... print_list(head); head 50 p 50 50 2 100 100 3 2015/10/1 300 5 300 情報処理・演習II 200 200 7 0 17 リストの走査 実行例(2) void print_list(struct cell *p) { while (p != NULL) { printf("%3d ", p -> value); p = p -> next; } printf("\n"); } struct cell *head; ... print_list(head); head 50 p 100 50 2 100 100 3 2015/10/1 300 5 300 情報処理・演習II 200 200 7 0 18 リストの走査 実行例(3) void print_list(struct cell *p) { while (p != NULL) { printf("%3d ", p -> value); p = p -> next; } printf("\n"); } struct cell *head; ... print_list(head); head 50 p 100 50 2 100 100 3 2015/10/1 300 5 300 情報処理・演習II 200 200 7 0 19 リストの走査 実行例(4) void print_list(struct cell *p) { while (p != NULL) { printf("%3d ", p -> value); p = p -> next; } printf("\n"); } struct cell *head; ... print_list(head); head 50 p 300 50 2 100 100 3 2015/10/1 300 5 300 情報処理・演習II 200 200 7 0 20 リストの走査 実行例(5) void print_list(struct cell *p) { while (p != NULL) { printf("%3d ", p -> value); p = p -> next; } printf("\n"); } struct cell *head; ... print_list(head); head 50 p 300 50 2 100 100 3 2015/10/1 300 5 300 情報処理・演習II 200 200 7 0 21 リストの走査 実行例(6) void print_list(struct cell *p) { while (p != NULL) { printf("%3d ", p -> value); p = p -> next; } printf("\n"); } struct cell *head; ... print_list(head); head 50 p 200 50 2 100 100 3 2015/10/1 300 5 300 情報処理・演習II 200 200 7 0 22 リストの走査 実行例(7) void print_list(struct cell *p) { while (p != NULL) { printf("%3d ", p -> value); p = p -> next; } printf("\n"); } struct cell *head; ... print_list(head); head 50 p 200 50 2 100 100 3 2015/10/1 300 5 300 情報処理・演習II 200 200 7 0 23 リストの走査 実行例(8) struct cell *head; ... print_list(head); head 50 50 p 0 終了を表す 2 void print_list(struct cell *p) { while (p != NULL) { printf("%3d ", p -> value); p = p -> next; } printf("\n"); } 100 100 3 2015/10/1 300 5 300 情報処理・演習II 200 200 7 0 24 リストの走査 実行例(9) void print_list(struct cell *p) { while (p != NULL) { printf("%3d ", p -> value); p = p -> next; } printf("\n"); } struct cell *head; ... print_list(head); head 50 p 0 50 2 100 100 3 2015/10/1 whileの条件にあてはまらないのでループを 抜ける。 300 5 300 情報処理・演習II 200 200 7 0 25 ??? list内に5があるか探す. 関数が返す値を答えなさい. struct cell *head; ... search_list(5, head); head 50 50 2 void search_list(int x, struct cell *p) { while (p != NULL) { if (x == p -> value) return(p); p = p -> next; } return(NULL); } 100 100 3 2015/10/1 300 5 300 情報処理・演習II 200 200 7 0 26 リストの生成:値を読み込みリストに付 け加える.0なら停止. void make_list(void) { struct cell *p; int x; head = NULL; for (;;) { scanf("%d", &x); if (x == 0) break; p=(struct cell *)malloc(sizeof(struct cell)); p -> value = x; p -> next = head; head = p; } } 2015/10/1 情報処理・演習II 27 動的なメモリの確保 • 配列は静的(static)にメモリを確保 – コンパイル時に大きさがきまってしまう • 動的(dynamic)なメモリ確保 – 実行時に必要に応じてメモリを確保 – OSに対してメモリ確保を依頼 2015/10/1 情報処理・演習II 28 動的なメモリの確保 • sizeof(型名) は,その型を格納するために必要 なメモリサイズ(単位はbyte)を返す • malloc(サイズ) は,指定サイズ分のメモリを確 保し,その先頭番地(ポインタ)を返す.確保でき なかった時はNULLを返す. • p=(struct cell *)malloc(sizeof(struct cell)); 右辺先頭の(struct cell *)はキャストといい指定された型 に強制的に型変換させる構文. 2015/10/1 情報処理・演習II 29 ???このプログラムを実行すると入力の逆順にリストが 作成される.その様子を箱を描いてわかりやすく説明せよ void make_list(void) { struct cell *p; int x; head = NULL; for (;;) { scanf("%d", &x); if (x == 0) break; p=(struct cell *)malloc(sizeof(struct cell)); p -> value = x; p -> next = head; head = p; } } 2015/10/1 情報処理・演習II 30 ???解答(1) head = NULL; for (;;) { scanf("%d", &x); if (x == 0) break; p=(struct cell *)malloc(sizeof(struct cell)); p -> value = x; p -> next = head; head = p; } head NULL 2015/10/1 情報処理・演習II 31 ???解答(2) head = NULL; for (;;) { scanf("%d", &x); if (x == 0) break; p=(struct cell *)malloc(sizeof(struct cell)); p -> value = x; p -> next = head; head = p; } head NULL 2015/10/1 2を入力 情報処理・演習II x 2 32 ???解答(3) head = NULL; for (;;) { scanf("%d", &x); if (x == 0) break; p=(struct cell *)malloc(sizeof(struct cell)); p -> value = x; p -> next = head; head = p; } head NULL p 50 x 2 50 2015/10/1 情報処理・演習II 33 ???解答(4) head = NULL; for (;;) { scanf("%d", &x); if (x == 0) break; p=(struct cell *)malloc(sizeof(struct cell)); p -> value = x; p -> next = head; head = p; } head NULL p 50 x 2 50 2 2015/10/1 情報処理・演習II 34 ???解答(5) head = NULL; for (;;) { scanf("%d", &x); if (x == 0) break; p=(struct cell *)malloc(sizeof(struct cell)); p -> value = x; p -> next = head; head = p; } head NULL p 50 x 2 50 2 2015/10/1 NULL 情報処理・演習II 35 ???解答(6) head = NULL; for (;;) { scanf("%d", &x); if (x == 0) break; p=(struct cell *)malloc(sizeof(struct cell)); p -> value = x; p -> next = head; head = p; } p head 50 50 x 2 50 2 2015/10/1 NULL つづきを必ずやってみること! 情報処理・演習II 36 ???解答(1) head = NULL; for (;;) { scanf("%d", &x); if (x == 0) break; p=(struct cell *)malloc(sizeof(struct cell)); p -> value = x; p -> next = head; head = p; } p head NULL 2を入力 50 2 2015/10/1 100 情報処理・演習II 37 課題1 • リストを先頭 から順に調べ て和を求める head 50 50 2 100 100 3 2015/10/1 int sum_list(struct cell *p) { sum = 0; while (p != NULL) { データを足しこむ; p = p -> next; } 合計を返す; } 300 5 300 情報処理・演習II 200 200 7 0 38 課題2 以下のプログラムでは入力と逆順のリストができる。 正順にリストを作成するnew_make_listを作れ. void make_list(struct cell *head) { struct cell *p; int x; head = NULL; for (;;) { scanf("%d", &x); if (x == 0) break; p=(struct cell *)malloc(sizeof(struct cell)); p -> value = x; p -> next = head; head = p; } } 2015/10/1 情報処理・演習II 39 課題2 ヒント void new_make_list(struct cell *head) { struct cell *p, *tail; int x; /* dummy 挿入 */ head=(struct cell *)malloc(sizeof(struct cell)); if(head==NULL){ printf("Can't allocate memory.\n");exit(1); } head->next = NULL; tail = head; head 2015/10/1 tail 50 50 NULL 50 情報処理・演習II 40 課題2 ヒント for (;;) { scanf("%d", &x); if (x == 0) break; p=(struct cell *)malloc(sizeof(struct cell)); if(p==NULL){ printf("Can't allocate memory.\n"); exit(1); } p -> value = x; 箱をかいて図で理解し、 p -> next = ; それからコーディングする tail -> = ; こと! tail = ; } /* ここにdummy削除を書く */ return(head); 2015/10/1 41 情報処理・演習II } 課題3 xを見つけてそのあとにyを挿入 search_listを改良すれば簡単に書ける.ノーヒント. 箱をかいて図で理解し、 それからコーディングする こと! 2015/10/1 情報処理・演習II 42 課題4 リストを逆順にする p head 50 q 100 r 300 50 50 2 100 100 3 2015/10/1 300 5 300 情報処理・演習II 200 200 7 0 43 課題4 リストを逆順にする p head q 100 r 300 50 50 2 50 NULL 100 3 2015/10/1 head -> next = NULL; q->next = p; 300 5 50 情報処理・演習II 200 200 7 0 44 課題4 リストを逆順にする p head 100 q 300 r 200 50 50 2 NULL 100 3 2015/10/1 300 5 50 情報処理・演習II 200 200 7 0 45 課題4 リストを逆順にする p head 100 q 300 r 200 50 50 q->next = p;をやるとどうなりますか? 2 NULL 100 3 2015/10/1 300 5 50 情報処理・演習II 200 200 7 0 46 リスト構造を使った名簿の操作(削除) • 後藤さんがやめたので名簿から削除. head 削除 50 50 飯田 100 300 後藤 200 200 吉澤 0 100 石川 300 2015/10/1 情報処理・演習II 47 リスト構造を使った名簿の操作(削除) • 後藤さんがやめたので名簿から削除. head 50 50 飯田 100 300 後藤 200 100 石川 200 2015/10/1 情報処理・演習II 200 吉澤 0 石川さんからの 矢印を書換える だけでよい. 48 リスト構造を使った名簿の操作(削除) • 後藤さんがやめたので名簿から削除. head ここの部分はそのままでも構わないが、 メモリの無駄にはなる.解決策は後述. 50 50 飯田 100 300 後藤 200 100 石川 200 2015/10/1 情報処理・演習II 200 吉澤 0 石川さんからの 矢印を書換える だけでよい. 49 リスト構造を使った名簿の操作(挿入) • 高橋さんが加入したので名簿に挿入. head 50 200 吉澤 50 0 飯田 100 100 石川 200 2015/10/1 情報処理・演習II 石川さんからの 矢印を書換える だけでよい. 50 リスト構造を使った名簿の操作(挿入) • 高橋さんのデータを入れる場所を確保. head 50 50 500 高橋 --- 200 吉澤 0 飯田 100 100 石川 200 2015/10/1 情報処理・演習II 石川さんからの 矢印を書換える だけでよい. 51 リスト構造を使った名簿 • 会員が飯田,石川,後藤,吉澤のあるクラブがあ る.これをリスト構造で示す. 50 飯田 100 300 後藤 200 100 石川 300 2015/10/1 情報処理・演習II 200 吉澤 0 0は終了 を示す. NULL. 52 文字列のメモリ管理(解決法) • 何行もある文章を扱うプログラム – 一つの大きな配列に、詰め込む. – 一つの行の区切りは0をいれる. – 行の始まりを示す配列 line[]を用意. line[i] には i 行目が格納される番地を入れる. line[0] = 320; line[1] = 325; line[2] = 329; 'a' 'b' 'c' 'd' 0 'p' 'q' 'r' 0 'x' 'y' 'z' 0 320 2015/10/1 324 328 情報処理・演習II 332 336 53 文章を格納するプログラム(1) char buffer[10000]; /* 10000文字格納 */ char *line[500]; /* 500行まで扱う */ int lc; /* line counter */ int cc; /* character counter */ int ch; /* 一文字入力用 */ lc = 0; cc = 0; 2015/10/1 情報処理・演習II 54 文章を格納するプログラム(2) for(;;){ /* 行の繰り返し */ ch = getchar(); if (ch == EOF) break; /* End Of File検出 */ if (lc >= 500) { printf("Too many lines.\n"); exit(1); } line[lc] = &buffer[cc]; /* lc行目の先頭番地 */ lc++; 2015/10/1 情報処理・演習II 55 文章を格納するプログラム(3) for(;;){ /* 文字の繰り返し */ if (ch == '\n') break; /* 改行検出 */ if (cc >= 9999) { printf("Too many characters.\n"); exit(2); } buffer[cc++] = ch; ch = getchar(); } buffer[cc++] = 0; } 2015/10/1 情報処理・演習II 56 リスト構造を使った名簿の実現 • 会員が飯田,石川,後藤,吉澤のあるクラブがあ る.これをリスト構造で示す. head 50 50 飯田 100 300 後藤 200 100 石川 300 2015/10/1 情報処理・演習II 200 吉澤 0 0は終了 を示す. NULL. 57 リスト構造をC言語で実現 head 50 50 飯田 100 300 後藤 200 100 石川 300 2015/10/1 情報処理・演習II 200 吉澤 0 0は終了 を示す. NULL. 58 リスト構造を使った名簿の操作(削除) • 後藤さんがやめたので名簿から削除. head 削除 50 50 飯田 100 300 後藤 200 200 吉澤 0 100 石川 300 2015/10/1 情報処理・演習II 59 リスト構造を使った名簿の操作(削除) • 後藤さんがやめたので名簿から削除. head 50 50 飯田 100 300 後藤 200 100 石川 200 2015/10/1 情報処理・演習II 200 吉澤 0 石川さんからの 矢印を書換える だけでよい. 60 リスト構造を使った名簿の操作(削除) • 後藤さんがやめたので名簿から削除. head ここの部分はそのままでも構わないが、 メモリの無駄にはなる.解決策は後述. 50 50 飯田 100 300 後藤 200 100 石川 200 2015/10/1 情報処理・演習II 200 吉澤 0 石川さんからの 矢印を書換える だけでよい. 61 リスト構造を使った名簿の操作(挿入) • 高橋さんが加入したので名簿に挿入. head 50 200 吉澤 50 0 飯田 100 100 石川 200 2015/10/1 情報処理・演習II 石川さんからの 矢印を書換える だけでよい. 62 リスト構造を使った名簿の操作(挿入) • 高橋さんのデータを入れる場所を確保. head 50 50 500 高橋 --- 200 吉澤 0 飯田 100 100 石川 200 2015/10/1 情報処理・演習II 石川さんからの 矢印を書換える だけでよい. 63 リスト構造を使った名簿 • 会員が飯田,石川,後藤,吉澤のあるクラブがあ る.これをリスト構造で示す. 50 飯田 100 300 後藤 200 100 石川 300 2015/10/1 情報処理・演習II 200 吉澤 0 0は終了 を示す. NULL. 64
© Copyright 2024 ExpyDoc