情報処理Ⅱ 2008年1月21日(月).

Slides:



Advertisements
Similar presentations
プログラミング演習 II 2005 年 1 月 19 日(第 9 回) 理学部数学科・木村巌. 前回までの復習 共用体( union type ) 共用体( union type ) 列挙 (enumerated type ) 列挙 (enumerated type ) 構造体、構造体のポインタ、
Advertisements

情報処理Ⅱ 第12回 2005 年 01 月 18 日(火). 2 本日学ぶこと 前処理指令 関数プロトタイプ ライブラリ関数の活用 関数は  すでにあるものを使うか?  関数として定義するか?  関数形式マクロとして定義するか?
情報・知能工学系 山本一公 プログラミング演習Ⅱ 第3回 配列(1) 情報・知能工学系 山本一公
情報処理演習C2 ファイル操作について (2).
情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
数理情報工学演習第一C プログラミング演習 (第3回 ) 2014/04/21
システムプログラミング 第5回 情報工学科 篠埜 功 ヒアドキュメント レポート課題 main関数の引数 usageメッセージ
第2回ネットワークプログラミング 中村 修.
第13回 プログラミングⅡ 第13回
記憶クラス 変数をどのような記憶領域に割り当てるかを指定するのが記憶クラス 記憶クラスには、自動変数、静的変数、外部変数などがある。
第6章 2重ループ&配列 2重ループと配列をやります.
情報処理Ⅱ 第13回 2006年1月20日(金).
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
情報処理Ⅱ 2006年1月13日(金).
関数 関数とスタック.
情報処理Ⅱ 第13回 2004年01月25日(火).
10: ファイル入出力 C プログラミング入門 基幹2 (月4) Linux にログインし、以下の講義ページ を開いておくこと
岩村雅一 知能情報工学演習I 第8回(後半第2回) 岩村雅一
情報処理Ⅱ 2007年11月5日(月).
プログラミング2 関数
Cプログラミング演習 第6回 ファイル処理と配列.
プログラミング 2 ファイル処理.
プログラミング論 ファイル入出力
Cプログラミング演習.
第10回関数 Ⅱ (ローカル変数とスコープ).
情報・知能工学系 山本一公 プログラミング演習Ⅱ 第2回 ファイル処理 情報・知能工学系 山本一公
プログラミング 4 記憶の割り付け.
プログラミング演習I 2003年6月25日(第10回) 木村巌.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
プログラミング入門2 第11回 情報工学科 篠埜 功.
第9回関数Ⅰ (簡単な関数の定義と利用) 戻り値.
プログラミング入門2 第11回 情報工学科 篠埜 功.
第7回 プログラミングⅡ 第7回
岩村雅一 知能情報工学演習I 第10回(後半第4回) 岩村雅一
復習 前回の関数のまとめ(1) 関数はmain()関数または他の関数から呼び出されて実行される.
プログラミング論 ファイル入出力
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
岩村雅一 知能情報工学演習I 第9回(C言語第3回) 岩村雅一
情報処理Ⅱ 第14回 2006年1月23日(月).
第4回 ファイル入出力方法.
岩村雅一 知能情報工学演習I 第12回(C言語第6回) 岩村雅一
疑似乱数, モンテカルロ法によるシミュレーション
情報処理Ⅱ 2007年1月19日(金).
C言語 はじめに 2016年 吉田研究室.
岩村雅一 知能情報工学演習I 第9回(後半第3回) 岩村雅一
15.1 文字列処理の基本 15.2 文字列処理用ライブラリ関数
プログラミング演習I 2003年7月2日(第11回) 木村巌.
ファイルの読み込み, ファイルからのデータの取り出し, ファイルの書き出し
情報処理Ⅱ 2008年1月28日(月).
第5回 プログラミングⅡ 第5回
情報処理Ⅱ 2006年11月24日(金).
情報処理Ⅱ 第7回 2004年11月16日(火).
情報処理Ⅱ 2007年1月26日(金).
コンパイラ 2012年10月11日
プログラミング 4 文字列.
岩村雅一 知能情報工学演習I 第12回(後半第6回) 岩村雅一
プログラミング入門2 第6回 関数 情報工学科 篠埜 功.
プログラミング言語論 第九回 理工学部 情報システム工学科 新田直也.
情報処理Ⅱ 2007年2月2日(金).
プログラミング演習I 2003年6月11日(第9回) 木村巌.
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
プログラミング言語論 第九回 理工学部 情報システム工学科 新田直也.
オブジェクト指向言語論 第七回 知能情報学部 新田直也.
情報処理Ⅱ 2005年11月25日(金).
プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。)
情報処理Ⅱ 第9回:2003年12月16日(火).
第4回 配列.
情報処理Ⅱ 小テスト 2005年2月1日(火).
第5回 配列.
岩村雅一 知能情報工学演習I 第7回(後半第1回) 岩村雅一
Presentation transcript:

