情報処理Ⅱ 第10回:2004年1月13日(火)
本日のテーマ 標準入出力 標準入力と標準出力 いくつかの標準入出力関数 記憶域管理関数 malloc, free など 実行時に配列領域を確保する手法 落ち穂拾い 入力の与え方 コメントの書き方 制御文 知っておくべき「 C 言語の文法」
問題(授業がつまらない人のた めに) ファイルを入力にとり,その中で最も長い行を出力 せよ. 最も長い行 = バイト数が最も多い行 複数ある場合は最初の一つのみ出力する. 標準入力に入力されたデータの中で,最も長い行を 出力せよ.
落ち穂拾い1:入力の与え方 プログラム内に書き込む. int a = 44, b = 16; int a = 44, b = 16; コマンドライン引数から獲得する. int main(int argc, char *argv[ ]) int main(int argc, char *argv[ ]) 標準入力(キーボード入力)から獲得する. scanf を用いる. getchar や fgets などを用いる. ファイルをアクセスする. fopen, fread などを用いる.
入力の与え方:得失(1) プログラム内に書き込む(埋め込む). ハードコーティングとも呼ばれる. 手軽(原始的)であり,他の環境でも実行しやすい. 入力の値の型は,プログラム内で指定できる. 入力の値が変わるたびにコンパイルが必要. コマンドライン引数から獲得する. 入力の値が変わってもコンパイル不要. 実行時に毎回引数指定が必要.ただしシェルのヒス トリ機能を使えば省力化できる. 入力サイズには(現実的な)制限がある. 入力の値の型は必ず文字列. メリットとデメ リット
入力の与え方:得失(2) 標準入力から獲得する. 入力の値が変わってもコンパイル不要. 入力サイズに制限がない. 実行時に毎回入力が必要.ただしシェルのリダイレ クション機能を使えば省力化できる. 入力の値の型は原則として文字または文字列. ファイルをアクセスする. 最も洗練された手法. 標準入力の特長を受け継ぐ. ファイルの内容を変えなければ,同じ入力が得られ る. プログラムは複雑になる.
入力の与え方:比較 コマンドライン引数 ファイルアクセス 標準入力 埋め込み 原始的 固定 洗練 柔軟 静的 ( main 関数実行前に 入力値が決まる) 動的 (実行中に入力値を 与える)
標準入力と標準出力 stdin 標準入力 (standard input, stdin) 通常はコンソール入力 ( キーボード入力 ) stdout 標準出力 (standard output, stdout) 通常はコンソール出力 ( 画面表示 ) シェルのリダイレクション機能を用いて変更可能. 実行例 : find > files 実行例 : patch -p0 < patch シェルのパイプ機能を用いて,あるプログラムの標 準出力と別のプログラムの標準入力を接続できる. 実行例 : echo '1+2*3' | bc
例題1 標準入力から入力を受け取り,各行の先頭に行番号 をつけて標準出力に出力する.
標準入力・標準出力とコンソー ル 実行環境 ( OS など ) abc\n... 1:abc\n... 標準入力 標準出力 は 1 バイト 実行プログラム コンソール 実行コマンド 入力(エコーバック) 出力
標準入力に関するライブラリ関 数 int getchar(void); 標準入力から 1 文字 (1 バイト ) 読み込む. 戻り値は unsigned char 型の値,もしくは定数 EOF . char *gets(char *s); 標準入力から 1 行読み込み, s の指す領域に格納する. 1 行が, s の領域サイズよりも大きいとしても,その まま格納しようとする. int scanf(const char *format,...); format に従って標準入力から入力を読み込み,第 2 引 数以降が参照する領域に格納する. 読み込みに失敗すれば,入力が進まない. なるべく使わな い!
標準出力に関するライブラリ関 数 int putchar(int c); c を unsigned char に変換した上で,標準出力に (1 文 字 ) 出力する. char *puts(const char *s); 文字列 s と改行文字を標準出力に出力する. int printf(const char *format,...); format に従って標準出力に出力する.
例題2 標準入力から入力を受け取り,最も長い行を出力す る. ただし, 255 バイト以上の行には対処できない. 対策 「 1 行は 255 バイトまで」を仕様にする. 配列に格納できる字数を大きくする. 配列領域を実行時に確保する.
ヒープ領域を 用いる. 実行時の領域確保について プログラム実行時 (main 関数に制御が移る前 ) に static 変数のオブジェクトが確保,初期化される. プログラム終了時に破棄される. ブロックが実行されるときに auto 変数のオブジェクトが確保される. ブロック終了時に破棄される. 記憶域管理関数 (malloc, calloc など ) を呼び出すと オブジェクトとして使用できる領域が確保される. free などの関数が呼び出されるか,プログラム終了時に 破棄される. スタック領域 を用いる.
malloc の基本的な使い方 準備 #include #include int *p; int *p; p = (int *)malloc(sizeof(int)); p = (int *)malloc(sizeof(int)); とすると, *p が int 型変数として利用可能. p = (int *)malloc(sizeof(int) * 10); p = (int *)malloc(sizeof(int) * 10); とすると, p[0],..., p[9] が int 型変数として利用可能. p sizeof(int) *10 バイトの領域 p[0]p[1]p[9]…
malloc 使用の注意点 領域の値は不定であるため,必要に応じて初期化す る.代わりに calloc を使用すれば,すべて 0 に初期 化された領域が得られる. p はポインタ変数なので,左辺値になり得る( p++; などとできる). if ((p = (int *)malloc(sizeof(int) * 10)) == NULL) { エラー処理 } 領域確保に失敗すると NULL を返すので, if ((p = (int *)malloc(sizeof(int) * 10)) == NULL) { エラー処理 } などとするのが一般的.
例題3(例題2の改良) 標準入力から入力を受け取り,最も長い行を出力す る. 1 行のバイト数制限をなくす. ついでに,入力に '\0' があっても適切に処理するよ うにする.
(一般的な)標準入出力関数 操作対象(関数への入力)は,ファイルポインタ. stdio.h で定義されている「ファイル構造体」のポイ ンタ. 型は FILE * 標準入力・標準出力に対する関数は,標準入出力関 数および stdin, stdout を用いた関数形式マクロによ り定義されている ( ことが多い ) . 例 : #define getchar( ) getc(stdin) 引数なしの関数形式マク ロ (void を書かない )
落ち穂拾い2:コメントの書き 方 コメント(注釈)の書き方 /* … */ /* … */ // … // … #if 0 … #endif #if 0 … #endif 書くべき箇所 ファイルの先頭 : ファイル名,作成者,作成日時など 関数定義の直前 : 機能,入出力など 型,変数,定数などを定義する行 : 意味・役割など 留意すべきプログラムコードの近辺 : 処理内容 書いてはいけない プログラムコードを見れば明らかな処理内容 複数行可.入れ子にできない. 行末まで.最近の規格で認められた. 行単位.入れ子にできる.本来の使い 方ではないので,「清書」では書かな い.
落ち穂拾い3:制御文 (1) if ( 条件 ) 処理 ;if ( 条件 ) { 処理 ;} if ( 条件 ) 処理 ; と if ( 条件 ) { 処理 ;} は,「処理」が (文法的に,一つの)文であれば,同じ.常に後者 の書式を用いるプログラマも多い. else, while, for にも当てはまる. if と,真のときに実行する文や else との対応に注意. if ( 条件 1) if ( 条件 2) 処理 1; else 処理 2; if ( 条件 1) { if ( 条件 2) 処理 1; } else 処理 2; if ( 条件 ) 処理 1; 処理 2; if ( 条件 ) { 処理 1; 処理 2; } 違う ! 条件の真偽にかかわらず 処理 2 を実行する. 条件 1 が真,条件 2 が偽のとき に限り,処理 2 を実行する. 違う !
落ち穂拾い3:制御文 ( 2 ) while や for のループ中に break break があれば,そこでループを抜ける. continue continue があれば,それ以降の処理を行わずに条件判 定に戻る (for ループでは増分処理を行う ) . 無限ループの決まり文句 while (1) { 処理 } while (1) { 処理 } for (;;) { 処理 } for (;;) { 処理 } 空文を使ったループ while ( 代入および条件判定 ); while ( 代入および条件判定 ); for ( 初期化 ; 条件判定 ; 増分処理 ); for ( 初期化 ; 条件判定 ; 増分処理 ); 通常,処理の中に break を置き,何ら かの条件でループから抜けるように 書く. セミコロンの前で 改行する(セミコ ロンのみの行を作 る)書法もある.
知っておくべき「 C 言語の文 法」 型 算術型,ポインタ型,関数型,構造体 (struct) と共用 体 (union) , typedef ,列挙型 (enum) 制御文 if, while, for, do ~ while, switch ~ case return, break, continue 配列とポインタ 演算子および式評価 識別子(変数,関数,ラベル) 前処理指令 青線部 : 主な出題範囲
今後の予定 1月20日(火):おさらい 1月27日(火):試験 2月3日(火) :補講.プログラミング能率向上 のノウハウについて(採点対象外)
試験について 日時:1月27日(火) 14:50 ~ 16:20 場所: A203 以下の2点の持ち込みを認める. C 言語に関する書籍 1冊 自筆ノート1冊,もしくは,シート1枚 ルーズリーフも許可するが,常にバインダに綴じておき, シートを取り出さないこと(カンニング防止のため) 持ち込み不可のもの 計算機類(電卓,ノート PC など) 2枚以上のシート(カンニング防止のため.ステー プラなどで綴じていても不可とする.) シートなどを貼り付けた書籍やノート
発展的な話題 : なぜ scanf はいけ ないのか u.ac.jp/~maekawa/lect/C/file-io.html が詳しい. while (scanf(…) != EOF) while (scanf(…) != EOF) は厳禁 ! 変換に失敗すれば,無限ループになる. 代替案 : while (scanf(…) == 変換の個数 ) while (scanf(…) == 変換の個数 ) 変換に失敗すれば,そこで while ループを抜ける. fgets で文字列領域に格納し, sscanf で変換する. 変換に失敗しても,その入力だけを読み飛ばせる.