プログラミング言語 2 第 06 回 (2007 年 06 月 11 日 ) 今日の配布物 片面の用紙 1 枚 今日の課題が書かれています 本日の出欠を兼ねています 1
今日やること http://www.tnlab.ice.uec.ac.jp/~s-okubo/class/language/ にアクセスすると 教材があります 2007 年 06 月 11 日分と書いてある部分が 本日の教材です 本日の内容 先週の課題の簡単な解答 ( と型変換の続き ) パイプとリダイレクト 関数の自作の基礎 課題について 課題の返却について まだ準備できてないですが そのうち返却を行いたいと思います 2
印刷方法 テキストファイルの印刷方法 a2ps hogehoge.txt lpr プログラムリストを印刷したいときとか ポストスクリプトファイルの印刷方法 lpr hogehoge.ps xgraph で保存したグラフを印刷したいときとか 第 05 回の課題の簡単な解説 ( と型変換の続き ) 3
第 05 回の課題 課題 2: 次の要求を満たすプログラムを書け 要求 1: input.txt を開いて 入力データを読み込む input.txt は 10 行からなっている input.txt の 各行には スペースで区切られて 4つの double 型の値が並んでいる 順に x,y,z,w とする 要求 2: output.txt に出力を書き込む output.txt は 10 行からなる input.txt の 各行には スペースで区切られて 2つの double 型の値が並ぶ 1つめの整数値は x とする 2つめの整数値は y z ln ( w) cos( 1.5) とする cos( z) 回答例プログラムの方針例 : 前回の課題と同様に書くことができる 1. 読み込む値の型が double 型 ( 前回は整数型 ) fscanf のformat 部分で指定する変換指定を %lf にする ( 前回は %d) 2. 値を4つ読む必要がある ( 前回は2つ ) %lf を4つ並べる 3. 出力する値の型が double 型 ( 前回は整数型 ) fprintfのformat 部分で指定する変換指定を %f にする ( 前回は %d) 4
回答例プログラムの方針例 : scanf と printf で 変換指定の方法が異なっていることに注意が必要 scanf では %f :float 型 %lf:double 型 printf では %f :float 型か double 型 printf で %lf としても表示されますが この場合の l に意味はありません その 1/4: 回答例 回答例 プログラムの方針例 : 入力用と出力用で それぞれファイルを開く 1. 入力用 2. 1 つの s c a n f で 4 つの値 ( 1 行分 ) を読み込む 3. 4 つの値を読み込んだら すぐに計算して ファイルに出力する 4. 上記のことを 1 0 回繰り返す ( 全部で 1 0 行分 ) #include<stdio.h> // // 入出力を使うので stdio.h を include する FILE *fp, *fp2 // // FILE 構造体のポインタ fp fpと fp2 を宣言 // // 読み取りファイル用と書き込みファイル用 int i; i; // // double 型の変数 i i を宣言 // // for for 文で使用する用 double x,y,z,w;// double 型の変数 x, x, y, y, z, z, w を宣言 // // 4 つの値を読み込むため 5
その 2/4: 回答例 回答例 プログラムの方針例 : 入力用と出力用で それぞれファイルを開く 1. 入力用 2. 1 つの s c a n f で 4 つの値 ( 1 行分 ) を読み込む 3. 4 つの値を読み込んだら すぐに計算して ファイルに出力する 4. 上記のことを 1 0 回繰り返す ( 全部で 1 0 行分 ) main(){ fp=fopen("input.txt","r"); // // input.txt を読み込み用に // // 開き fp fpと関連付け fp2=fopen("output.txt","w"); //output.txt を書き込み用 // // に開いて fp2 と関連付け 今回は input.txt から読み込み output.txt に書き込むように ファイルを開きます その 3/4: 回答例 回答例 プログラムの方針例 : 1. 入力用と出力用で それぞれファイルを開く 2. 1つのつ s c an f つの s c an fで 4 つの値 ( 1 行分 ) を読み込む つの値を読み込んだら すすぐに計算して ファイルに出力する 3. 4つの 上記のことを 1 0 回繰り返す ( 全部で 10 行分 ) 4. 上記 for(i=1;i<=10;i++){ // // 入力ファイルは 10 10 行あるから fscanf(fp,"%lf%lf%lf%lf",&x,&y,&z,&w); // // fp fpから 4 つの整数を読み込み // // x, x, y, y, z, z, w に代入する fprintf(fp2,"%f %f n",x, sqrt(pow(y,z))*log(w)*cos(1.5)/fabs(cos(z))); // // 2 つの値を fp2 に書き込む 1 つの fscanf で 4 つの値 ( つまり 1 行分 ) を読み込みことを 10 回繰り返しています 6
回答例 その4/4: fclose(fp); // // fp fpを閉じる fclose(fp2); // // fp2 を閉じる ファイルをクローズして 終わります 得られる結果 1.000000 0.524537 2.000000 0.222027 3.000000 0.348167 4.000000 0.620997 5.000000 0.737950 6.000000 0.962300 7.000000 0.345702 8.000000 0.407889 9.000000 1.549765 10.000000 2.167927 7
回答例 問題 1: 次の printf がどのような値を出力するか答えなさい printf("%f n",5/4); 何らかの値がでます printf("%f n",5/4.0); 1.250000 printf("%f n",(double)5/4); 1.250000 printf("%f n",(double)(5/4)); 1.000000 前回の内容では説明をしていないので 実際にやってみないと解けない問題です 型変換について 計算式中に複数の型が現れるときは 自動的に型変換が行われる 優先順位 int unsigned int long unsigned long float double long double 例 : int 型 + long 型 = long 型 int 型 + double 型 = double 型 8
問題 1 の解説その 1 printf("%f n",5/4); 何らかの値がでます 5は整数型 4も整数型計算結果も整数型に printfは自動的に型変換を行ってくれません 整数型があるメモリの部分を浮動小数点として解釈して 何かしら出力する 優先順位 int unsigned int long unsigned long float double long double 問題 1 の解説その 2 printf("%f n",5/4.0); 1.250000 5は整数型 4.0はdouble 型整数型 /double 型の計算結果はdouble 型 計算結果は1.250000 となり それが出力される 優先順位 int unsigned int long unsigned long float double long double 9
問題 1 の解説その 3 printf("%f n",(double)5/4); 1.250000 (double)5はdouble 型 4は整数型 double 型 / 整数型の計算結果はdouble 型 計算結果は1.250000 となり それが出力される 優先順位 int unsigned int long unsigned long float double long double 問題 1 の解説その 4 printf("%f n",(double)(5/4)); 1.000000 計算の順番に注意! (5/4) が行われた後 (double) でキャストされる (5/4) の計算結果は整数型の値 1 に 整数型の値 1 を (double) でキャストした結果 1.000000になる 優先順位 int unsigned int long unsigned long float double long double 10
パイプとリダイレクト パイプとリダイレクトとは 通常 標準出力への出力を行うと コンソールに表示される パイプとリダイレクトを用いると この標準出力の出力先を切り替えたりすることができる このことを利用すると 様々な手間を省略することができたりできるので 便利に使いましょう 11
リダイレクトとは 標準出力の出力先を変更する 代表的な切り替え先は ファイル コマンドラインでの記述 command > filename command >> filename command < filename 効果 command を実行したときの 標準出力への出力を filename に書き出す command を実行したときの 標準出力への出力を filename の末尾に追加書き込みする command を実行したときの 標準入力を filename から貰う 例 1: リダイレクトの例 ls > ls.txt コンソールには何も表示されないが 代わりに ls.txt というファイルの中に 結果が書き出される 例 2: ls >> ls.txt コンソールには何も表示されないが 代わりに ls.txt というファイルの末尾に 結果が追記される 12
リダイレクトの例 リダイレクトを利用すると 標準出力にしか出力しないプログラムでも 結果をファイルに書き出せる fprintf を用いたプログラムを書かなくても ファイルに書き出すことができる ファイルに出力する必要がある場合 fprintf 等を使ったプログラムを書く printf を使ったプログラムを書いて リダイレクトでファイルに書き出すの 2 つのパターンを 用途によって使い分けるとよい パイプとは プログラムの標準出力とプログラムの標準入力の間をつなぐ コマンドラインでの記述 command01 command02 効果 command01 の実行したときの標準出力への出力を command02 の標準入力として入力する 13
リダイレクトの例 例 1: a2ps hogehoge.txt lpr 1. a2ps は hogehoge.txt をポストスクリプトファイルに変換し その結果を標準出力に出力する 2. lpr は その出力結果を標準入力として貰い 印刷を行う a2ps : テキストファイルをポストスクリプトファイルに変換し 標準出力に結果を出力 lpr :( ファイル指定がない場合は ) 標準入力から入力を貰い 印刷を行う リダイレクトの例 例 2: ls tee ls.txt 1. ls は 今いるディレクトリにあるファイルの一覧を 標準出力に出力する 2. tee は その出力結果を貰い その中身をコンソールに書き出し かつファイル ls.txt にも書き出す tee: パイプから入力を貰い その結果を標準出力とファイルに書き出す 14
関数の自作の基礎 関数の作成 #include<stdio.h> int sample01(int max){ tmp=tmp+2; return tmp; main(){ 文 value=sample(10); 関数を自作している部分 引数 戻り値 文が必要になる 15
関数の作成 戻り値の型を指定関数名引数と引数の型を指定 int sample01(int max){ tmp=tmp+2; return tmp; 文 戻り値を指定 構造 戻り値の型を指定関数名 ( 引数と引数の型を指定 ){ ){ 文 return 戻り値 ; 戻り値について 関数の戻り値の型として指定できるもの 今まで変数として宣言してきた型 返り値を返さない場合は void を指定する 関数は return 文の次にある値を返り値として返す 返り値が無い場合は 単に return; とすればよい return が無い場合は 関数の末尾まで実行したら 戻り値を返さずに 関数が終了する main も関数なので 返り値がある 型を指定しなかった場合は int を返す関数と解釈される 16
引数について 関数の引数には 2 パターンあります 値渡し関数に 値を渡します アドレス渡し関数に 変数のアドレスを渡します それぞれの特徴を捕らえて 適宜使い分ける必要があります 値渡し 関数の引数として 値を渡す方法 関数に渡されるのは あくまでも 値のみのみ です 変数が渡されるわけではありません 関数内部では 渡された値を 自前の変数に代入し 処理を行います 自前の変数 tmp に 10 を代入するよ main(){ int scan_value=10; value=sample01(scan_value); 値は 10 だよ int sample01(int tmp){ tmp=tmp+2; return tmp; 17
アドレス渡しその 1 関数の引数として ( 変数の ) アドレスを渡す方法 関数に渡されるのは 変数変数のアドレスアドレス です 関数内部では 自分自分で使用使用するする変数変数のアドレスアドレスを 引数としてとして渡されたされたアドレスアドレスに設定設定します 関数内部と外部で名前は異なる変数かもしれませんが 保存する場所は一緒になります そのアドレス使うよ main(){ int scan_value=10; value=sample01(&scan_value); アドレスを渡すよ int sample01(int *tmp){ *tmp=*tmp+2; return *tmp; main(){ アドレス渡しその 2 int scan_value=10; value=sample01(&scan_value); アドレスを渡すので & がついている int sample01(int *tmp){ アドレスを貰うので ポインタを宣言 *tmp=*tmp+2; return *tmp; 関数 main 内の scan_value のアドレス = 関数 sample01 内の *tmp のアドレス 関数 sample01 内で *tmp の値を変えると 関数 main 内の scan_value の値も変わる 18
アドレス渡しその 3 どういうときに使うのか? 関数に配列を渡したいとき 配列の内容を まとめて値渡しする事はできない 関数から 2 つ以上の結果を手に入れたいとき 戻り値は 1 つしか指定できない! 変数のアドレスを渡して そのアドレスに保存されている値を関数内で弄ってしまう スコープその 1 #include<stdio.h> int scan_value; int sample01(int max){ int i; i; int value=0; 略 main(){ int value; 略 それぞれの場所で宣言された変数は どこで参照可能か? 関数 main の中で宣言 (value) 関数 sample01 の中で宣言 (i, value, max) 関数 main と sample01 の外 (scan_value) 19
スコープその 2 #include<stdio.h> int scan_value; int sample01(int max){ int i; i; int value=0; 略 main(){ int value; 略 それぞれの場所で宣言された変数は どこで参照可能か? 関数の中で宣言された変数は その関数中でしか使用できない 関数の外で宣言された変数は どの関数からでもみることができる スコープその 3 #include<stdio.h> int scan_value; int sample01(int max){ int i; i; int value=0; 略 main(){ int value; 略 関数の中で宣言された変数は その関数中でしか使用できない 関数の外で宣言された変数は どの関数からでもみることができる 全体で参照できる sample01 の中でのみ参照できる value scan_value i,value main の中でのみ参照できる 20
スコープその 4 #include<stdio.h> int scan_value; int sample01(int max){ int i; i; int value=0; 略 main(){ int value; 略 関数の中で宣言された変数は その関数中でしか使用できない 関数の外で宣言された変数は どの関数からでもみることができる 全体で参照できる sample01 の中でのみ参照できる 参照できる範囲が被ってないので 名前が同じでも問題ない value scan_value main の中でのみ参照できる i,value 21