情報処理Ⅱ 2008年1月21日(月)

本日学ぶこと 前処理指令 マクロ 問題 サイコロを何度も振って,全ての目が最低1回出るまで,何回振らなければならないか? 最小6回,上限なし(∞回?) 欲しいのは現実的な値 100面のサイコロだったら?

前処理とコンパイル(1) 前処理・コンパイル・アセンブル・リンクの各処理は通常,コンパイラ(ccなど)が一手に引き受ける. ソースファイル (前処理前) ソースファイル (前処理後) 前処理 コンパイル アセンブル オブジェクト ファイル 実行ファイル リンク 前処理・コンパイル・アセンブル・リンクの各処理は通常,コンパイラ(ccなど)が一手に引き受ける. 入p.115 リp.15

前処理とコンパイル(2) 前処理は, 前処理のコマンド(プリプロセッサ,前処理系)は,cpp 狭義には,「コンパイルに先立って行われる処理」であり,したがってコンパイルとは別 広義には,ccでコンパイルすれば自動的に処理してくれる,という意味でコンパイル作業の一部 前処理のコマンド(プリプロセッサ,前処理系)は,cpp Cの前処理以外にも使用可能 入p.113 リp.380

前処理指令 (Preprocessing directive) 「プリプロセッサ 指令」ともいう マクロ定義(#define) オブジェクト形式マクロ ⇒「定数」の定義 関数形式マクロ ⇒「関数もどき」の定義 マクロとは…コンピュータ関連の作業において,複数の機能や意味をまとめて扱えるようにしたもの ソースファイルの取り込み(#include) ⇒12月3日その2参照 条件付きコンパイル(条件付き取り込みともいう.#if ... #endif など) 「マクロとは」の説明は, http://d.hatena.ne.jp/keyword/%A5%DE%A5%AF%A5%ED による. リp.380

要素数は,コンパイル時に評価可能な定数式なので,問題なし オブジェクト形式マクロ(1) 語の置き換えを行う. #define 置換対象 置換内容 #define WORD_SIZE 6 と記述すると,それ以降 int a[WORD_SIZE]; は int a[6]; と同じ意味になる. プログラム修正により変わり得る定数値があるときに,よく用いられる. 配列の上限値,他と区別する値など. うまく使うことで,定数値を変えるときのプログラム修正箇所を少なくできる. 要素数は,コンパイル時に評価可能な定数式なので,問題なし 列挙型のほうがいいかも リpp.384-388

オブジェクト形式マクロ(2) 注意点 前提: #define WORD_SIZE 6+1 単純に置き換える. int a[WORD_SIZE * 2]; は,int a[6+1 * 2]; に置き換えられる(意図した動作ではない). ⇒ #define WORD_SIZE (6+1) とすればよい. 語のみを置き換える. print_WORD_SIZE() のような「語の一部」や,printf("WORD_SIZE"); のような「文字列中の語」は,置き換えない.

オブジェクト形式マクロ(3) 注意点(続き) 予約語も置換可能. #define char signed char は文法上問題ないが,よい書き方ではない. typedef signed char schar; とすべきである. 置換内容のない名前も定義できる. #define DEBUG 末尾にセミコロンをつけない. #define WORD_SIZE 6; は(たいていの場合)間違い.

関数形式マクロ(1) オブジェクト形式マクロとほぼ同じ書式. 置換対象に「(…)」をつける. #define pint(x) printf("%d\n",x) に対して,pint(a+1); は printf("%d\n", a+1); に置き換えられる. 複数の引数をとることもできる.そのときは,置換対象の各引数をカンマで区切る. カッコ内に何も書かなければ,引数なしの関数形式マクロが定義される. リpp.388-390

関数形式マクロ(2) 注意点 単純に置き換える. #define mul(x, y) x*y に対して, z=mul(6+1,2); としたとき,z=14ではなくz=8となる. ⇒ #define mul(x, y) ((x)*(y)) のように, 置換内容には,全体と各引数にカッコをつける. 置換内容の中に,引数を2箇所以上書くことができる. その回数だけ置換される. #define triple(x) ((x)+(x)+(x)) に対してb=triple(++a); と書くと,b=((++a)+(++a)+(++a)); となる.

関数形式マクロ(3) 置換内容の中で「#引数」と書くと,引数を文字列にできる. #define pint(x) printf(#x " = %d\n", x) に対して,pint(a+1); は printf("a+1" " = %d\n", a+1); に置き換えられる. 通常の関数定義では,変数名を 引数にとってその文字列を得る ことはできない. 「文字列リテラルの連結」により,これは printf("a+1 = %d\n", a+1); と同じとなる. リpp.390-392, p.10, p.57

関数か関数形式マクロか 関数…「機能」を正確に表現したいとき マクロ…「機能」を簡便に表現したいとき 例:int square_int(int x) { return x * x; } 引数や戻り値の型に制約される. 関数呼び出しのオーバーヘッドがある. ローカル変数や制御文を活用できる. 実引数が++aなどのときも,その評価は一度だけ. マクロ…「機能」を簡便に表現したいとき 例:#define square_int(x) ((x) * (x)) 引数や評価式に型はない. (狭義の)コンパイル前に展開され,オーバーヘッドは少ない. ローカル変数や制御文は使用しにくい. (マクロ利用側の)引数は,置換内容の回数だけ評価される.

# define pint( x ) printf ( #x " = %d\n" , x ) 前処理指令と空白・コメント ...不可 ...必須 # define pint( x ) printf ( #x " = %d\n" , x ) ...任意 一つの前処理指令は,1行で書かなければならない.ただし, 行末に「\」を置くことで,複数行で書ける. 関数形式マクロの場合,括弧の途中で改行できる. 前処理指令の中でコメント(/* */)を書くと,前処理時に空白文字に置き換えられる. リpp.382-383

サイコロ問題 仕様 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ 目の数は6つ.ただし変更の可能性あり. サイコロの目は1~6のいずれかとする. 出た目はその都度出力する. 全ての目が出たら,何回振ったかを出力し,終了する. ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ allspot.c

サイコロ問題の考え方(1) サイコロの振り方 ライブラリ関数のrandを用いる あらかじめ #include <stdlib.h> int a = rand(); により,aにはint型の値が一つ代入される.この値は,ある範囲の中でどの値も等しい確率で選ばれる(一様乱数). int spot = rand() % 6; で,spot には 0~5 のいずれかが代入される. int spot = rand() % 6 + 1; とすればいい! 擬似乱数のため,何度実行しても同じ目が出る.これを変えるには,ライブラリ関数のsrandを呼び出して,適切な値の種を与えればよい. リpp.504-506, p.118

サイコロ問題の考え方(2) 「全ての目が出る」とは? int spot_counter[SPOT_MAX + 1]; 目ごとに出た回数 特定の目がまだ出ていないかの判定は, if (spot_counter[spot] == 0) 全ての目が出たかを,この配列変数だけで判定することはできるが,非効率 int counter_unfound = SPOT_MAX; まだ出ていない目がいくつあるか 一つずつ減らしていき,0になれば「全ての目が出た」

サイコロ問題で定義したマクロ #define SPOT_MAX 6 目の数を表す,オブジェクト形式マクロ ここ以外で「6」と書かない.これにより,目の数が変わるようなプログラムにも対処しやすい. #define cast_dice(spot_max) (rand() % (spot_max) + 1) サイコロを1回振って,出た目を返す,関数形式マクロ 呼び出し元では int spot = cast_dice(SPOT_MAX); であり,これは int spot = (rand() % (SPOT_MAX) + 1); になる. 「cast_dice(100*5)」のように使うことも可能

条件付きコンパイル(1) #if 定数式 … #endif 定数式が真のときに「…」を残し,そうでなければ「…」を捨てる. 定数式の評価や「…」の取捨は,前処理時(≠実行時)に行われる. リpp.397-398

条件付きコンパイル(2) 「#if 定数式」に代えて,「#ifdef 名前」や 「#ifndef 名前」も利用可能. 「#else」や「#elif 定数式」も記述可能. 条件付きコンパイルは入れ子にできる. 条件付きコンパイル 参考: Cのif文 #if 条件式1 … #elif 条件式2 #else #endif if (条件式1) { … } else if (条件式2) { } else { } リpp.399-402

ヘッダファイルで条件付きコンパイル ある環境の /usr/include/stdio.h より #ifndef _STDIO_H #define _STDIO_H 1 … #endif /* !_STDIO_H */ この記述により,複数のファイルに #include <stdio.h> があっても問題なく動作する. ヘッダファイルの中で,別のヘッダファイルをインクルードすることがある. _STDIO_H を定義して「…」を獲得するのは,1回だけ. 「インクルードガード」と呼ばれる. http://www.geocities.jp/ky_webid/c/060.html

次に学ぶこと ファイル入出力 問題 これまでそのプログラムを何回実行したか,記録できる? ファイルの各行を逆順に出力できる? "wakayama\n" ⇒ "amayakaw\n"

ファイルとは 補助記憶装置に保存する単位となる,データの集まり. プログラムが終了しても,内容が保持されるデータ構造. バイト列とは? (比較)変数の値はメモリ上にあるため,プログラム終了時に破棄される. コンピュータの電源を切って入れ直しても,保持されていることが多い. ストリームと呼ばれるバイト列として,読み書き可能. バイト列とは? char配列で表現できるデータ構造. '\0'(ナル文字,ヌル文字)で終わるものではなく,途中に '\0' があってもいいという点が,文字列と異なる. 入p.188 リp.455

実行回数管理プログラム 仕様 考え方 ./count を実行すると,count.txt というファイルに実行回数が保存される. 入pp.186-187 count.c

ファイルポインタ Cでファイルを操作するには,ファイルポインタを使用する. FILE オブジェクト fp stdio.h で定義されているFILE型のポインタ. 例: FILE *fp; FILE オブジェクト fp リp.457

ファイル操作のライブラリ関数(1) FILE *fopen(char *path, char *mode); ファイルを開き(プログラムから使えるようにし),ファイルポインタを返す. 第1引数はファイル名(「パス名」ともいう). 第2引数が "r" なら,読み込み専用で開く. 第2引数が "w" なら,書き込み専用で開く. リpp.463-464, p.462

ファイル操作のライブラリ関数(2) int fscanf(FILE *stream, const char *format, ...); char *fgets(char *s, int size, FILE *stream); 「size-1バイト」,「改行文字まで」,「ファイルの終わりまで」のうち最小のバイト数を読み込んで,s が指し示す配列領域に格納し,最後に '\0' をつける. int fgetc(FILE *stream); 1バイト読み出して,その値を返す. リpp.467-468, p.462, p.461

ファイル操作のライブラリ関数(3) int fprintf(FILE *stream, const char *format, ...); int fclose(FILE *fp); 開いたファイルを閉じる(プログラムから使えないようにする). リpp.464-465, pp.458-459

ifとfopenの組み合わせ コード例 if ((fp = fopen("count.txt", "w")) == NULL) { printf("failed to open file: count.txt\n"); return 1; } ファイルを開くことができなければ,メッセージを出力して,関数の処理を終える.開ければ,fpにファイル構造体のポインタが代入され,あとのファイル処理で利用できる. カッコの対応に注意. × if (fp=fopen(ファイル名, "w"))==NULL) × if (fp=fopen(ファイル名, "w")==NULL) この例では,「fpへの代入」と「if文」を分けて書いてもよい.しかし「代入」と「while文」を同時に書く(分けると保守性を損なう)ことが多いので,この記述に慣れてほしい.

