1 C言語入門 第4週 プログラミング言語Ⅰ(実習を含む。), 計算機言語Ⅰ・計算機言語演習Ⅰ, 情報処理言語Ⅰ(実習を含む。) News 2 Visual Studio Code https://code.visualstudio.com/ • Microsoft 製プログラミング用テキストエディタ 2015-04-29 にリリースされたばかり のテキストエディタ。Windows 以外 にも Mac, Linux 用もある。 3 条件分岐と繰り返し 制御構造 4 教科書 pp.112-116. 流れ図(フローチャート) • プログラムの処理の流れを図示する方法 端子 プログラムの開始と終了 表示 処理 代入や演算等 各ステップにおける処理 手操作入力 定義済み処理 条件分岐 結合子 サブルーチンや関数等 画面等への出力 キーボード等からの入力 処理の振り分け 各部品を矢印で繋ぐ事で処理の流れ を視覚的に表現する。 各部品の内部にはそれぞれのステップ で行う内容に書き換えて使う。 別のフローチャートと結合 2015-05-12追加 5 if 文と switch 文 条件分岐 教科書 pp.130-133. 6 条件分岐 (if 文) • 真偽値による場合分け if (条件式1) { // 条件式1が真の場合の処理1 } 真 条件式1 偽 処理1 if 文を使うと特定の条件下で実行する処理を指定出来る。 教科書 pp.130-133. 7 条件分岐 (if, else 文) • 真偽値による場合分け if (条件式1) { // 条件式1が真の場合の処理1 } else { // 条件式1が偽の場合の処理2 } 真 処理1 条件式1 偽 処理2 if, else 文を使うと特定の条件下で別の処理を指定出来る。 教科書 pp.130-133. 8 入れ子の条件分岐 (if, else 文) 2015-05-15修正 誤:処理2,3,1 正:処理1,2,3 • 真偽値による場合分け if (条件式1) { if (条件式2) { // 条件式1が真かつ // 条件式2が真の場合の処理1 } else { // 条件式1が真かつ // 条件式2が偽の場合の処理2 } } else { // 条件式1が偽の場合の処理3 } 真 真 処理1 条件式2 条件式1 偽 処理2 if, else 文は任意の数入れ子に出来る。 だだし、入れ子が深くなると読み難くなるので注意。 偽 処理3 教科書 pp.130-133. 9 入れ子の条件分岐 (if, else 文) • 真偽値による場合分け if (条件式1) { // 条件式1が真の場合の処理1 } else { if (条件式2) { // 条件式1が偽かつ // 条件式2が真の場合の処理2 } else { // 条件式1が偽かつ // 条件式2が偽の場合の処理3 } } 真 処理1 条件式1 真 処理2 if, else 文は任意の数入れ子に出来る。 else 側の if は else if で置き換ると入れ子を防げる。 偽 条件式2 偽 処理3 教科書 pp.130-133. 10 複数条件の条件分岐 (if, else if, else 文) • 真偽値による場合分け if (条件式1) { // 条件式1が真の場合の処理1 } else if (条件式2) { // 条件式1が偽かつ // 条件式2が真の場合の処理2 } else { // 条件式1が偽かつ // 条件式2が偽の場合の処理3 } 真 処理1 else if は任意の数追加出来る。 入れ子を作らずに複数の条件を追加出来る。 条件式1 真 処理2 偽 条件式2 偽 処理3 11 教科書 pp.134-140. 多分岐判断機構 (switch 文) • 値による場合分け switch (式) { case 値1: // 式が値1の場合の処理1 case 値2: // 式が値2の場合の処理2 default: // 他の条件に // 当てはまらない場合の処理N }; break 文を入れておかないと 次の条件の処理を 連続して実行するので注意。 式 値1 値2 default 処理1 処理2 処理N break break break 12 条件分岐の例題 平方根の計算 13 sqrt 関数 • 書式: #include <math.h> double sqrt(double x); • 引数: • x: 求める平方根の2乗 • 戻り値: • 0 ≤ 𝑥 の場合 𝑥 を返す。 • 0 ≤ 𝑥 でない場合は処理系依存? • x が負なら、nan (=非数) を返し、グローバル変数 errno に EDOM を代入する(errno は errno.h を include すると参照出来る)。 • x が 0 なら 0 を、inf (=∞) なら inf を返す。 • x が nan なら nan を返す。 14 平方根の計算 • sqrt() 関数は引数が負の場合計算出来ない。 sqrt_practice_1.c 1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> #include <math.h> void main() { double x; fprintf(stderr, "x = ?\b"); scanf("%lf", &x); printf("x: %f\n", x); printf("sqrt(x): %f\n", sqrt(x)); } mintty+bash+gcc $ gcc sqrt_practice_1.c && ./a x = 2 x: 2.000000 sqrt(x): 1.414214 $ ./a x = -2 x: -2.000000 sqrt(x): nan 15 演習: 平方根の計算(if文) • sqrt_practice_1.c を参考に 0 ≤ 𝑥 の場合は実 数の平方根 𝑥を、 𝑥 < 0 の場合は虚数の平 方根 −𝑥𝑖 を if 文で場合分けして表示する sqrt_practice_if.c を完成させよ。虚数は数値 の後に i を表示する事で表現すれば良い。 • 例えば x=-2 の場合は以下のようになる。 mintty+bash+gcc $ gcc sqrt_practice_if.c && ./a x = -2 x: -2.000000 sqrt(x): 1.414214i 16 演習: 平方根の計算(if文) ヒント • sqrt_practice_1.c の 10 行目は 𝑥 を表示して いる。これを if 文で場合分けしてやれば良い。 sqrt_practice_1.c 10 printf("sqrt(x): %f\n", sqrt(x)); sqrt_practice_if.c 10 if (条件式) { 11 // 𝑥 を表示 12 } else { 13 // −𝑥 と 𝑖 を表示 14 } 𝑥 真 𝑥 条件式 偽 −𝑥 𝑖 [1] pp.63-66, 256-257. 17 条件演算子 三項演算子(?:) • 書式: 真 式1 条件式 偽 式2 • 条件式 ? 式1 : 式2 1 2 3 4 5 6 7 8 9 condexprtest.c mintty+bash+gcc #include <stdio.h> $ gcc condexprtest.c && ./a i = 1 not zero $ ./a i = 0 zero void main() { int i; fprintf(stderr, "i = ?\b"); scanf("%d", &i); printf("%s\n", i ? "not zero" : "zero"); } 18 演習: 平方根の計算(条件演算子) • 先程の負の数の平方根の計算を条件演算子 を用いて解決してみよう。 • 10行目の printf では 2 つ目の引数が %f に 3 つ目の引数が %s へ埋め込まれる。 sqrt_practice_condexpr.c 6 7 8 9 10 double x; fprintf(stderr, "x = ?\b"); scanf("%lf", &x); printf("x: %f\n", x); printf("sqrt(x): %f%s\n", sqrt(/*WYCH*/), /*WYCH*/); 2015-05-18追加 条件分岐の例題 奇数と偶数の判定 19 20 奇数と偶数 • 整数のうち 2 で割り切れるのが偶数、2 で割 り切れないのが奇数 • 2 で割り切れるとは? • 整数を 2 で割った余り(剰余)が 0 • 2 で割り切れないとは • 整数を 2 で割った余り(剰余)が 0 以外 21 C言語の剰余 • 剰余は二項演算子 % • i % 2 で 2 で割った余りが求まる • 同じかどうか比較するのは二項演算子 == や != • i % 2 == 0 は i を 2 で割った余りが 0 なら真、そ れ以外は偽 • i % 2 != 0 は i を 2 で割った余りが 0 以外なら真、 それ以外は偽 • i % 2 != 0 は単項演算子 ! を用いて !(i % 2 == 0) としても同じ意味になる 22 演算子の優先度 優先度 高 演算子 ( ) [ ] -> 備考 左から右→ . 右から左← 単項演算子 左から右→ 二項演算子 左から右→ 二項演算子 左から右→ bitシフト 左から右→ 関係演算子 左から右→ 等値演算子 & 左から右→ bit毎のAND ^ 左から右→ bit毎のXOR | 左から右→ bit毎のOR && 左から右→ 論理演算子(AND) || 左から右→ 論理演算子(OR) ?: 右から左← 三項演算子 右から左← 代入演算子 ! ~ ++ * / % + - << < == = 低 結合規則 , -- + - * & (type) sizeof >> <= > >= != += -= *= /= %= &= ^= |= <<= >>= 左から右→ [1] p.65. より 23 演習: 偶数かどうか判定する • 標準入力から入力された整数値が奇数かど うか判別して、奇数であれば入力された数値 に続けて " is even number\n" そうでなければ " is not even number\n" と表示するプログラ ムを作成せよ。奇数でない場合は何も表示し なくて良い。 • if_practice_even.c の /*WYCH*/ の個所を修 正すれば良い。 24 演習: 奇数かどうか判定する • 標準入力から入力された整数値が奇数かど うか判別して、奇数であれば入力された数値 に続けて " is odd number\n" そうでなければ " is not odd number\n" と表示するプログラム を作成せよ。奇数でない場合は何も表示しな くて良い。 • if_practice_odd.c の /*WYCH*/ の個所を修正 すれば良い。 25 演習: 奇数か偶数か判定する • 標準入力から入力された整数値が奇数かど うか判別して、奇数であれば入力された数値 に続けて " is odd number\n"、偶数であれば 入力された数値に続けて "is even number\n" と表示するプログラムを作成せよ。 • if_practice_evenodd.c の /*WYCH*/ の個所を 修正すれば良い。 26 入れ子の条件分岐の例題 閏年の判定 27 閏年(leap year)とは • 地球の平均回帰年は365日+約1/4日である ため1年を356日にしているとカレンダー上の 日付と季節が4年で1日ずつずれてしまう。こ れを防ぐのが4年に1回設ける2月29日(閏日)。 • 閏年の求め方 • 西暦を4で割り切れるなら閏年? 真 閏年 閏年 判定式 偽 平年 28 閏年(leap year)の判定 • 4で割り切れる(割れる)とは? • 4で割った余りが0ということ • 4で割り切れる • 4で割り切れない : year % 4 == 0 : year % 4 != 0 % : 剰余算演算子 等値演算子 == : 等しい != : 等しくない • C言語では0は偽、0以外は真だったから • 以下のようにも書けるが・・・ • 4で割り切れる • 4で割り切れない ! : 論理否定演算子 : !(year % 4) : (year % 4) ぱっと見て意味の分かり易い書き方をしましょう 29 演算子の優先度 優先度 高 演算子 ( ) [ ] -> 備考 左から右→ . 右から左← 単項演算子 左から右→ 二項演算子 左から右→ 二項演算子 左から右→ bitシフト 左から右→ 関係演算子 左から右→ 等値演算子 & 左から右→ bit毎のAND ^ 左から右→ bit毎のXOR | 左から右→ bit毎のOR && 左から右→ 論理演算子(AND) || 左から右→ 論理演算子(OR) ?: 右から左← 三項演算子 右から左← 代入演算子 ! ~ ++ * / % + - << < == = 低 結合規則 , -- + - * & (type) sizeof >> <= > >= != += -= *= /= %= &= ^= |= <<= >>= 左から右→ [1] p.65. より 30 演習: 4 で割り切れるか表示する • leap_year_practice_1.c の /*WYCH*/ を書き 換えて、year が 4 で割り切れる場合 "can be divided by 4."、割り切れない場合 "can not be divided by 4." と表示するプログラムを完成せ 2015-05-11修正 よ。 誤:dividec 正:divided mintty+bash+gcc $ gcc leap_year_practice_1.c && ./a year = 2015 2015 can not be divided by 4. $ ./a year = 2016 2016 can be divided by 4. 31 閏年(leap year)の定義 • グレゴリオ暦における閏年の定義 • 判定したい年を西暦で表した際 • 4で割り切れる場合は閏年 (条件1) • 但し100で割り切れる場合は平年 (条件2) • 但し400で割り切れる場合は閏年 (条件3) • 地球の平均回帰年は約365.242199日であ るため、上記ルールだと約3320年で1日ずれ る程度で済む。 32 閏年(leap year)の判定 真 条件1 閏年 4で 割れる 偽 平年 33 閏年(leap year)の判定 真 条件1 +条件2 真 平年 100で 割れる 4で 割れる 偽 閏年 偽 平年 34 閏年(leap year)の判定 真 条件1 +条件2 +条件3 真 完成 真 閏年 400で 割れる 100で 割れる 偽 平年 4で 割れる 偽 閏年 偽 平年 35 演習: 閏年(leap year)の判定 • leap_year_practice_2.c の /*WYCH*/ の 部分を書き換えて閏年か判定するプログラムを 完成せよ。 • /*WYCH*/ の部分には year に格納された西 暦が閏年であれば変数 leap_year_flag に 1 を閏年でなければ 0 を代入するコード作成 すれば良い。これは前のページのフローチャート を参考に if 文を 3 重の入れ子にすれば出 来る。 • なおここでは紀元前については考慮する必要は ない。 36 演習: 閏年(leap year)の判定 • ヒント1 • 前述の条件1~3のフローチャートをif,else文 で書くと以下のようになる。 条件1 条件1+条件2 条件1+条件2+条件3 if (/*条件1*/) { /*閏年*/ } else { /*平年*/ } if (/*条件1*/) { if (/*条件2*/) { /*平年*/ } else { /*閏年*/ } } else { /*平年*/ } if (/*条件1*/) { if (/*条件2*/) { /*条件3のif,else文*/ } else { /*閏年*/ } } else { /*平年*/ } 37 演習: 閏年(leap year)の判定 • ヒント2 • 閏年なら leap_year_flag に 1 そうでなけれ ば 0 を代入すれば良い。 条件1 if (/*条件1*/) { leap_year_flag = 1; /*閏年*/ } else { /*平年*/ } 38 多分岐の例題 月の名前 39 演習: 月の名前の表示 • monthname_practice_1.c の /*WYCH*/ の部 分を変更し、入力した月 month に対応する英 語の月名(January, February, March, April, June, July, August, September, October, November, December )を表示せよ。 mintty+bash+gcc $ gcc monthname_practice_1.c && ./a month = 1 January 40 for文, while文, do-while 文によるループと continue 文、break 文によるループの再開と脱出 繰り返し(ループ) 41 ループの再開と脱出 • continue 文 • while, do while, for 文内で使用可能 • 以降の処理を中断してループ末尾から再開する • for文では後処理(第3パラメータ)も実行する • break 文 • while, do while, for, switch 文内で使用可能 • 以降の処理を中断してループを脱出する • switch文の場合はswitch文から脱出する 42 教科書 p.123. 後判定ループ (do while 文) • 真偽値による繰り返し continue break 処理 do { // 条件式 が真の場合の処理 } while (条件式); 真 条件式 偽 do while 文は、ループ内の処理を 1回以上実行する(=最低1回は実行する)。 43 教科書 pp.119-122. 前判定ループ (while 文) 2015-06-04修正 誤:式2 正:条件式 • 真偽値による繰り返し while (条件式) { // 条件式が真の場合の処理 } 条件式 偽 真 continue while 文は、ループ内の処理を 0回以上実行する(=実行しない場合もある) 。 処理 break 44 教科書 pp.124-129. 初期化・更新処理付きループ (for 文) • 真偽値による繰り返し 式1 for (式1; 式2; 式3) { // 式2が真の場合の処理 }; 式2 偽 真 continue 処理 式3 for 分は前判定ループで 式1による初期化と 式3による更新処理を ひとまとめにしてコンパクトに書ける。 break 45 教科書 pp.123-129. for文とwhile文 (前判定ループ) • 以下のループは等価 式1 • ただしcontinue時の式3の扱いに注意 式2 for (式1; 式2; 式3) { // 式2が真の場合の処理 }; 式1; while (式2) { // 式2が真の場合の処理 式3; }; 偽 真 for文の continue 処理 while文の continue 式3 break 46 教科書 pp.123-129. for文とwhile文 (前判定ループ) • 以下のループは等価 • continue時の式3の扱いに注意 i < 10 for (i = 0; i < 10; i++) { // ループ内の処理 }; i = 0; while (i < 10) { // ループ内の処理 i++; }; i = 0 偽 真 for文の continue 処理 while文の continue i++ break 47 教科書 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回は実行する。 48 教科書 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 49 教科書 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 50 [1] p.281. continue 文 • 以下のループ内に更に小さなループが含ま れない場合の continue は goto contin と同義 for (...) { // ... contin: ; } do { // ... contin: ; } while (...); while (...) { // ... contin: ; } 51 [1] p.281. goto文 • 指定した名札付き文へ移動(ジャンプ)する • 名札(label)は以下のように設定出来る ラベル名: 文 goto 文は 余程理由がない限り使わないこと do while 文相当 while 文相当 for 文相当 loop: ; { // something to do contin: ; } if (expr) goto loop; brk: ; loop: ; if (expr) { // something to do contin: ; goto loop; } brk: ; expr1; loop: ; if (expr2) { // something to do contin: ; expr3; goto loop; } brk: ; 2015-05-15追加 前判定ループの演習 簡易版 seq コマンドの作成 seq コマンド • UNIX系のOSに標準で搭載されているコマンド • 書式: • seq FIRST LAST • 機能: • FIRST から LAST までの整数を 1 刻みで小さい順に表示す る。 • 他にも詳細な機能があるが、ここでは省略 JM / seq (1) 演習: while 文による簡易 seq コマンド • seq_practice_1_while.c の /*WYCH*/ の部分 を修正して以下のプログラムを完成させよ。 • 標準入力から int 型の変数 first, last に整数 値を読み取り、first 以上、last 以下の整数を1 刻みで小さい順に表示せよ。 mintty + bash $ gcc seq_practice_1_while.c && ./a first = 5 last = 9 5 6 7 8 9 演習: for 文による簡易 seq コマンド • seq_practice_1_for.c の /*WYCH*/ の部分を 修正して以下のプログラムを完成させよ。 • 標準入力から int 型の変数 first, last に整数 値を読み取り、first 以上、last 以下の整数を1 刻みで小さい順に表示せよ。 mintty + bash $ gcc seq_practice_1_for.c && ./a first = 5 last = 9 5 6 7 8 9 2015-05-18追加 条件分岐とループの例題 素数判定 56 57 素数 • 1と自分以外に正の約数を持たない自然数 (正整数)で1でない数 • 調べたい数をiとすると、2以上i/2以下の整 数jの全てについてiが割り切れないことを確 認すれば良い。 • ※厳密には2以上 𝑖以下の整数jについて調 べれば良いが、平方根の計算は除算に比べ かなり遅いのでここではi/2以下について確 認する。 58 演習: 素数判定 • 標準入力から入力された正整数が素数かど うか判定するプログラムを作成せよ。 • isprime_practice_1.c の /*WYCH*/ の箇所を修正すれば良い。 mintty + bash $ $ i 1 $ i 2 $ i 3 $ i 4 gcc is_prime_practice_1.c ./a = 1 is not prime number ./a = 2 is prime number ./a = 3 is prime number ./a = 4 is not prime number 59 演習: 素数判定(ヒント) • 作成すべきプログラム の素数判定部分のフ ローチャートは以下の 通りである 素数判定部分 1 偽 iは2以上 jを2から i/2の範囲 で繰り返し 真 素数と 仮定 1 非素数 iがjで 割切れる 偽 真 非素数 break j++ 60 参考文献 • [1] B.W.カーニハン/D.M.リッチー著 石田晴久 訳、プログラミング言語C 第2版 ANSI 規格準 拠、共立出版(1989)
© Copyright 2024 ExpyDoc