ソフトウェア工学入門
第4回 ライブラリ関数
ライブラリ関数 stdio
stdio : 標準入出力ライブラリ
カーネルレベルのストリームにAPIを追加し、インタフェースを提供する カーネル プログラムstdio
read(2) バッファ BUFSIZ write(2) fd BUFSIZごと 小さい単位 バッファ : 一時的にデータを保存しておく場所のこと バッファリング : バッファを経由してデータをやり取りすること バイト単位の遅いシステムコールより、素早く読み書きができるFILE型の正体
FILE型 : ストリームを表現するために使用される型
FILE型の中にはファイルディスクリプタとstdioバッファ情報が入っている ファイルディスクリプタ 正式名称 stdioでの変数名 意味 0 STDIN_FILENO stdin 標準入力 1 STDOUT_FILENO stdout 標準出力 2 STDERR_FILENO stderr 標準エラー出力fopen
fopen : stdioにおいてシステムコール open() に相当するAPI
#include <stdio.h>
FILE *fopen ( const char *path , const char *mode ) ;
ファイルをパス指定するとファイルに繋がるストリームを作り、FILE型へのポインタを返す 第二引数のmodeはストリームの性質を示す 値 mode 意味 “r” O_RDONLY 読み込み専用 “w” O_WRONLY_CREATEIO_TRUNC 書き込み専用 “a” O_WRONLY_CREATEIO_APPEND 追加書き込み専用 “r+” O_RDWR 読み書き両用 “w+” O_RDWRIO_CREATEIO_TRUNC 読み書き両用 “a+” O_RDWRIO_CREATEIO_APPEND 読み書き両用
fclose
fclose : stdioにおいてシステムコール close() に相当するAPI
#include <stdio.h>
int fclose ( FILE *stream ) ;
fgetc() と fputc()
#include <stdio.h>
Int fgetc (FILE *stream );
Int fputc ( int c , FILE *stream );
バイト単位の入出力
API
fgetc( ) : 引数のstreamから1バイト読み込んで、バイト数を返す ストリームが終了するとEOFを返す
getchar()とputchar
#include <stdio.h> Int getchar (void); Int putchar ( int c );
入力元と出力先が固定されているバイト単位の入出力
API
getchar( ) : fgetc( stdin) と同じ意味
つまり、標準入力から1バイト読み込んで、バイト数を返す putchar( ) : fputc(c , stdout) と同じ意味
stdio版cat ①
#include <stdio.h> #include <stdlib.h> int
main(int argc, char *argv[]) {
int i;
for (i = 1; i < argc; i++) { FILE *f; int c; f = fopen(argv[i], "r"); if (!f) { perror(argv[i]); exit(1); } 次のプログラムをcat2.c という名前で作成し、保存してください
stdio版cat ②
while ((c = fgetc(f)) != EOF) { if (putchar(c) < 0) exit(1); } fclose(f); } exit(0); } 次のプログラムをcat2.c という名前で作成し、保存してください ビルドして、実行してみましょう >gcc -o cat2 cat2.c >ls
文字列処理 ① ルール
文字列(バイト列)の末尾に \n を付けることで1行を意味する
Hello World! \n もじもじもじもじもじ \n Gooooooooooooogle \n
例文 文字列 行の列
Hello World! \n
もじもじもじもじもじ \n
Gooooooooooooogle \n
文字列処理 ② fgets()
#include <stdio.h>
char *fgets ( char *buf , int size , FILE *stream );
文字列の行単位の入力
API
fgets : ストリーム stream から1行読み込んで、第1引数のbufに格納する この際、最大 size-1バイトまでしか読み込まない
正常に読み込めば buf を返す
文字列処理 ③ fputs()
#include <stdio.h>
int fputs ( const char *buf , FILE *stream );
文字列の行単位の出力
API
fputs : 文字列 buf をstream に書き込む 正常に読み込めば 0 を返す
文字列処理 ④ printf()とfprintf()
#include <stdio.h>
int printf ( const char *fmt , . . . );
int fprintf ( FILE *stream , const char *fmt , . . . );
文字列の出力
API
printf : 引数 fmt で指定した体裁に従って、後続の文字列を標準出力する fprintf :引数 fmt で指定した体裁に従って、後続の文字列をstreamに出力する printf は便利なため多用されるが、バッファオーバーフローの原因になる バッファオーバーフロー : バッファをはみ出してメモリを使ってしまうこと データが破壊されるなどの問題を生じる
固定長入出力
Stdio経由で行う固定長バイト列の入出力
#include <stdio.h>size_t fread ( void *buf , size_t size , size_t nmemb , FILE *stream ) ;
size_t fwrite (const void *buf , size_t size , size_t nmemb , FILE *stream ); fread : stream から size × nmemb バイトを読み込み、buf に格納する
読み込みに成功すれば nmemb を返す 失敗すると nmemb より小さい値を返す
fwrite : size × nmemb 分のバイト列をbuf からstreamに書き込む 読み込みに成功すれば nmembを返す
失敗すると nmemb より小さい値を返す
read()やwrite()はシステムコールだが、fread()とfwrite()はライブラリ関数
ファイルオフセット操作
ファイルオフセット操作のAPI
#include <stdio.h>int fseek ( FILE *stream , long offset , int whence ) ; long ftell (FILE *stream );
void rewind (FILE *stream );
lseek()はシステムコールだが、fseek()はライブラリ関数
システムコールはUNIXでしか使えないが、ライブラリ関数はC言語で使える
fseek : stream のファイルオフセットをwhence と offset で示される位置に移動する
ftell : stream のファイルオフセットの位置を返す
rewind : stream のファイルオフセットの位置を先頭に戻す
ラッパー
カーネル プロセス ストリームA ストリームB ストリームC 0 1 2 1 ファイルディスクリプタ バッファ ストリームFILE
FILE型 : ストリームを表現するために使用される型
ラッパー : APIで覆って機能を追加すること
FILE
はファイルディスクリプタのラッパーである
追加機能stdio
ファイルディスクリプタとFILE操作
ファイルディスクリプタとFILE操作のAPI
#include <stdio.h>int fileno ( FILE *stream ) ;
FILE *fdopen ( int fd , const char *mode );
fileno : stream がラップしているファイルディスクリプタの値を返す fdopen :ファイルディスクリプタ fd をラップする FILE型の値を新しく作成し、ポインタを返す 第二引数のmodeはストリームの性質を示す 値 mode 意味 “r” O_RDONLY 読み込み専用 “w” O_WRONLY_CREATEIO_TRUNC 書き込み専用 “a” O_WRONLY_CREATEIO_APPEND 追加書き込み専用 “r+” O_RDWR 読み書き両用 “w+” O_RDWRIO_CREATEIO_TRUNC 読み書き両用 “a+” O_RDWRIO_CREATEIO_APPEND 読み書き両用
stdio動作 演習 ①
>strace ./hello > /dev/null
execve("./hello", ["./hello"], [/* 56 vars */]) = 0 uname({sys="Linux", node="cl0028", ...}) = 0
brk(0) = 0x804955c
old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE| MAP_ANONYMOUS, -1, 0) = 0x40015000
open("/etc/ld.so.preload", O_RDONLY) = -1 ENOENT (No such file or directory) open("/usr/local/lib/i686/mmx/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat64("/usr/local/lib/i686/mmx", 0xbfffee64) = -1 ENOENT (No such file or directory)
open("/usr/local/lib/i686/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
………..
straceコマンドを使って、以前作成したhelloの動作を見てみましょう
stdio動作 演習 ②
以前作ったcatを使って、システムコールread, write, open, close の動作を見てみましょう
2048バイト単位のreadが1回、102バイト単位のwriteが1回行われています 102×2=204バイトの転送が行われています
>strace -e trace=open,read,write,close ./cat hello.c > /dev/null ……….. close(3) = 0 open("/lib/i686/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\340\\\1"..., 512) = 512close(3) = 0 open("hello.c", O_RDONLY) = 3
read(3, "#include <stdio.h>\n\nint\nmain(int"..., 2048) = 102 write(1, "#include <stdio.h>\n\nint\nmain(int"..., 102) = 102 read(3, "", 2048) = 0
stdio動作 演習 ③
以前作ったcat2を使って、システムコールread, write, open, close の動作を見てみましょう
1024バイト単位のreadが1回、102バイト単位のwriteが1回行われています 102×2=204バイトの転送が行われています
メモリが1024バイト節約されているということですね → バッファリング効果
>strace -e trace=open,read,write,close ./cat2 hello.c > /dev/null ………. open("/etc/ld.so.cache", O_RDONLY) = 3 close(3) = 0 open("/lib/i686/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\340\\\1"..., 512) = 512close(3) = 0 open("hello.c", O_RDONLY) = 3
read(3, "#include <stdio.h>\n\nint\nmain(int"..., 1024) = 102 read(3, "", 1024) = 0
close(3) = 0
第一回個別試験告知
日時: 5月14日(木) 8:30~10:00
場所: S14生 情報棟5階AV実習室
S10,S11,S12,S13生 大学院棟4階計算機実験室
当日スケジュール
8:40 答案配布
9:50 答案回収(出席確認のため)
10:00までにメールにてプログラムを送付
※ 他人との相談は厳禁です。
cat2.cの場合、次のように実行すると何も起こらない >./a.out > 課題の場合、次のように実行すると標準入力を受け付ける >./a.out 5 ← 標準入力 5 3 ← 標準入力 3 2 ← 標準入力 2