ファイルから整数値を得る方法 fscanfで整数値を獲得する fgetsで1行読み出して配列に格納し,atoiで文字列を整数値に変換する fgetsで1行読み出して配列に格納し,sscanfで文字列から整数値を獲得する fgetcで1文字ずつ読み出し,整数値を構成する 「非負整数値を一つ読み出す」なら,どれでもよい 実用上の観点(入力情報が変わってもコードを書きやすい)でおすすめは,「fgets+sscanf」と「fgetc (ただし関数にする)」. 各手法のデメリット: fscanf…入力がフォーマット(第2引数で与える文字列)に合っていないとき,読み飛ばせない. fgets+atoi…1行の情報を格納する配列領域が必要になる.1行から獲得したい情報が複数あるとき,処理が難しい. fgets+sscanf…1行の情報を格納する配列領域が必要になる.入力が空白文字(' 'でも'\n'でもよい)で区切られていて一つ一つ読み出すとき,処理が複雑になる. fgetc…1文字を読み出す変数が必要になる.バイト列から整数への変換を自前で作ることになり,バグが入りやすい.

まとめ 前処理指令をうまく使えば,読みやすく保守しやすいプログラムを書くことができる. 前処理は,コンパイルの前に行われる.そのため,前処理指令の書式はCの文法と異なる. ファイル操作により,プログラムの外から情報を受け取ったり,情報を保存したりすることができる.

スケジュール 第13回:1月28日(月) 第14回:1月30日(水) 試験:2月?日(?) ファイル入出力(続き),標準入出力,mallocなど 第14回:1月30日(水) おさらい問題 試験:2月?日(?) 自筆ノート1冊の参照を認める 教科書・書籍,印刷資料等は参照不可