プログラミング入門2 ファイルの入出力 ポインタ 芝浦工業大学情報工学科 青木 義満 前回演習問題,課題についての注意事項 演算の優先順位について radian = acos(naiseki/p1*p2); naiseki = x1*x2 + y1*y2; p1 = sqrt(x1*x1+y1*y1); p2 = sqrt(x2*x2+y2*y2); naiseki * p2 p1 ()を使って計算の 優先順位を決める radian = acos(naiseki/(p1*p2)); naiseki p1 * p2 配列の要素数について int num; char name[num][128]; printf(“人数を入力してください:”); scanf(“%d”, &num); コンパイルエラー 重要 配列の要素数に変数は使用できない。 定数のみ使用可能 (直接数字を書くか,#defineで定数を定義するか) ※必要なサイズの配列を動的に確保することも可能 alloc, malloc関数 プログラミング入門2 2 今回の講義内容 ファイル入出力 ファイルからのデータ読込み ファイルと配列 ポインタ ポインタの基本 プログラミング入門2 3 ファイルへのデータ書き込み(復習) ソースファイル名:fileio1.c データをファイルに書き込み #include <stdio.h> 100 morishima 95.5 int main(void) ファイルポインタ宣言 { FILE *fp; int student_id = 100; char name[ ]= "morishima"; ファイルに書き込むデータ double tensu = 95.5; fp = fopen("test.txt","w"); “test.txt”という名前でファイルをオープン (書き込み用) fprintf( fp, "%d\n", student_id ); fprintf( fp, "%s\n", name ); fprintf( fp, "%f\n", tensu ); データをファイルに書き込み fprintf( fp, "%d %s %f\n", student_id, name, tensu ); fprintf( fp, "%d\t%s\t%f\n", student_id, name, tensu ); fclose(fp); ファイルを閉じる return(0); } プログラミング入門2 4 ファイルからのデータの読込みの必要性 ファイルに格納されているデータを,プログラム中に 読み込んで処理 プログラム~xxx.c ファイル~成績表リスト 1 aoki 80.5 100 morishima 95.5 50 tokunaga 70.4 ・・・・・・ ・・・・・・ ・・・・・ ・・・・・ int student_id[NUMBER]; char name[NUMBER][256]; double score[NUMBER]; ・・・・・・・・・・・ ・・・・・・・・・・ ・・・・・・・・・・ ・・・・・・・・・・ プログラミング入門2 5 データの作成(野球選手成績リスト) ソースファイル名:player1.c チーム名,選手名,ホームラン数をファイルに書き込み #include <stdio.h> #define NUM 100 /* 最大100人までの選手データを扱う */ for(i=0; i < 5; i++){ /* 読み込んだデータを画面表示 */ printf( "%s %s %d\n", team[i], name[i], hr[i]); /* 読み込んだデータをファイルへ書き込み */ fprintf( fp, "%s %s %d\n", team[i], name[i], hr[i]); int main(void) { FILE *fp; char team[NUM][256]; char name[NUM][256]; int hr[NUM]; int i; } fclose(fp); return(0); fp = fopen("player.txt","w"); } /* キーボードから選手データを読込み */ for(i=0; i < 5; i++){ printf("Input Players' Data\n"); printf("Team : "); scanf("%s", team[i]); printf("name : "); scanf("%s", name[i]); printf("Homerun : "); scanf("%d", &hr[i]); } player.txt G Matsui 50 L Matsui 33 D Fukudome 20 ・・・・・・ ・・・・・・ ・・・・・ ・・・・・ プログラミング入門2 6 ファイルからのデータの読み込み ファイルからデータを読み込む際の手順 ファイルを開く (読込みモードで) 変数へのデータの読込み fscanf関数等を使用 ファイルを閉じる プログラミング入門2 7 ファイルを開く ~ fopen関数 データを読み込む対象の ”ファイル名” を指定 読み込みモードを指定(2つ目の引数に”r”を指定) 読み込みモードでファイルを開く操作 FILE *fp FILE *fp; (ファイルポインタ) fp = fopen( “file.txt”, “r” ); ファイルを指定 file.txt (ファイル) 読込みモード ファイル名 fp を介して,ファイルにアクセスが可能! プログラミング入門2 8 ファイルを開く際のモードの種類 (テキストファイル) モード 意味 “r” 読込み用にファイルを開く。ファイルが存在しない場合や 見つからない場合は失敗する。 “w” 書き込み用にファイルを作成。ファイルが存在する場合は そのファイルの内容は破棄する。 “a” 追加書き込みのために既存ファイルを開く。ファイルが存 在しない場合には作成する。 “r+” 読み書き用にファイルを開く。ファイルは存在していなけ ればならない。 “w+” 読み書き用にファイルを作成。ファイルが存在する場合に は,そのファイルの内容は破棄する。 “a+” 読込みと追加の両方のモードでファイルを開く。ファイル が存在しない場合は作成する。 プログラミング入門2 9 ファイルからのデータの読込み ~ fscanf関数 scanf関数(キーボードからのデータ入力) → fscanf関数(fileからのデータ入力) fscanf関数の書式 fp = fopen( “file.txt”, “r” ); file.txt scanf関数 100 scanf( “%d”, &x); fscanf関数 fscanf( fp, “%d”, x ); fscanf プログラム中の変数 データを読み込む対象の のファイル(ポインタ) int x プログラミング入門2 10 fscanf関数の使用例 実数データの読み込み ファイル:fscanf( fp, “%lf”, x ); double x 文字列データの読込み 文字配列1つ: char name[256] ← fscanf( fp, “%s”, str ); 複数の文字配列: char name[5][256] ← fscanf( fp, “%s”, str[i] ); 1行に並んでいる複数の型のデータを読み込み fscanf( fp, “%d %s %f”, student_id, name, tensu ); file 1 aoki 80.5 char name[ 256] ; int student_id; double tensu; プログラミング入門2 11 ファイルを閉じる 使い終わったら,後片付け ファイルを閉じる ファイルを開く時に指定したファイルポインタを指定 fclose(fp); プログラミング入門2 12 ファイルからのデータの読込み 例題 ソースファイル名:player2.c データをファイルから読み込み,変数に格納, 表示 for(i=0; i < 5; i++){ /* 読み込んだデータを画面表示 */ printf( "%s\t%s\t%d\n", team[i], name[i], hr[i]); } #include <stdio.h> #define NUM 100 int main(void) { FILE *fp; char team[NUM][256]; char name[NUM][256]; int hr[NUM]; int i; fp = fopen("player.txt","r"); fclose(fp); return(0); } 読込みモードで ファイルをオープン /* ファイルから選手データを読込み */ for(i=0; i < 5; i++){ fscanf(fp, "%s %s %d", team[i], name[i], &hr[i]); } プログラミング入門2 13 入出力ファイル名の指定 これまでのプログラム 入出力ファイル名をプログラム中で指定 fp = fopen("player.txt","r"); キーボードからファイル名を指定できれば便利! ファイル名の指定 char filename[256]; printf(“input filename : ”); scanf( “%s”, filename ); fp = fopen( filename, “r” ); プログラミング入門2 14 入出力ファイル名の指定 例題 ソースファイル名:filename.c ファイル名を指定して開く #include <stdio.h> int main(void) { FILE *fp; char filename[256]; ファイル名格納用変数 printf( "Input file name : " ); scanf( "%s", filename ); fp = fopen(filename, "r"); キーボードから入力した文字列を filenameに格納 Filenameでファイル名を指定して開く printf("ファイル名は%sです", filename); /* ファイルからのデータ読込み */ /* データ処理 */ return(0); } 指定したファイルが存在しなかったら? プログラミング入門2 15 ファイルが開けるかどうかのチェック fopen関数 の仕様 fp = fopen(filename, "r"); ファイル”filename”が存在し,開ける場合 fpにそのファイルを指定するファイルポインタを格納 (fpには,そのファイルを指定可能にするための何らかの値が入る) ファイル”filename”が存在しない,開けない場合 fpにはNULL(空)が代入される ファイルが開けるかどうかの判定 fp = fopen(filename, “r”) == NULL == それ以外の値 if文を使って判定 プログラミング入門2 16 ファイルが開けるかどうかのチェック 例題 ソースファイル名: filecheck.c 指定したファイルが開けるかどうかを判定 #include <stdio.h> #include <stdlib.h> ヘッダーを追加(exit関数使用のため) int main(void) { FILE *fp; char filename[256]; printf( "Input file name : " ); scanf( "%s", filename ); フ ァ イ ル が 開 け る か ど う か の 判 定 if( (fp = fopen(filename, "r") ) == NULL ){ printf("指定したファイル%sは開けません\n", filename); printf("プログラムを終了します\n"); exit(1); プログラムを終了 } else{ printf( "file open成功!\n" ); printf( "ファイル名は%sです", filename ); } fp=NULL, ファイルが開けなかった場合 fp != NULL : ファイルが開けた場合 /* ファイルからのデータ読込み,データ処理 */ return(0); } プログラミング入門2 17 ポインタ ポインタ C言語の理解を妨げる難敵の一つ 更にプログラミングの勉強を進めるためには, 超えなければならない壁 理解できれば,C言語の機能をフルに活用できるようになる これまで,流して説明してきた部分を理解できる scanf関数でなぜ変数に&をつけるの? 文字列をscanfで読み込む場合にはなぜ&が不要? 関数に配列を渡すとき,どうして配列名だけを書いて渡すの? ファイルポインタとは? その他 変数のメモリ空間上での振舞いを意識! プログラミング入門2 19 ポインタ の必要性 ソースファイル名: list1001.c (p.226) 2つの整数の和と差を求める関数(間違い) #include <stdio.h> void sum_diff(int n1, int n2, int sum, int diff) { sum = n1 + n2; diff = n1 - n2; } int main(void) { int na, nb; int wa = 0, sa = 0; puts("二つの整数を入力してください。"); printf("整数A:"); scanf("%d", &na); printf("整数B:"); scanf("%d", &nb); sum_diff(na, nb, wa, sa); printf("wa = %d\n", wa); printf("sa = %d\n", sa); return (0); 和と差の値は表示されない! どうして? } プログラミング入門2 20 関数呼び出しと引数の引渡し過程(復習) main関数 int main(void) 重要 { int na, nb; 実引数の値が,仮引数にそれぞれ コピーされる int sa=0, wa=0; na = 10; (変数そのものでなく,中に格納されている値 が受け渡される!) nb = 20; 実引数 sum_diff( na, nb, wa, sa ); 10 20 0 0 仮引数 printf("wa = %d\n", wa); printf("sa = %d\n", sa); void sum_diff( int n1, int n2, int sum, int diff ) { } sum = n1 + n2; Main文のwa, saの値は 書き換えられない! diff = n1 – n2; この関数内では,sumは30, diffは-10となるが。。。 } 解決にはポインタが必要! プログラミング入門2 21 変数 と オブジェクト (p.227) 変数の型とオブジェクト 型 オブジェクト 型 オブジェクト int m; double x; int 型 m double型 x 設計図と実体 設計図 実体 型は設計図,オブジェクトは設計図に基づいて作られた実体 プログラミング入門2 22 論理的なオブジェクトと物理的なオブジェクト 変数(オブジェクト)は,記憶域(メモリ)中の一部を占める箱 int m; double x; イメージ 現実 オブジェクトは独立した個別の箱 オブジェクトは記憶域中の一部を占有する箱 1000番地 m m x 1008番地 メ モ リ 空 間 x 論理的なオブジェクトの概念 アドレス(番地) 物理的なオブジェクトの概念 プログラミング入門2 23 アドレスとは? オブジェクトのアドレス オブジェクト(実体)が格納されているメモリ上の 番地のこと プログラム中で変数を宣言 コンピュータのメモリ内にある場所(アドレス)に, ある大きさの領域を確保すること プログラミング入門2 24 アドレスを表示 ソースファイル名: list1002.c (p.228) オブジェクトのアドレスを表示 #include <stdio.h> int main(void) { int double int nx; dx; vc[3]; printf("nxのアドレス:%p\n", &nx); printf("dxのアドレス:%p\n", &dx); printf("vc[0]のアドレス:%p\n", &vc[0]); printf("vc[1]のアドレス:%p\n", &vc[1]); printf("vc[2]のアドレス:%p\n", &vc[2]); return (0); } プログラミング入門2 25 アドレス演算子(&) オブジェクトの頭に&をつけると,そのオブジェクトの格納されてい るアドレスを取り出すことが可能 アドレスをprintfで表示するための変換指定は%p int m; double x; &m 1000番地 &x m 1008番地 メ モ リ 空 間 x アドレス(番地) printf("nxのアドレス:%p\n", &nx); プログラミング入門2 nxのアドレスを16進数で表示 26 ポインタとは? ポインタ変数 オブジェクトのアドレス(メモリ上の位置)を格納する ためのもの ポインタの宣言 int nx; int *p; int型 : 整数の値を格納するための変数nx int型のオブジェクトのアドレスを格納する ポインタ変数pを宣言 ポインタ変数pの型は? → 「int *」型,int型オブジェクトへのポインタ型 プログラミング入門2 27 最初のポインタ・プログラム ソースファイル名:pointer1.c 整数の値とポインタの値を表示 #include <stdio.h> int main(void) { int nx; int *pt; int型オブジェクトのアドレスを格納するための int型へのポインタ型=(int *)型 nx = 57; pt = &nx; printf("nxの値:%d\n", nx); printf("nxのアドレス:%p\n", &nx); printf("ptの値:%p\n", pt); return (0); 表示される値を確認! } プログラミング入門2 28 プログラムの解説 int int nx; *pt; 57 メモリ上の番地 1000番地 nx = nx = 57; pt = &nx; &nx 代入 pt nxのアドレス int *型 = int型のアドレスを格納するための変数 printf("nxの値:%d\n", nx); printf("nxのアドレス:%p\n", &nx); printf("ptの値:%p\n", pt); プログラミング入門2 57 同じアドレスを示す 29 int型とintへのポインタ型 int *型 int型 int nx; int nx *pt; pt その値は整数を格納する オブジェクトのアドレス その値は整数 &nx は(int *)型 pt (int *)型 プログラミング入門2 30 ポインタがオブジェクトを「指す」 ポインタptの値がオブジェクトnxのアドレスであるとき, pt は nx を指す という。 57 メモリ上の番地 int nx; int *pt; pt = &nx; 1000番地 = nx &nx 代入 nxのアドレス pt ptにはオブジェクトnxのアドレスが格納されているので, ptはnxを指している といえる。 プログラミング入門2 31 間接演算子 ソースファイル名:pointer2.c ポインタが指すオブジェクトの値を表示 #include <stdio.h> int main(void) { int nx; int *pt; nx = 57; pt = &nx; ptに,int型の変数nxのアドレスを代入 pt は nx を指す printf( "nxのアドレス:%p\n", &nx); printf( "nxの値:%d\n", nx ); printf( "ptの値:%p\n", pt ); printf( "*ptの値:%d\n", *pt ); return(0); } プログラミング入門2 32 ポインタとエイリアス(別名) int nx; int *pt; nx = 57; pt = &nx; pt は x を指す *pt はptが指すオブジェクトの エイリアス(別名) x x pt *pt pt *ptはxの別名(エイリアス) 重要 ポインタpがオブジェクトxを指すとき,*pはxのエイリアス(別名)となる プログラミング入門2 33 ポインタが指すオブジェクトへの代入 ソースファイル名:pointer3.c ポインタを介して,オブジェクトの値を変更 #include <stdio.h> int main(void) { int nx, ny; int *pt; *ptはnxのエイリアス ptはnxを指す pt = &nx; *pt = 100; printf( "nxの値:%d\n", nx ); printf( "*ptの値:%d\n", *pt ); nx pt = &ny; *pt = 300; printf( "nyの値:%d\n", ny ); printf( "*ptの値:%d\n", *pt ); return(0); } *pt 300 pt *ptに300を代入することは, nxに300を代入することと同じ プログラミング入門2 34 演習課題(第9回) 1. 以下のようなサッカー選手のデータファイルをエディタ(emacs)で作成せよ 。そのファイルから,選手名,アシスト数,ゴール数を読み込み,アシスト王 ,得点王,MVP(アシスト+得点がMAX)の選手を判定し,画面に結果を 表示するプログラムを作成せよ。(kadai9-1.c) 選手数は各自で決めて良い 最大値を求める関数を自分で設計し、使用すること 名前 アシスト Takahara Nakayama Magron Kazu ・・・・・・・ ・・・・・・・ プログラミング入門2 7 5 2 6 ゴール 20 13 8 10 ファイル:player.txt 35 演習課題 2. ポインタの理解(kadai9-2.c) 次の手順でプログラムを作成せよ。 Int型のオブジェクト x, y を宣言 Intへのポインタ型 ptr を宣言 x, yにそれぞれ,100,500を代入 ptr がオブジェクトxを指すようにする *ptrの値を表示 ptrを介して,xの値を400に変更 x, *ptrの値を表示 ptr がオブジェクトyを指すようにする *ptrの値を表示 ptrを介して,yの値を200に変更 y, *ptrの値を表示 プログラミング入門2 36 提出方法 演習課題1〜2のプログラムを作成し,動作確認した上で ソースファイル(kadai9-1.c 〜 kadai9-2.c) をメールに添付して提出せ よ。 提出方法: メールの題名(subjet)を, “pro2-9” + “学籍番号”+”自分の苗字”(全て半角英 数文字にて)とする。 例) pro2-9 L02001 aoki 提出先: [email protected] (青木アドレス) 提出期限: 12月10日(月) 13:00まで(時間厳守) プログラミング入門2 37
© Copyright 2024 ExpyDoc