Comments
Description
Transcript
malloc
C言語 ポインタ H108102 濱口 賢人 H108137 山崎 貴英 H108139 山下 佳隆 1 ポインタとは 2 ポインタって何? • 変数がメモリ上のどこ(何番地)にあるの かという情報がアドレス • そのアドレスを格納するための変数が ポインタ 3 実体とアドレス 宣言 実体 アドレス int x; x &x ポインタ int* x; 変数 int *x; *x x ふつうの 変数 よくわかるC言語 P.79より 4 #include <stdio.h> int main(void) { int a = 100; int* p = &a; double b = 0.25f; double* q = &b; printf(“pのアドレスは%x、実体の値は%d\n”, p, *p); printf(“qのアドレスは%x、実体の値は%f\n”, q, *q); return 0; } 5 メモリ ※1マス = 1バイト 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 番地 6 変数の宣言 int a = 100; int型 : 4バイト a 1 2 3 4 5 6 7 8 9 100 10 11 12 13 14 15 16 番地 変数は値を代入するもの 7 ポインタの宣言 p int* p = &a; p 1 2 a 9 番地 3 4 5 6 7 8 9 a 100 100 10 11 12 13 14 15 16 番地 ポインタは番地を格納するもの 8 変数の宣言 double b = 0.25f; double型 : 8バイト b 1 2 3 4 5 6 7 0.25 8 9 10 11 12 13 14 15 16 番地 9 変数の宣言 q double* q = &b; q 1 2 b 8 番地 3 4 5 b 0.25 6 7 0.25 8 9 10 11 12 13 14 15 16 番地 ポインタの大きさは型に依存しない 10 動的なメモリの確保 11 malloc関数 キャスト(型変換) sizeof演算子 sizeof(データ型) void*型をint*型に変換 型の大きさを求める int* p = (int *) malloc (sizeof(int)); malloc関数 void *malloc(size_t size) sizeバイトのメモリを確保 戻り値はvoid*型 12 realloc関数 p = (int *) realloc (p, sizeof(int)*2); • 一度確保した領域を再確保する その場合、元の領域の内容がコピーされる • 第一引数: void* • あらかじめ確保した領域を指すポインタ • 第二引数: size_t • 確保する領域の大きさ 13 calloc関数 int* p = (int *) calloc (3, sizeof(int)); • sizeバイトの領域をn個割り当てる 確保された領域はゼロで初期化される • 第一引数: size_t (n) • 確保する個数 • 第二引数: size_t (size) • 確保する領域の大きさ 14 どうしてキャストするの? void* p = malloc (sizeof(int)); • voidポインタは指し示すことは出来るが 実体に対して値の代入が出来ない (何型の変数を指しているか分からないため) *p = 100; × • 目的の型のポインタにキャストして使 おう 15 free関数 free(p); • mallocで確保された領域は、プログラム が終了しても解放されないことがある • メモリ上に管理者不在の領域が出来て しまう • 確保と逆の順で開放しよう 16 #include <stdio.h> #include <stdlib.h> int main(void) { int* p = (int *)malloc(sizeof(int)*3); *p = 2010; *(p+1) = 10; *(p+2) = 7; printf(“%4d/%2d/%2d¥n”, *p, *(p+1), *(p+2)); free(p); p=NULL; return 0; } 17 malloc int *p = (int *) malloc (sizeof(int)*3); p 10 20 15 20 25 30 35 40 45 番地 intの3倍 → 12バイトが確保された 18 malloc *p = 2010; p 10 20 2010 15 20 25 30 35 40 45 番地 pはint*型 → 4バイトを使って代入 19 malloc p+1 = 24 番地 *(p+1) = 10; p 10 20 2010 15 20 10 25 30 35 40 45 番地 pはint*型 → 4バイト先を見る 20 malloc p+2 = 28 番地 *(p+2) = 7; p 10 20 2010 15 20 10 7 25 30 35 40 45 番地 21 malloc free(p); p 10 20 2010 15 20 10 7 25 30 35 40 45 番地 不要になったメモリを解放 22 malloc p p=NULL; × p 20 NULL 10 15 20 25 30 35 40 45 番地 使わなくなったらNULLを代入 23 どうしてNULLを代入するの? • メモリの二重解放を防ぐため • 発見するのが難しいバグになりがち • うっかりミスをなくすための記述 24 #include <stdio.h> 実行結果 int main(void) { int *p1 = (int*)malloc(sizeof(int)); *p1 = 100; printf("p1のアドレスは%xです¥n", p1); 100080が解放されました free(p1); p2のアドレスは100080です printf("%xが解放されました¥n",p1); 100080が解放されました int *p2 = (int*)malloc(sizeof(int)); printf("p2のアドレスは%xです¥n", p2); free(p1); // 二重解放 printf("%xが解放されました¥n",p1); printf("p2の値は…%d¥n", *p2); free(p2); return 0; p1のアドレスは100080です p2の値は…100 間接的にp2が 開放されている p1 p2 } 25 100080番地 ? どうしてNULLを代入するの? • 解放された100080番地は 他のスレッドに確保されてしまうかも • p2を開放してる訳ではないため、 バグに気づきにくい • ポインタにNULLを代入しておけば このようなバグを回避可能 26 #include <stdio.h> 改善例 int main(void) { int *p1 = (int*)malloc(sizeof(int)); *p1 = 100; printf("p1のアドレスは%xです¥n", p1); free(p1); printf("%xが解放されました¥n",p1); p1=NULL; 実行結果 p1のアドレスは100080です 100080が解放されました p2のアドレスは100080です int *p2 = (int*)malloc(sizeof(int)); printf("p2のアドレスは%xです¥n", p2); free(p1); // ここで二重開放は起こらない printf("%xが解放されました¥n",p1); printf("p2の値は…%d¥n", *p2); free(p2); p2=NULL; return 0; } 0が解放されました p2の値は…100 free関数にNULL(0)を 渡しても何も起こらない 27 配列とポインタ 28 静的確保と動的確保の違いは? • 静的確保 • 宣言の時点でメモリを確保し、プログラム 終了と同時に解放される 例) int a[10]; • 動的確保 • プログラムの途中でメモリを確保し、 free関数で任意のタイミングで解放できる 例) int *a = (int *)malloc(sizeof(int)*10); 29 配列とポインタの関係 • ポインタと配列は実は同じもの • 配列aがあるとき、aはa[0]のポインタ • 配列は連続した領域を型で区切る • 添字によってポインタを進めている 30 int a[5]; a[0] 10 15 20 a[1] a[2] 25 30 a[3] a[4] 35 40 45 番地 a int* p = (int *)malloc(sizeof(int)*5); *p 10 15 20 25 30 35 40 45 番地 p 31 動的確保のメリット • メモリの最大量を固定する必要が 無い • 必要な量だけメモリを確保する ため、メモリの無駄遣いを防ぐ 32 構造体とポインタ 33 構造体とは • 複数のデータ型をひとまとめにしたもの • 構造体の要素のことをメンバという • メンバにはドット演算子を使ってアクセ スする • student.id = 109001; 34 構造体変数の宣言 struct student { int id; char name[10]; }; struct student s; s s.id s.name 10 15 20 25 30 35 40 45 番地 メンバとなる変数がそれぞれ確保される 35 メンバの参照 • ドット演算子 • student.name • 構造体のメンバを参照 • アロー演算子 • student->name • ポインタから構造体のメンバを参照 • (*student).nameと同義 36 構造体とポインタ struct student { int id; char name[10]; }; p 10 s s.id s.name 24 15 s p 20 25 30 35 40 45 番地 s.id, (*p).id, p->idは同じものを指す 37 課題 38 課題1 • strchr関数を作ってみよう • 探す対象の文字列 (charの配列) と 探す文字(char)を引数として受け取る • 文字が見つかったらその文字のポインタを、 見つからなかったらNULLを返す 39 注意とヒント • 関数内で配列を使ってはダメ • 代わりにポインタを使おう • 文字列の終わりは’¥0’ 40 課題2 • 学生を表す構造体を作り、成績管理ソフ トを作成せよ • 構造体には以下のメンバを持たせること • 名前(charの配列) • 学籍番号(charの配列) • GPA(float) 41 構造体例 typedef struct student { char name[20]; char number[10]; float gpa; } STUDENT; 42 条件1 • 管理は動的配列とすること。 • 静的配列は× • 10人分の入力を受け付ける • 入力が終わったら、入力した情報を 表示する 43 条件2 • 条件1の表示が終わった後に、検索機 能をつける • 名前、学籍番号を入力し、該当する 学生情報を出力する 44 課題3 • 課題2のプログラムを、入力する人数 の制限をなくすよう改良しよう • 名前に特定の文字列が入力されたら、 その時点で入力を終了する • 特定文字列は自由に決めてください 45 ヒント • reallocを使うと楽 • 最初に10人分の領域を確保し、 足りなくなったら20人分の領域を reallocで再確保、と出来る 46 ラシゼミ恒例課題 • 何か作ってみよう(自由課題) • 以下のものを全て使うこと • 構造体を指すポインタ • ポインタを引数として受け取る関数 • テキストファイルかコメント文で どんなプログラムか簡単に説明を付けて • 無責任ってレベルじゃねーぞ! 47 評価方法 • 課題1, 2, 3の完成度で判断 • 自由課題は加点(課題が終わってから) 48 提出について • 課題ごとに別々のファイルに保存 • .cファイルをメールに添付して提出 • 次回のレクチャまでに • 宛先: [email protected] • 絶対になくすなよ!削除するなよ! 49 勉強におすすめ • Cのドリル (Workbook in Programming) • C言語ポインタ完全制覇 瀬戸 遥 (著) (標準プログラマーズライブラリ) 前橋 和弥 (著) • Cの絵本 C言語が好きになる9つの扉 (株)アンク (著) • よくわかるC言語 —イメージと例題で理解する 長谷川 聡 (著) 50