プログラミング演習

プログラミング演習
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
n0
演習問題

演習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回入力を間違えた場合,「口座停止」などの
メッセージを表示させてプログラムを終了させる.