Document

プログラミング演習
1.
2.
3.
4.
5.
6.
7.
関数定義
関数宣⾔(関数プロトタイプ)
ライブラリ
数学ライブラリ
gnuplot
再帰関数
Cプリプロセッサ
1. 関数定義
z プログラミング⾔語の基本のひとつ
1. 関数定義
1. 関数?
1. 0個以上の引数をもち,単独の型の値を返す
2. これまでに学んだ関数の例
3. int main(void);
1. voidが引数 (何もない)
2. intを返すmainという名前の関数
4. void printf(); /* 引数省略 */
1. 引数がある。
2. 返り値が不明(ない、無効)な値
3. 戻り値, return, 返り値
引数を持たず、
戻り値を持たない関数の例[1]
void print_string(void)
{
fprintf(stdout,"-------------------¥n");
}
int main(void)
{
print_string();
print_string();
}
出力は?
[1]の出⼒
------------------------------------関数が呼ばれた回数だけ,
文字列が出力される
(返り値でないから、
これを関数の副作用という)
引数をもつ関数の例[2]
void print_sum(int a, int b)
{
int sum=0;
sum = a+b;
printf("sum: %d¥n",sum);
}
main関数の中で次のように記述すると
print_sum(4,6);
print_sum(50,-50);
出力は?
[2]の出⼒
sum: 10
sum: 0
引数をもち、戻り値のある
関数の例[3]
int calc_sum(int a, int b)
{
int sum=0;
sum = a+b;
return sum;
}
main関数の中では、つぎのように利用する
int total=0;
total = calc_sum(4,50);
引数をもち、戻り値のある
関数の例[3]
int calc_sum(4, 50)
{
int sum=0;
sum = a+b;
return sum;
}
main関数の中では、つぎのように利用する
int total=0;
total = calc_sum(4,50);
引数をもち、戻り値のある
関数の例[3]
54
int calc_sum(4, 50)
{
int sum=0;
sum = 4+50;
return sum;
}
main関数の中では、つぎのように利用する
int total=0;
total = calc_sum(4,50);
引数をもち、戻り値のある
関数の例[3]
54
int calc_sum(4, 50)
{
int sum=0;
sum = 4+50;
return sum;
}
main関数の中では、つぎのように利用する
int total=0;
total=54;
total = calc_sum(4,50);
配列を引数とする
関数定義の例
int sum(int array[], int size) {
int total=0,count=0;
for (count=0;count<size;count++) {
total += array[count];
}
return total;
}
配列を引数とする
関数の使い⽅[1]
int sum(int array[],int size);
int localArray[4]={3,5,6,7};
int total=0;
total = sum(localArray, 4);
printf("和: %d¥n", total);
和: 21
出力は?
配列を引数とする
関数の使い⽅[2]
int sum(int array[],int size);
int localArray[6]={4,5,6,7,8,9};
int total=0;
total = sum(localArray, 6);
printf("和: %d¥n", total);
和: 39
出力は?
関数の定義は順序を意識
1. これまでの演習授業で
1. print ("this is %d¥n", num);
2.
として、エラーになった⼈は?
2. printを関数だと思って、コンパ
イラが「関数がない」というエ
ラーを出⼒している。
printfが正しい
関数の実⾏順序の復習
/* sample.c */
int test(int a)
{
return a;
}
int main(void)
{
test(30);
}
next
start
next
コンパイル順序
/* sample.c */
int main(void)
{
test(30);
}
まだ、コンパイル
していないので、 next
処理方法が不明
int test(int a)
{
return a;
}
main()とtest()の
順序が異なる点に
注意
start
2. 関数プロトタイプ
関数宣⾔
全ての関数をmain()の前に?
1. 変数宣⾔と同様に、関数も宣⾔するこ
とができる。
1. 宣⾔する事を推奨されている。
2. プロトタイプ宣⾔
1. 省略すると出現した時点で、整数型として仮
定されてしまう。引数のチェックも⾏われな
くなる。
2. たとえば、int add(int a, int b);
1. 整数を返す、2つの整数を引数とする。
3. main()だけでなく、関数から他の関数
を利⽤するときにも必要
関数プロトタイプ
型名
関数名
(
引数の型
);
ex.,
int sub(int a, int b);
void print_message(void);
float ave(int array[],int size);
折返し
void print_numbers(float matrix[][][],
int size_x,int size_y, int size_z);
関数プロトタイプなしの場合
int main(void)
{
int c=3, d=5, e=0;
int add(int a, int b)
{
return a+b;
}
e = add(3,5);
}
NG!
関数プロトタイプありの場合
int add(int a, int b);
int main(void)
{
int c=3, d=5, e=0;
e = add(3,5);
}
int add(int a, int b)
{
return a+b;
}
OK !
関数プロトタイプ
z 今回
学習するライブラリと密接な関係
z #include <stdio.h>
z /usr/include/stdio.hの中⾝は、
関数プロトタイプと変数宣⾔、マクロ定義
マクロ定義は次週以降
変数の有効範囲
#include <stdio.h>
int Number=1;
int main(void)
{
int number=0;
printf("Number: %d¥n",Number);
printf("number:%d¥n",number);
}
出力は?
出⼒
Number: 1
number: 0
変数の有効範囲
int num=0;
void count_up(void)
{
num++;
}
int main(void)
{
count_up();
count_up();
count_up();
fprintf(stdout,"
%d¥n",num);
}
count_up()でも
main()でも、
numを宣言せず
利用している。
出力は?
count_up() * 3の出⼒
3
変数の有効範囲
1. 関数定義の中で宣⾔された変数は、その
関数の中でのみ有効。
1. 呼び出されるたびに初期化される。
2. main()、そのほかの関数定義の前に関数
の外側で定義された変数は、⼤域変数
(外部変数、グローバル変数)と呼ばれ、
全ての関数から常に利⽤可能。
1. ⼤変に危険
2. 関数も「⼤域的」である。
関数と変数の有効範囲
void show_num(int num)
{
num = num * 100;
printf("%d¥n",num);
}
int main(void)
{
int num=1;
show_num(num);
printf("%d¥n",num);
}
変数は
もとの
値
100
1
関数と変数の有効範囲
配列
void ch_a(int ar[],int size)
{
ar[size-1]=0;
}
int main(void)
{
int a[]={2,3};
fprintf(stdout,"%d¥n",a[1]);
ch_a(array,2);
printf("%d¥n",a[1]);
}
配列は
関数で
上書き
3
0
extern宣⾔
z 暗黙に変数が外部で宣⾔されているとい
うよりも、externという修飾⼦をつける。
z 同じファイルの中の場合は省略する⽅が多い。
z ex.,
int num;
int main(void)
{
extern int num; /* 外部で宣⾔されてい
るという修飾⼦ */
関数と配列利⽤の講義内演習
int array[3][10]; /* 値が入っていると仮定 */
int print_num(int a, int b,int c);
int print_num(int a, int b,int c) {
printf("%d,%d,%d¥n",a,b,c);
}
という関数が定義されているときに
この関数を使って、int array[3][10];
の中身を出力するにはどのようにするか?
回答例
int array[3][10]; /* 値が入っていると仮定 */
int print_num(int a, int b,int c);
for (c=0;c<10;c++) {
print_num(array[0][c],array[1][c],array[2][c]);
}
関数
課題
z 4つの整数の引数をもち、その合計、平
均をstdoutに出⼒し、戻り値をvoid
とする関数を作成し、利⽤例を作成する。
void print_sum_and_ave(int c1,int c2,int c3,int c4);
意外と忘れがちなこと
z C⾔語では,整数同志の演算結果は整数
z 例)
z 10 / 4 => 2
z 演算結果の型が異なることが想定
z キャスト,cast, 型変換 する
z 例)
z 10 / (double) 4 => 2.50000
4/8/2015
Shigeru Fujita, Chiba Institute of Technology, JAPAN
35
例題
z 整数配列を引数として、その平均値を
doubleで返す関数を定義し、利⽤例を
作成する。
double average(int array[], int size);
例題
z 6つのdouble変数を引数として、2元1次⽅程式
を解き、画⾯に解を出⼒する関数を作成する。
戻り値はvoid。
void calc(double a1, double b1, double c1,
double a2, double b2, double c2);
折返し
線形⽅程式(連⽴⽅程式)
中学⽣の数学
線形代数
⾏列を使って線形⽅程式を解く
4/8/2015
Shigeru Fujita, Chiba Institute of Technology, JAPAN
38
連⽴⽅程式
z
z
z a1 = 1.0, b1=2.0, c1=3.0
z a2 = 2.0, b2=1.0, c2=3.0
z
4/8/2015
= Shigeru Fujita, Chiba Institute of Technology, JAPAN
39
配列利⽤プログラムの注意
1. C、C++の仕様(制約)
1. 配列を引数にするときはサイズも渡す
2. 配列を引数とするときには、先頭の次元の
みが不定⻑でも可能
3. void function(array[][3]);
4. のように、する必要がある。
2. Javaは不定⻑の引数が可能
1. オブジェクト指向的な考えで、配列⾃体が
⾃分⾃⾝の⼤きさを知っているから
gnuplotを使ってグラフ化
1. 卒論・修⼠論⽂や仕事で多数のグラフを書く
1. エクセルで死ぬほど繰り返す ⼜は
2. gnuplotで⾃動的にグラフを描く
2. gnuplotでグラフを描く
1. $ gnuplot
2. gnuplotのコンソールが起動
3. gnuplot> plot “sin.dat” with lines
あらかじめ作成したデータファイル
4/8/2015
Shigeru Fujita, Chiba Institute of Technology, JAPAN
42
レポートのためにグラフを
ファイルに保存する
1. 画⾯でグラフを確認したら画像ファイル化
1.
2.
3.
4.
gnuplot> set terminal png
gnuplot> set out “graph‐1.png”
gnuplot> replot
(画⾯には何も出ないが、カレントディレクトリに、
graph‐1.pngという名称でファイルが出⼒されてい
る)
5. (元に戻す)
6. gnuplot> set terminal qt
4/8/2015
Shigeru Fujita, Chiba Institute of Technology, JAPAN
43
例題 (⾃分で⼊⼒して確認)
1. sin関数のグラフを描いてみる
2. % man sinでsin関数の仕様を確認
/* sin.c */
3. グラフをgnuplotで描く
#include <stdio.h>
#include <math.h>
int main(void)
{
double radian=0.0, value=0.0;
1. ⾃分で⼊⼒
for (radian=0.0; radian<=4*M_PI; radian+= (1.0/M_PI)) {
value = sin(radian);
printf("%f %f¥n", radian, value);
}
return 0;
}
% gcc –o sin sin.c –lm
% ./sin > sin.dat
% gnuplot
gnuplot> plot “sin.dat” with line
gnuplot> quit
定数としての円周率
gnuplot> plot “sin.dat” with line, sin(x)
gnuplot> quit
6.
関数の再帰呼び出し
1年⽣の復習
再帰関数の例
z
z
階乗を求める
3! = 3 * 2 * 1 = 6
z
N! = N * (N ‐1) * (N ‐ 2) * … * 1
z
N! = 1 * … * (N‐2) * (N‐1) * N
int factorian (N); を設計する
N-1
×
N
乗算の解を求める部分
factorial(4-1) * 4
(4-1)の階乗と4の積
factorial(3-1) * 3
(3-1)の階乗と3の積
factorial(3-1) * 3
(3-1)の階乗と3の積
factorial(2-1) * 2
(2-1)の階乗と2の積
factorial(1) は 1
再帰関数の例 階乗を求める
int factorial(int num) {
if ( num == 1 ) {
return 1;
} else {
num = factorial(num‐1) * num;
return num;
}
}
再帰関数利⽤
int factorial(int num) {
if ( num == 1 ) {
return 1;
} else {
num = factorial(num‐1) * num;
return num;
}
}
factorial(3)
if (3 == 1)
num = factorial(3-1) * 3;
再帰関数利⽤
int factorial(int num) {
if ( num == 1 ) {
return 1;
} else {
num = factorial(num‐1) * num;
return num;
}
}
factorial(2)
if (2 == 1)
num = factorial(2-1) * 2;
再帰関数利⽤
int factorial(int num) {
if ( num == 1 ) {
return 1;
} else {
num = factorial(num‐1) * num;
return num;
}
}
factorial(1)
if (1 == 1)
return 1;
再帰関数利⽤
int factorial(int num) {
if ( num == 1 ) {
return 1;
} else {
num = factorial(num‐1) * num;
return num;
}
}
factorial(2)
if (2 == 1)
num = factorial(2-1) * 2;
return 2;
2
1
再帰関数利⽤
int factorial(int num) {
if ( num == 1 ) {
return 1;
} else {
num = factorial(num‐1) * num;
return num;
}
}
factorial(3)
if (3 == 1)
num = factorial(3-1) * 3;
return 6;
6
2
再帰関数で記述可能な関数の例
z
z
z
z
z
階乗
フィボナッチ数列
フラクタル図形描画(コッホ曲線など)
ハノイの塔を解く
インターネットのコンテンツをリンクをた
どって処理
z ディレクトリを再帰的に処理
z 再帰関数の弱点
z 単純なプログラムミスで無限ループになる
z 関数を繰り返し呼び出すので遅い