プログラミング演習 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 関数を繰り返し呼び出すので遅い
© Copyright 2024 ExpyDoc