プログラミング演習II 2003年10月29日(第2,3回) 木村巌 今日やること 配列の復習 ポインタの復習 多倍長整数の配列での実現 足し算と繰り上がり malloc()とfree() キャスト(明示的な型変換) 配列 それぞれの型Tについて、T型の配列が存在 する(void型、「…を返す関数」型を除く): T var[16]; のように宣言する.Tは、整数、浮動小数点数、 文字、などが主な例. 例:int ary[16]; int型の配列.ary[0]からary[15]までの16個 のint型の値を保持できる テキスト7章1節 配列の正体 T型の値を、指定された個数だけ保持でき るメモリの先頭のアドレス(T型のポインタ). 例: int ary[16]; sizeof (ary); /* == sizeof (int) * 16 == 64byte */ 配列は0から始まる 配列は、0から始まる添え字を持つ int ary[16]なら、ary[0]からary[15]まで. ary[16]を読み書きしても(読み書きできて しまう!)、結果は予測できない!! 配列の要素はすべて同じ型 int ary[16]; と宣言すると、ary[0]からary[15]はすべて int型. 配列の初期化 int ary[3] = {1, 2, 3}; int ary[] = {1, 2, 3}; どちらも同じ意味. 後者のほうが間違いがない(コンパイラが 要素の数を数えてくれる). レポート課題1(フィボナッチ数) フィボナッチ数f(n)は、次の漸化式で定義さ れる: f(0) = 1, f(1) = 1, f(n) = f(n-1) + f(n-2), n >= 2. f(n)を求めるプログラムをCで書け.各項は longで表すこと 何項目まで正しく求められるか? 多倍長整数の配列での実現 暫定的に、以下のようにする: 配列の長さを固定:DIMとする 基数を固定:BASEとする longの配列、次元はDIM とりあえず非負整数のみ 添え字の小さい方が小さい方の桁 多倍長整数の配列での実現(2) つまり、 #define BASE 10 #define DIM 3 long a[DIM] = {1, 0, 0}; /* DIM = 3 */ なら、a = 1 + 0*101 + 0 * 102 = 1. DIM桁のBASE進表記 繰り上がり a = a_0 + a_1*10^1 + a_2 * 10^2, b = b_0 + b_1*10^1 + b_2 * 10^2, のとき、c=a+bをどのように計算するか考え る 各c_i=a_i+b_iをi桁目にするのではない BASEより大きくなったときに、次の桁へ繰 り上げる プログラム例 (mpinteger-wo-malloc.c) 以上のようなアイディアで、フィボナッチ数 の計算を行うプログラムを作る. ポインタ型 Cの型の一つ 任意の型Tに対して、「Tのポインタ型」が 定義できる 従って、「Tのポインタのポインタ」型等々も定 義できる 例:integer型のポインタ型変数a……int *a 例:char型のポインタ型変数s…char *s テキスト9章 ポインタって何? 型Tのポインタ型の値は、型Tのモノ (object)のアドレス(メモリ上の場所) 型Tのポインタ型の変数は、型Tのモノのア ドレスを保持するのに必要なだけのメモリ を占める ポインタの作り方 型Tのモノから、それを指すポインタ値をつ くるには、アドレス演算子&を使う 例:int a = 1; int *aptr; aptr = &a; 型Tのポインタ値から、それが指している 型Tのモノを見るには、間接演算子*を使う 例:int *bptr; int b; b = *aptr; /* aと同じ値 */ ポインタって何?(続) int a = 1; /* int が保存できるメモリを確保し、1を保存*/ int *aptr = &a; /* そのメモリの番地 */ 1 aptr メモリの模式図 ポインタに対して出来ること T *p; /* pがT型のポインタ */ ポインタの間接参照(そのポインタが指す モノを取り出す): *T /* T型のモノ */ ポインタの算術:T ary[8]; T *p = &ary[0]; ならば、p + i と ary[i] とは同じメモリを指 す なぜポインタがいるのか? 一つの理由は、C言語での関数引数が、 値渡しだから 例 int a = 1; f(a); とすると、f()に渡されるのは、aの値のコ ピー.変数aそのものが渡されるのではな い プログラム例:swap.cを参照 scanf()系の関数で、読み込んだ結果を保 存する変数に&演算子をつけるのも同じ理 由 ポインタと配列との関係(テキスト1 0章) 式の中に現れる配列は、「Tの配列」から「Tへ のポインタ」に変換される(ary_ptr.c参照) このときのポインタは、配列の0番目の成分を 指すポインタ int a[8], *aptr; aptr = a; と、 int a[8], *aptr; aptr = &a[0]; とはまったく同じ 例外:配列がsizeof演算子の引数のとき sizeof (a) = sizeof (int) * 8 /* 上の宣言の元で*/ メモリの確保:静的な確保 これまでは、プログラムの字面に、メモリの 確保が指示されていた: たとえば、long a[10]; は、sizeof(long) * 10 byteのメモリを確保し、その先頭アドレスをaと いう名前で識別する プログラムの実行が始まる前、コンパイル 時にすべてのメモリが確保されている メモリの動的確保:malloc() プログラムの実行が始まってから、指定さ れた大きさのメモリを確保するライブラリ関 数:void *malloc(size_t). malloc (16); で16byteのメモリを確保し、その 先頭アドレスを返す. 返されるアドレスは、void型のポインタ Void型のポインタは、任意の型に変換可能な 「総称型」(generic type) 型の明示的変換(キャスト) Cの型システムでは、一定の規則の下で、 型の変換が行える. 当然行えるべき変換…intからlong, floatか らdouble, intからfloat, longからdoubleな ど:int a; long aa; aa = (long) a; 逆もある.(精度の切り捨て) malloc()は何を返すべき? 総称型:void * malloc()で確保したメモリは、何に使われ るか限定できない 何にでも使える型:void * 実際には、void *をほかの型に変換(キャ スト)して使う 例 void *tmp; tmp = malloc (16); long *a; a = (long *)tmp; malloc()の使い方 場合によっては、メモリの確保に失敗する. その場合、返値がNULL. void *tmp; long *a; tmp = malloc (sizeof (long) * 4); if (tmp == NULL) /* エラー処理 */ else a = (long *)tmp; のように使う.使い終わったら、 free(a); /* 解放する */ 変数のエクステント 変数の有効期間 関数内で宣言された変数は、その関数内 に制御がある間のみ有効だった(プログラ ミング演習Iの6/15の回の資料参照) 関数内で確保したメモリは、明示的に解放 されるまで有効. malloc()を使った多倍長計算 プログラム例参照. レポート課題2 BASE100, DIM 3の場合、 1. a = 23 + 34*100 + 45*100^2, b = 11 + 22*100 + 33*100^2について、a+bを手で計算 せよ. 2. a = 23 + 34*100 + 45*100^2, b = 88 + 77*100 + 44*100^2について、a+bを手で計算 せよ. レポート課題3 プログラム例mpinteger.cで、BASEを100 とし、mpinteger-wo-malloc.cのように、引 数として何番目のフィボナッチ数まで計算 するかを指定できるように改造したものを 作成せよ. レポート課題提出要領 2003年11月4日(火)一杯に、木村まで メールで送ること.アドレスは [email protected] 添付ファイルではなく、できるだけメール本 文にレポート本文を記載してください. 参考にした文献や友人のレポートは、その 旨明記すること.
© Copyright 2024 ExpyDoc