C言語入門 - メディア基盤センター

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