Download presentation
Presentation is loading. Please wait.
1
情報処理Ⅱ 2008年1月28日(月)
2
本日学ぶこと ファイル入出力,標準入力・標準出力 記憶域管理関数(mallocなど) 問題
ファイルを入力にとり,先頭に行番号をつけて出力できる? 標準入力の内容を行ごとに逆順に出力できる? Wakayama University 1:Wakayama 2:University ./line Wakayama University amayakaW ytisrevinU ./liner
3
ファイル補足 Cでは,ファイルその他への入出力方法は文法で規定されていない.代わりに豊富なライブラリ(標準入出力ライブラリ)が規定されている. ストリームは,ファイルやコンソール(キーボード入力と画面表示)などを統一的に扱うためのものである. テキストストリームとバイナリストリームに分けられる.前回と今回の授業では,テキストストリームを対象とする. 「バイト列であること」は本質ではないが,とくにバイナリストリームでは,入出力の途中にナル文字('\0')があっても問題なく処理できなければならない. 例えばバイナリストリームを扱うときにfgetsで読み出すと,入力の途中にナル文字が入っていればおかしな動作になり得る.代わりにfreadを用いるべきである. リp
4
行番号問題1 仕様 コマンドライン引数(複数ある場合は最初のみ)をファイル名とみなして,そのまま出力していく.ただし行の先頭には行番号をつける. 考え方 fgetcを用いて1バイトずつ読み出す. 「行の先頭」とは,「ファイルの先頭」か「改行文字('\n')の直後」のいずれか. 「今何行目を読んでいるか」を保存する変数line_countを用意する. line.c
5
1文字ごとの読み出し(1) while ((c = fgetc(fp)) != EOF) {...} 'a' 'b' 'c' '\n'
これがストリーム ファイルabc プログラムの 内部状態 'a' 'b' 'c' '\n' FILE オブジェ クト fp '\0'がない点に注意 (文字列ではない) c = ??? 'a'
6
1文字ごとの読み出し(2) while ((c = fgetc(fp)) != EOF) {...} 'a' 'b' 'c' '\n'
プログラムの 内部状態 'a' 'b' 'c' '\n' FILE オブジェ クト fp c = ??? EOF '\n' リp.255
7
EOF(1) 「ファイルの終わり(End Of File)」を表す定数.
stdio.hで,#define EOF (-1)などと定義されている. unsigned char signed char ASCII (7ビット) 入p.189で「EOFを入力」という表現が見られるが, ユーザがEOFに相当する値をコンピュータに送るわけではないし, ファイルにEOFに相当する値が格納されるわけではない. (ただし,MS-DOSなどの環境で,テキストファイルの途中でも特定のバイトコードがあれば, それをファイルの終わりとみなすという使い方はある.) -128 -1 127 255 EOF ナル 文字 入p.189 リp.457
8
EOF(2),ファイルアクセス 文字(バイト)単位で読み書きするとき 文字列単位で(何バイトか一括して)読み書きするとき
int型を使用する. signed char型だと,255をEOFと区別できない. unsigned char型だと,EOFが来ても255にしてしまう. EOFから255までの「257種類の値」は,char型(signed char型,unsigned char型)で区別できない! 読み出してEOFが来たら,そこでおしまいにする. ライブラリ関数はfgetc, getcharなど 文字列単位で(何バイトか一括して)読み書きするとき char配列またはchar*型を使用する. ライブラリ関数はfgets, freadなど リp.461, p.471, p.462, p.466
9
標準入力と標準出力 標準入力(standard input, stdin) 標準出力(standard output, stdout)
通常はコンソール入力(キーボード入力) 標準出力(standard output, stdout) 通常はコンソール出力(画面表示) シェルのリダイレクション機能を用いて変更可能. 実行例: find ~ > files 実行例: ./liner < /usr/share/dict/words シェルのパイプ機能を用いて,あるプログラムの標準出力と別のプログラムの標準入力を接続できる. 実行例: echo '1+2*3' | bc stdin,stdoutは FILE *型の値(ファイル ポインタ)として利用 可能.ただし代入は できない. 入pp リp.480
10
標準入力・標準出力とコンソール 実行環境 (OSなど) 標準入力 実行プログラム コンソール 'a' 'b' 'c' '\n' 'a'
$ ./liner abc 1: abc $ 'a' 'b' 'c' '\n' '1' ':' ' ' 標準出力 実行コマンド 入力(エコーバック) 出力 入p.188
11
標準入力に関するライブラリ関数 int getchar(void); char *gets(char *s);
標準入力から1文字(1バイト)読み込む. 戻り値はunsigned char型の値,もしくは定数EOF. char *gets(char *s); 標準入力から1行読み込み,sの指す領域に格納する. 1行が,sの領域サイズよりも大きいとしても,そのまま格納しようとする. int scanf(const char *format, ...); formatに従って標準入力から入力を読み込み,第2引数以降が参照する領域に格納する. 読み込みに失敗すれば,入力が進まない. なるべく使わない! なるべく使わない! リp , pp , pp
12
標準出力に関するライブラリ関数 int putchar(int c); char *puts(const char *s);
cをunsigned char型に変換した上で,標準出力に(1文字)書き出す. char *puts(const char *s); 文字列sと改行文字を標準出力に書き出す. int printf(const char *format, ...); formatに従って標準出力に書き出す. 「標準出力に書き出す」の主語は,それぞれのライブラリ関数である. 「画面に表示する」ではない. ただし,「画面に表示される」というのは,利用するプログラマの観点とした表現であり,これは差支えない(利用する状況にも夜が). 「書き出す」「読み出す」はそれぞれ,「書き込む」「読み込む」と表現されることもある. リp.474, p.473, pp
13
標準入出力と標準入出力関数 標準入力・標準出力に対する関数は,標準入出力関数およびstdin, stdoutを用いた関数形式マクロにより定義されている(ことがある). 例: #define getchar() getc(stdin) 第2回レポート課題の for (k = 0; k < face_byte; k++) { putchar(((i == x && j == y) ? face_set_bold : face_set)[face_byte * c + k]); } は fwrite(((i == x && j == y) ? face_set_bold : face_set) + face_byte * c, 1, face_byte, stdout); に置き換えられる. リpp
14
行番号問題2 仕様 考え方 標準入力から入力をとり,各行の文字列を逆順にして標準出力に書き出す.
読むのはgetchar,書くのはputchar. 1行を読んでから,逆順に出力する. 行単位で,1行の入力内容を保持する. 標準入力は後戻りができない! 実行開始時に字数はわからないので, 「記憶域管理関数」を使用する. 「標準入力から入力し」と書いてはいけない. 一般に,入力「する」のはプログラムではないから. 一方,「出力する」と書くのは,その主語はプログラムなので,問題ない. ただし「標準出力に出力する」と表記すると,「出力」が2箇所出現してくどいので, 「標準出力に書き出す」とここでは書いた. なお,「画面に表示する」という表現がよく見られるが,これも,画面に表示「する」のは プログラムではないので,その意味では不適切であるが,古くから用いられ,また オンラインマニュアルの日本語訳でもよく見かけるので,「表示する」と書いて 試験で減点するようなことはしない. liner.c
15
liner.cの概要 定義した型 定義した関数 struct Line, line:1行の文字列の情報
「本体(具体的な中身,コンテンツ)」と「確保した要素数」と「格納されている要素数」をメンバに持つ. 定義した関数 init_line: 1行構造体を作成する enlarge_line: 1行構造体の本体容量を拡張する free_line: 1行構造体を開放する get_line: 1行読み出す print_reverse: 逆順に出力する
16
実行時の領域確保について プログラム実行時(main関数に制御が移る前)に ブロック({...})が実行されるときに
static変数のオブジェクトが確保,初期化される. プログラム終了時に破棄される. ブロック({...})が実行されるときに auto変数のオブジェクトが確保される. ブロック終了時に破棄される. 記憶域管理関数(malloc, callocなど)を呼び出すと オブジェクトとして使用できる領域が確保される. freeなどの関数が呼び出されるか, プログラム終了時に破棄される. スタック領域 mallocは,リストやツリーのプログラムをCで書くときに必要になる. 例えば,ノード(節点)を自己参照構造体で定義し,新たなノードを作るときに,mallocなどでその構造体オブジェクトを生成させる. ヒープ領域 入pp
17
mallocの基本的な使い方 準備 p = (int *)malloc(sizeof(int)); ⇒ *p がint型変数として利用可能.
#include <stdlib.h> int *p; p = (int *)malloc(sizeof(int)); ⇒ *p がint型変数として利用可能. p = (int *)malloc(sizeof(int) * 10); ⇒ p[0], ..., p[9] がint型変数として利用可能. 必ずポインタ型 (intの部分は用途による) p p[0] p[1] p[9] … sizeof(int) *10 バイトの領域 リp.235, p.341, p.501
18
malloc使用の注意点 領域の値は不定であるため,必要に応じて初期化する.代わりにcallocを使用すれば,すべて0に初期化された領域が得られる. 代入される変数はポインタ変数なので,左辺値になり得る(p++; などとできる). 領域確保に失敗するとNULLを返すので, if ((p = (int *)malloc(sizeof(int) * 10)) == NULL) { エラー処理 } などとするのが一般的. リp.497
19
可変長配列 可変長配列の例 可変長配列をCで取り扱うときは, 実行時にその個数(の上限)がわからないような配列
行番号問題2では,「1行の本体(具体的な中身)を格納する領域」が該当する. 可変長配列をCで取り扱うときは, mallocまたはcallocで初期化し, より大きな領域が必要になったら,reallocを用いるとよい. liner.c では,line構造体も,mallocで領域を確保しfreeで開放しているが,これは可変長配列ではない. 実のところ,レポート課題2と同様に,init_line関数の中にstaticをつけたline構造体の変数を定義し,その参照を返すのでも,動作する.ただしliner.cを相当修正しないといけないが. ファイルの1行を読み出す方法については,1行の長さの上限を決めておき,そのサイズ(ナル文字も格納できるように!)のchar配列変数を確保し,そこにfgetsなどで格納することで,mallocやfreeを使わないコードにするのがポピュラーであり,2年前期の演習でもそれで問題ないと思われる.2年後期の情報ネットワーク演習では,長さに制限のない情報を受け取るためのプログラムが必要となる. リp.505
20
後始末 ファイルの読み書きを終えたら,fcloseを用いる. ヒープ領域の内容を解放するには,freeを用いる.
「fopen/fclose」をペアで覚える. fcloseを呼び出す前のファイルの出力内容は,プログラム内に保持されている(バッファリング)可能性がある. プログラム終了時に,閉じられていないファイルは保存されるが,これに頼らない(積極的にfcloseを用いる)ほうがよい. ヒープ領域の内容を解放するには,freeを用いる. 「malloc/free」をペアで覚える. プログラム終了時に,freeされていない領域も破棄されるが,できれば頼らない(可能ならfreeを用いる)ほうがよい. 反復(whileなど)の中で領域を動的に確保して,1回の(反復内の)処理の中で用済みになれば,freeすべきである. 一つのプログラムの実行中に多数の領域を動的に確保して相互に参照させる(線形リストや木構造をプログラムにするなど)とき,用済みになったらfreeするのは,バグのもとになりやすく,勧められない. リpp , p.499
21
まとめ ファイルは,実行プログラムとは異なる生存期間を持つ,バイト列のデータ構造である.
ファイル,標準入力,標準出力にアクセスするためのさまざまな標準入出力関数が存在する. 実行後のオブジェクト確保には,mallocなどを用いる. 後始末を忘れずに.
22
プログラミング講義を終えて(1) 授業内容の関連 難しかったテーマを復習するときは,関連する(一つ前や後の)テーマを見直すのもよい. 構造体
入出力 ライブラリ関数 再帰呼び出し その他の型 前処理指令 関数 変数の有効範囲 (新設) 記憶域管理関数 (名称変更) 算術型⇒算術型・型変換 ファイル入出力⇒入出力 制御文 演算子 ポインタ 記憶域管理関数 識別子 算術型・型変換 配列・文字列
23
プログラミング講義を終えて(2) 2年以降で身につけてほしいこと 自在にCでプログラミングできる能力
特に,ファイル処理,文字列処理,リストとツリー,スタックとキュー 「先人の知恵」を学び活用する能力 ライブラリ関数,先生・先輩のノウハウ プログラムを設計する能力 与えられた課題に対して,何をすべきかを自主的に決め,プログラムコードや文章で表現すること 「データ構造」や「関数」として適切なものを定めれば, スムーズに実装できる 入pp
24
今後の日程 1月30日(水):おさらい問題 2月4日(月):試験 何でも参照可.周りと相談可.成績には関係ない.
3分復習問題,40分おさらい問題,45分解説 2月4日(月):試験 80点満点 自筆ノート1冊の参照を認める 教科書・書籍,印刷資料等は参照不可
Similar presentations
© 2024 slidesplayer.net Inc.
All rights reserved.