情報処理II

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