プログラミング演習 2004年7月1日(木) 奈良先端科学技術大学院大学 情報科学研究科 猿渡 洋 斎藤 将人 新保 仁 for 文:ループ 書式: for (式1;式2;式3){ 文,またはブロック } 式1は{}に入る前に1度だけ実行される 式2が真であればループを繰り返す 式3でインクリメントを行う for 文を使用した演算の例 整数を0から99まで1ずつ増やしながら表示させる for (n=0; n < 100; n++) { printf("%d\n", n); } 無限ループ for ( ; ; ) { } while と同様の機能 for 文を使ったプログラム 100個の乱数([0,1]の一様乱数)を発生させて,その平均,分 散を求める. #include <stdio.h> #include <stdlib.h> /* drand48 のために必要 */ main(){ int i, N=100; double x, sum=0.0, sqsum=0.0, ave=0.0, var=0.0; for (i=0;i<N;i++) { x = drand48(); /*一様乱数生成関数 drand48 */ sum += x; /* 総和を計算 */ sqsum += x*x; /* 二乗和を計算 */ } ave = sum/N; /* 平均値を求める */ var = sqsum/N - ave*ave; /* 分散を求める */ printf("Ave. = %f\n Var. = %f\n", ave, var); } 配列:準備 配列とは? ⇒同種のデータを各要素に対応した添字で参照できるもの 算術演算等においてベクトル,行列を表現するのに便利 文字列(後述) 配列の宣言 型指定子 配列名[定数式][定数式]…[定数式]; (例) int a[6]; …int型の6要素1次元配列(ベクトル)a float b[2][3] …float型の2行3列2次元配列b 配列:準備(続き) 宣言された配列の構造 配列の添え字の範囲は0から(定数式-1) (例) int a[4] ; ⇒ a[0], a[1], a[2], a[3] のみ使用できる 配列の各要素はメモリ上に連続して格納される a[0] a[1] a[2] a[3] 4バイト(int型の場合) 他の型のサイズについては 初回配付資料等参照 int型の連続する箱 にデータがつめられる (異なる型のデータは 一配列にはならない) 配列を使用した計算例1 y = x2+x+1 を計算.x を0から10まで1ずつ増加.総和も求める. #include <stdio.h> main() 配列の宣言(11個の要素を持つ) { int y[11], x, sum = 0; for(x=0; x<11; x++){ y[x] = x*x + x + 1; y = x2+x+1 を計算して配列 y に代入 printf("y[%d] is %d\n", x, y[i]); } for(x=0; x<11; x++) { sum += y[x]; /* 総和の計算 */ } printf(“SUM is %f\n”, sum); } 配列を使用した悪計算例 #include <stdio.h> main() { int y[11], x, sum = 0; 定義されていない配列を 使用するとどうなるか? for(x=0; x<11; x++){ y[x] = x*x + x + 1; printf("y[%d] = %d\n", x, y[x]); } 定義されていない配列要素を 参照すると何が表示されるか? for(x=0; x<20; x++) { printf("y[%d] = %d\n", x, y[x]); } } 配列を使用した計算例2 #include <stdio.h> 三角関数を1周期分 積分する #include <math.h> main(){ double s[180],c[180],integ_s,integ_c,prdct,pi,dx; int i, N=180; prdct = integ_s = integ_c = 0.0; /* 初期化 */ pi = 3.14159; 数値積分 dx = 2.0*pi/N; 2 for(i=0; i<N; i++){ sin( x) dx s[i] = sin(dx*i); 0 2 c[i] = cos(dx*i); cos( x) dx 0 integ_s += s[i]*dx; 2 integ_c += c[i]*dx; sin( x) cos( x)dx 0 prdct += s[i]*dx*c[i]*dx; } printf(“%lf, %lf, %lf\n”, integ_s, integ_c, prdct); } 補足:関数の積分(近似計算) より正確な計算を行うためには数値計算の 専門書を参考にすること. y sin(dx) 0 x 2 dx N 2 0 N 1 sin( x)dx sin( 0 n * dx) * dx n0 演習問題 演習3-1; 以下の関数の数値積分を求めよ。aの値を大きくしていくと / 2 に 近づくことを示せ。但し積分の近似に関して、台形近似を利用せよ。 a 0 台形近似: ただし i は exp( x )dx 2 i のステップ幅 f ( x)dx ( f (i) i π = 3.141592653... f (i 1)) / 2 i メモリイメージ:変数とアドレス 変数はアドレスのついたメモリに記憶される アドレスは,実行時に決定され,変数に固有な値を持ち, 変更できない. アドレス 変数のアドレスとその中身を見る 変数 メモリの中身 (16進数) &i で,変数 i のアドレスを表す. j ffbef5bc 1 i で,変数 i の中身を表す. i ffbef5c0 0 (256) サンプルプログラム int i=0, j=1; printf("i: Address: %p, i=%i\n",&i,i); 実行結果の例 i = 256; printf("i: Address: %p, i=%i\n",&i,i); printf("j: Address: %p, j=%i\n",&j,j); int 型変数には4バイト毎に アドレスが割当てられる 変数の指定(1):名前による指定 int i; あるいは double d;と変数宣言する 変数 i , d を記憶するためのメモリ割当 メモリのサイズは変数の型によって決まる(資料を参照) プログラム内で変数の値を用いるには変数名を使う. アドレス int i; 変数iにメモリ割当 i = 2004; 変数名が参照する メモリに値を格納する j = i + 1; 変数iにある値を 用いて演算 ffbef5bc bd be bf ffbef5c0 c1 メモリ(1箱1Byte) i2004 変数iに割り当てられたメモリ(4Byte) 変数の指定(2):アドレスによる指 定 変数の記憶されているアドレスを用いて,その変数に値の代 入や参照を行うことができる. しかし・・・プログラミングの時点で,アドレスの値を知るこ とはできない. 変数 i の宣言 ffbef5bc int i; アドレスffbef5bcの変数に 値2004を代入したい ??? = 2004; どう書くか? 変数のアドレスは ffbef5bc ffbef5bc 2004 ポインタ変数:アドレスを保存する 変数 ポインタ変数の宣言(例) int型変数へのポインタ pi: int *pi; double型変数へのポインタ pa: double *pa; ポインタ変数に指定したい変数のアドレスを代入 pi = &i; ポインタ変数が指すアドレスの中身への代入 *pi = 2004; int i; int *pi=NULL; i = 0; pi = &i; *pi = 2004; pi i ffbef5c0 ffbef5bc ffbe 0 f5c0 0 2004 ポインタ変数:例 #include <stdio.h> main() { ポインタの初期化:NULL (または 0) を 代入することで何処も指していないこと を示す int i, *pi=NULL; int 型へのポインタ pi の宣言 i = 0; 変数 i に 値 0 を代入 printf("i = %d\n", i); 変数 i の内容を確認 pi = &i; 変数 i のアドレスを pi に代入 ポインタ pi が指すアドレスに 数値を代入 printf("i = %d\n", i); 変数 i の内容を確認 *pi = 2004; } 配列のメモリイメージ int a[5]; メモリ上で連続した5つの 領域が割り当てられる アドレス ffbef5ac ffbef5b0 ffbef5b4 ffbef5b8 配列のアドレスを 参照するには? ffbef5bc メモリ a[0] a[1] a[2] a[3] a[4] 配列のアドレス・メモリの参照 配列名は先頭のアドレスを プログラム中 アドレス での表現 表す a ffbef5ac a と &a[0] は等しい 配列の内容を参照する a[i] :i番目の内容 *(a+i) :〃 配列のアドレスをポインタに 渡す pi=a+i; :a[i]のアドレ スをポインタ pi に代入 a+1 ffbef5b0 a+2 ffbef5b4 a+3 ffbef5b8 a+4 ffbef5bc メモリ a[0] a[1] a[2] a[3] a[4] 文字列:ヌル文字(\0)で終わる char の配列 文字列 "NAIST" のメモリイメージ アドレス 文字列のアドレスと内容を確かめるプログラム #include <stdio.h> main() { char *str; int i; str = "NAIST"; for (i=0;i<6;i++) { printf("%d\t%c\t%p\n", i,*(str+i),str+i); } } メモリ 20838 'N' 20839 'A' 2083a 'I' 2083b 'S' 2083c 'T' 2083d '\0' 文字列の取り扱い:例1 文字列の長さを計る #include <stdio.h> main() { char *str; int i; 文字配列”Hello NAIST!!”の 先頭のアドレスをstrに代入 文字配列”Hello NAIST!!”の str = ”Hello NAIST!!”; i = 0; i番目の要素がヌル文字か while(*(str+i)!=’\0’) どうか調べている i++; printf(”Length of %s is %d\n”, str, i); } 文字列の取り扱い:例2 #include <stdio.h> main() 文字配列sの先頭のアドレスをstrに代入 { char *str, s[20]; int i; str = s; printf("please input a string :"); scanf("%s", str); 配列sに文字列を入力 i = 0; while(*(str+i)!='\0') i++; printf("Length of %s is %d\n", str, i); } ポインタ変数使用上の注意 有効なアドレスが代入されていないポインタ変数を使わない 変数、配列以外、文字列にも有効なアドレスを得る方法有 り 文字配列はヌル文字で必ず終わる 配列の長さ=文字列の長さ+1(ヌル文字用) 人は何故ポインタ変数を使うのか? 使わなくてもプログラムは書けるが... 一旦その味を覚えると止められない ポインタ変数なんて使えなくても良い? 他人の書いたプログラムを読めなくても良いなら... 演習問題 演習問題3-2 ATM の暗証番号入力をシミュレートするプログラムの作 成 あらかじめ4桁の数字(暗証番号)を設定しておく ユーザに暗証番号を入力させ,入力の正誤に応じた メッセージを表示させる. 入力が正しければ,そこでプログラムを終了させる. ユーザが3回入力を間違えた場合,「口座停止」などの メッセージを表示させてプログラムを終了させる.
© Copyright 2024 ExpyDoc