C言語入門
第7週
プログラミング言語Ⅰ(実習を含む。),
計算機言語Ⅰ・計算機言語演習Ⅰ,
前回の復習
連立一次方程式を行列で計算する
• 吐き出し法(ガウスの消去法)
• ステップ1: 前進消去(上三角行列の作成)
gaussian_elimination1.c // step1 for (k = 0; k < M - 1; k++) { for (i = k + 1; i < M; i++) { x = a[i][k] / a[k][k]; for (j = 0; j < N; j++) {a[i][j] = a[i][j] - a[k][j] * x; } b[i] = b[i] - b[k] * x; } } まずどこで ピボッティング処理するのが 良いのか考える 少なくとも、a[k][k]で割る 前でないと意味がないので 候補はこの3か所
連立一次方程式を行列で計算する
• 吐き出し法(ガウスの消去法)
• ステップ1: 前進消去(上三角行列の作成)
gaussian_elimination1.c // step1 for (k = 0; k < M - 1; k++) { for (i = k + 1; i < M; i++) { x = a[i][k] / a[k][k]; for (j = 0; j < N; j++) {a[i][j] = a[i][j] - a[k][j] * x; } b[i] = b[i] - b[k] * x; } } k毎に処理しないと いけないので一番上は除外 k毎に処理しておけば i毎に処理する必要はない 従って一番下も除外 結果、真ん中が最適 と言うことになる
ピボッティング
• a[k][k] が 0 か調べる
gaussian_elimination1.c // step1 for (k = 0; k < M - 1; k++) { if (a[k][k] == 0) { // この場合にピボッティングする必要がある } for (i = k + 1; i < M; i++) { x = a[i][k] / a[k][k]; for (j = 0; j < N; j++) {a[i][j] = a[i][j] - a[k][j] * x; }
b[i] = b[i] - b[k] * x; }
ピボッティング
• k+1行目以降でk列目が 0 でない行を探す
gaussian_elimination1.c // step1 for (k = 0; k < M - 1; k++) { if (a[k][k] == 0) { for (i = k + 1; i < M; i++) { // ここでa[i][k]が0でない行を探す } } // ... }ピボッティング
• 目的の行を見つけた際の処理を書く
gaussian_elimination1.c // step1 for (k = 0; k < M - 1; k++) { if (a[k][k] == 0) { for (i = k + 1; i < M; i++) { if (a[i][k] != 0) { // 目的の行を見つけた際の処理を書く } } } // ... } 授業では最初ここを a[i][k] == 0 としていたので、 上手く動いていませんでした。ピボッティング
• k行とi行の各列で値を交換する
gaussian_elimination1.c // step1 for (k = 0; k < M - 1; k++) { if (a[k][k] == 0) { for (i = k + 1; i < M; i++) { if (a[i][k] != 0) { for (j = 0; j < N; j++) { // k行とi行の各列でaの値を交換する処理 } // k行とi行でbの値を交換する処理 } } } // ... }ピボッティング
• 暫定的に完成したピボッティング
gaussian_elimination1.c if (a[k][k] == 0) { for (i = k + 1; i < M; i++) { if (a[i][k] != 0) { float tmp; for (j = 0; j < N; j++) { tmp = a[k][j]; a[k][j] = a[i][j]; a[i][j] = tmp; } tmp = b[k]; b[k] = b[i]; b[i] = tmp; } } } なぜ暫定的かと言うと このままだと、 二重にピボッティングする事もある。 実害はないが無駄な処理なので ピボッティングに成功したら そこでピボッティングの処理を 打ち切るほうが効率的。繰り返し(ループ)の復習
ループの再開と脱出
• continue 文
• while, do while, for 文内で使用可能
• 以降の処理を中断してループを再開する
• for文では後処理(第3パラメータ)も実行する
• break 文
• while, do while, for, switch 文内で使用可能
• 以降の処理を中断してループを脱出する
• switch文の場合はswitch文から脱出する
continue 文の
2つ目の説明が微妙に間違ってました
正しくはループ末尾から再開
8週資料参照
後判定ループ (do while 文)
• 真偽値による繰り返し
do {
// 条件式 が真の場合の処理
} while (条件式);
条件式 処理 真 偽 do while 文は、ループ内の処理を 最低1回は実行する。 教科書 p.123. continue breakdo while 文内の
continue 文で
飛ぶ先が間違ってました
正しくは次項または
8週資料参照
後判定ループ (do while 文)
• 真偽値による繰り返し
do {
// 条件式 が真の場合の処理
} while (条件式);
条件式 処理 真 偽 do while 文は、ループ内の処理を 最低1回は実行する。 教科書 p.123. continue break初期化・更新処理付きループ (for 文)
• 真偽値による繰り返し
for (式1; 式2; 式3) {
// 式2が真の場合の処理
};
式2 式1 真 偽 前判定ループだが 式1による初期化と 式3による更新処理を ひとまとめにして書ける。 処理 式3 教科書 pp.124-129. continue breakfor文とwhile文 (前判定ループ)
• 以下のループは等価
• continue時の式3の扱いに注意
for (式1; 式2; 式3) {
// 式2が真の場合の処理
};
式2 式1 真 偽 処理 式3 教科書 pp.123-129. for文の continue break式1;
while (式2) {
// 式2が真の場合の処理
式3;
};
while文の continuefor文とwhile文 (前判定ループ)
• 以下のループは等価
• continue時の式3の扱いに注意
for (i = 0; i < 10; i++) {
// ループ内の処理
};
i < 10 i = 0 真 偽 処理 i++ 教科書 pp.123-129. for文の continue breaki = 0;
while (i < 10) {
//ループ内の処理
i++;
};
while文の continue後判定ループ (do while 文)
• continue, break 後の処理(iの値)に注目
looptest_dowhile.c int i = 0, j = 0, n; fprintf(stderr, "n = "); scanf("%d", &n); do { j++; printf("%d, %d: 1st", i, j);
if (j == 2) {printf(" continue¥n"); continue;} printf(" 2nd");
if (j == 4) {printf(" break¥n"); break;} printf(" 3rd¥n"); i++; } while (i < n); 教科書 pp.119-122. mintty + bash $ ./looptest_dowhile n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 1, 3: 1st 2nd 3rd 2, 4: 1st 2nd break mintty + bash $ ./looptest_dowhile n = 0 0, 1: 1st 2nd 3rd do while 文は、 ループ内の処理を 最低1回は実行する。
前判定ループ (while 文)
• continue, break 後の処理(iの値)に注目
looptest_while.c int i = 0, j = 0, n; fprintf(stderr, "n = "); scanf("%d", &n); while (i < n) { j++; printf("%d, %d: 1st", i, j);
if (j == 2) {printf(" continue¥n"); continue;} printf(" 2nd");
if (j == 4) {printf(" break¥n"); break;} printf(" 3rd¥n"); i++; } 教科書 p.123. mintty + bash $ ./looptest_while n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 1, 3: 1st 2nd 3rd 2, 4: 1st 2nd break mintty + bash $ ./looptest_while n = 0
初期化・更新処理付きループ (for 文)
• continue, break 後の処理(iの値)に注目
looptest_for.c int i = 0, j = 0, n; fprintf(stderr, "n = "); scanf("%d", &n); for (i = 0; i < n; i++) { j++; printf("%d, %d: 1st", i, j);
if (j == 2) {printf(" continue¥n"); continue;} printf(" 2nd");
if (j == 4) {printf(" break¥n"); break;} printf(" 3rd¥n"); } 教科書 pp.124-129. mintty + bash $ ./looptest_for n = 10 0, 1: 1st 2nd 3rd 1, 2: 1st continue 2, 3: 1st 2nd 3rd 3, 4: 1st 2nd break mintty + bash $ ./looptest_for n = 0
コマンドライン引数
• main 関数の引数として取得出来る。
argtest.c
#include <stdio.h> #include <stdlib.h>
int main(int argc, char *argv[]) {
int i;
printf("argc = %d¥n", argc); for (i = 0; i < argc; i++) {
printf("argv[%d] = ¥"%s¥"¥n", i, argv[i]); } return EXIT_SUCCESS; } main の引数名は 自由につけて良いが 以下の名前を使うのが 慣例になっている
argc: ARGument Count argv: ARGument Vector 教科書 pp.265-272.
[1] pp.139-144.
argument は英語で 引数を意味する
コマンドライン引数
• main 関数の引数として取得出来る。
コマンドプロンプト >argtest a b "c d" "e¥"f" argc = 5 argv[0] = "C:¥Users¥kou¥Desktop¥CLangI2014S1¥argtest.exe" argv[1] = "a" argv[2] = "b" argv[3] = "c d" argv[4] = "e"f" mintty + bash $ ./argtest a b "c d" "e¥"f" argc = 5 argv[0] = "./argtest" argv[1] = "a" argv[2] = "b" argv[3] = "c d" argv[4] = "e"f" 教科書 pp.265-272. [1] pp.139-144. コマンドライン空白で分割されて argvに格納される。 argvに空白を含めたい場合は 「"」(ダブルクォーテーション)で囲む 「"」を含めたい場合は「¥」でエスケープする argv[0]は実行中のコマンド名標準入出力と標準エラー出力
• 以下の入出力が利用できる
• stdin: STanDard INput:
標準入力
• stdout: STanDard OUTput:
標準出力
• stderr: STanDard ERRor output: 標準エラー出力
• scanf や getchar 等は stdin から入力している
• printf や putchar 等は stdout へ出力している
• stdin, stdout はパイプやリダイレクトの対象だ
が stderr は標準では対象外
標準入出力と標準エラー出力
• パイプやリダイレクトで処理されたくない内容
は stderr へ出力する
• fscanf や fprintf を使うと、入出力先を自由に
選択出来る
[1] pp.196, 199, 218. stdiotest.cprintf("output to stdout with printf¥n");
fprintf(stdout, "output to stdout with fprintf¥n"); fprintf(stderr, "output to stderr with fprintf¥n"); mintty + bash
$ ./stdiotest > redirect.txt output to stderr with fprintf $ cat redirect.txt
output to stdout with printf output to stdout with fprintf
標準入出力と標準エラー出力
• stdin,stdout,stderrはstdio.hで定義されている
• stdio.h は standard input / output header
fprintf 関数
• int fprintf(FILE *fp,
const char *FORMAT, ...);
• printfの結果をfpへ書き出す
• 引数:
• fp:
FILE 構造体へのポインタ
• FORMAT:
書式
• ...:
任意の数の引数
• 戻り値:
• 書き出された文字数
• エラーの場合負の数
教科書 pp.61, 64-66, 98, 300. 参考: [1] pp.305-306.fscanf 関数
• int fscanf(FILE *fp,
const char *FORMAT, ...);
• fpからデータを読み込む
• 引数:
• fp:
FILE 構造体へのポインタ
• FORMAT:
書式
• ...:
任意の数の引数
値を格納する変数へのポインタ
• 戻り値:
• 変換され代入された入力項目の数
• ファイル終端またはエラーの場合EOF
教科書 pp.80-83, 254. 参考: [1] pp.307-309.fopen 関数
• FILE *fopen(const char *filename,
const char *mode);
• ファイルを開き、FILE構造体へのポインタを得る
• 引数:
• filename: ファイル名(パス)の文字列
• mode:
ファイルを開くモード
• 戻り値:
• FILE 構造体へのポインタ
• エラーの場合 NULL
教科書 pp.298-305. 参考: [1] pp.194-198.fopen 関数の mode
mode 読み込み 書き込み 動作 "r" 任意の位置 × ファイルを開く、存在しない場合エラー "w" × 任意の位置 ファイルを作成し、前の内容は消去する "a" × ファイル末尾 ファイルを開く、または作成 "r+" 任意の位置 任意の位置 ファイルを開く、存在しない場合エラー "w+" 任意の位置 任意の位置 ファイルを作成し、前の内容は消去する "a+" 任意の位置 ファイル末尾 ファイルを開く、または作成 教科書 pp.298-305. 参考: [1] pp.194-198. "r", "w", "a", "r+", "w+", "a+" はテキストモードで読み書きする テキストモードでは改行コード(¥n)の扱いが環境によって異なる • Windows: CR LF (0xd 0xa) • Mac: CR (0xd) • UNIX: LF (0xa) バイナリモードにするには、"rb", "wb", "ab", "r+b", "w+b", "a+b" のように "b" を追加するfclose 関数
• int fclose(FILE *fp);
• ファイルを閉じます
• 引数:
• fp:
FILE構造体へのポインタ
• 戻り値:
• 0 を返す
• エラーの場合 EOF を返す
教科書 pp.298-305. 参考: [1] pp.194-198.演習: print_0.c
• 0を表示せよ
• 数値の直後で改行すること
コマンドプロンプト>print0
0
>
演習: print_dec.c
• scanf関数を用いてキーボードから入力され
た整数をint型の変数iに読み込み10進数と
して表示せよ
• i を入力する前に「i = 」と
標準エラー出力に表示する事
第3週資料 pp.23-33, 34-42. mintty + bash $ ./print_dec i = 123 123演習: print_octdechex.c
• scanf関数を用いてキーボードから入力された
8,10,16進数の整数をint型の変数iに読み込
み8,10,16進数の3種類の表示せよ
• i を入力する前に「i = 」と
標準エラー出力に表示する事
• 8進数表示の前には0を表示せよ
• 16進数表示の前には0xを表示せよ
• 各数値は幅6桁で右寄せで表示し
末尾は改行すること
8,16進数頭の0,0xは6桁に含める
第3週資料 pp.23-33, 34-42. mintty + bash $ ./print_octdechex i = 0123 0123 83 0x53 $ ./print_octdechex i = 123 0173 123 0x7b $ ./print_octdechex i = 0x123 0443 291 0x123演習: print_evenodd.c
• iが偶数か奇数か調べ以下の文字列を
printf関数に与えて表示せよ
• 偶数の場合: "even number¥n"
• 奇数の場合: "odd number¥n"
• print_evenodd_tmp.c を
print_evenodd.c にコピーし
指定の場所に作成する事
mintty + bash $ ./print_evenodd i = 1 odd number mintty + bash $ ./print_evenodd i = 2 even number演習: print_natural_lt.c
• n未満の非負整数(0を含む自然数)を小さい
順に全て表示せよ
• 各数値の直後で改行すること
• 変数はi,nのみを使うこと
• print_natural_lt_tmp.c を
元に、指定の場所に作成する事
mintty + bash $ ./print_natual_lt n = 10 0 1 2 3 4 5 6 7 8 9演習: print_even_lt.c
• 0以上n未満の偶数を小さい順に全て表示せ
よ
• 各数値の直後で改行すること
• 変数はi,nのみを使うこと
• print_even_lt_tmp.c を
元に、指定の場所に作成する事
mintty + bash $ ./print_even_lt n = 20 0 2 4 6 8 10 12 14 16 18素数
• 1と自分以外に正の約数を持たない自然数
(正整数)で1でない数
• 調べたい数をiとすると、2以上i/2以下の整
数jの全てについてiが割り切れないことを確
認すれば良い。
演習: print_isprime.c
• iが素数かどうか調べ以下の文字列をprintf関数に
与えて表示せよ
• 非素数: "not prime number¥n"
• 素数: "prime number¥n"
• 変数はi,j,primeのみを使う事
• 変数は以下の目的で使う事
• i: 素数かどうか調べたい数
• j: 2以上i/2以下の正数
• prime: 素数判定用のフラグ
0=非素数,1=素数
• print_isprime_tmp.c を元に
指定の位置に作成せよ
mintty + bash $ ./print_prime_lt n = 2 $ ./print_isprime i = 2 prime number $ ./print_isprime i = 3 prime number $ ./print_isprime i = 5 prime number $ ./print_isprime i = 7 prime number $ ./print_isprime i = 11 prime number演習: print_isprime.c
• 非素数の場合も確認
mintty + bash
$ ./print_isprime i = 4
not prime number $ ./print_isprime i = 6
not prime number $ ./print_isprime i = 8
not prime number $ ./print_isprime i = 9
not prime number $ ./print_isprime i = 10
not prime number
mintty + bash
$ ./print_isprime i = -1
not prime number $ ./print_isprime i = 0
not prime number $ ./print_isprime i = 1
not prime number
負の場合、 0及び1の場合 合成数の場合