C言語入門
第4週
プログラミング言語Ⅰ(実習を含む。),
計算機言語Ⅰ・計算機言語演習Ⅰ,
https://code.visualstudio.com/
• Microsoft 製プログラミング用テキストエディタ
2015-04-29 にリリースされたばかり のテキストエディタ。Windows 以外 にも Mac, Linux 用もある。
制御構造
条件分岐と繰り返し
流れ図(フローチャート)
• プログラムの処理の流れを図示する方法
条件分岐 処理 端子 定義済み処理 表示 手操作入力 プログラムの開始と終了 代入や演算等 各ステップにおける処理 サブルーチンや関数等 処理の振り分け 画面等への出力 キーボード等からの入力 各部品を矢印で繋ぐ事で処理の流れ を視覚的に表現する。 各部品の内部にはそれぞれのステップ で行う内容に書き換えて使う。 結合子 別のフローチャートと結合 2015-05-12追加条件分岐
if 文と switch 文
(if 文)
• 真偽値による場合分け
if (条件式1) {
// 条件式1が真の場合の処理1
}
条件式1 真 偽 処理1 if 文を使うと特定の条件下で実行する処理を指定出来る。条件分岐
(if, else 文)
• 真偽値による場合分け
if (条件式1) {
// 条件式1が真の場合の処理1
} else {
// 条件式1が偽の場合の処理2
}
教科書 pp.130-133. 条件式1 真 偽 処理1 処理2 if, else 文を使うと特定の条件下で別の処理を指定出来る。(if, else 文)
• 真偽値による場合分け
if (条件式1) {
if (条件式2) {
// 条件式1が真かつ
// 条件式2が真の場合の処理1
} else {
// 条件式1が真かつ
// 条件式2が偽の場合の処理2
}
} else {
// 条件式1が偽の場合の処理3
}
条件式1 真 偽 処理3 条件式2 真 偽 処理1 処理2 if, else 文は任意の数入れ子に出来る。 だだし、入れ子が深くなると読み難くなるので注意。 2015-05-15修正 誤:処理2,3,1 正:処理1,2,3入れ子の条件分岐
(if, else 文)
• 真偽値による場合分け
if (条件式1) {
// 条件式1が真の場合の処理1
} else {
if (条件式2) {
// 条件式1が偽かつ
// 条件式2が真の場合の処理2
} else {
// 条件式1が偽かつ
// 条件式2が偽の場合の処理3
}
}
条件式1 真 偽 条件式2 真 偽 if, else 文は任意の数入れ子に出来る。 else 側の if は else if で置き換ると入れ子を防げる。 教科書 pp.130-133. 処理1 処理2 処理3(if, else if, else 文)
• 真偽値による場合分け
if (条件式1) {
// 条件式1が真の場合の処理1
} else if (条件式2) {
// 条件式1が偽かつ
// 条件式2が真の場合の処理2
} else {
// 条件式1が偽かつ
// 条件式2が偽の場合の処理3
}
else if は任意の数追加出来る。 入れ子を作らずに複数の条件を追加出来る。 条件式1 真 偽 条件式2 真 偽 処理1 処理2 処理3多分岐判断機構 (switch 文)
• 値による場合分け
switch (式) {
case 値1:
// 式が値1の場合の処理1
case 値2:
// 式が値2の場合の処理2
default:
// 他の条件に
// 当てはまらない場合の処理N
};
教科書 pp.134-140. break 文を入れておかないと 次の条件の処理を 連続して実行するので注意。 break break break 式 値1 処理1 処理2 default 値2 処理N平方根の計算
条件分岐の例題
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 を返す。
平方根の計算
• sqrt() 関数は引数が負の場合計算出来ない。
sqrt_practice_1.c #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)); } 1 2 3 4 5 6 7 8 9 10 11 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
演習: 平方根の計算(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
演習: 平方根の計算(if文) ヒント
• sqrt_practice_1.c の 10 行目は 𝑥 を表示して
いる。これを if 文で場合分けしてやれば良い。
条件式 真 偽 𝑥 −𝑥 𝑖 sqrt_practice_1.c printf("sqrt(x): %f¥n", sqrt(x)); 10 𝑥 sqrt_practice_if.c if (条件式) { // 𝑥 を表示 } else { // −𝑥 と 𝑖 を表示 } 10 11 12 13 14• 書式:
• 条件式 ? 式1 : 式2
条件演算子
三項演算子(?:)
[1] pp.63-66, 256-257. 条件式 真 偽 式1 式2 condexprtest.c #include <stdio.h> void main() { int i; fprintf(stderr, "i = ?¥b"); scanf("%d", &i);printf("%s¥n", i ? "not zero" : "zero"); } 1 2 3 4 5 6 7 8 9 mintty+bash+gcc
$ gcc condexprtest.c && ./a i = 1
not zero $ ./a i = 0 zero
演習: 平方根の計算(条件演算子)
• 先程の負の数の平方根の計算を条件演算子
を用いて解決してみよう。
• 10行目の printf では 2 つ目の引数が %f に 3
つ目の引数が %s へ埋め込まれる。
sqrt_practice_condexpr.cdouble x;
fprintf(stderr, "x = ?¥b");
scanf("%lf", &x);
printf("x: %f¥n", x);
printf("sqrt(x): %f%s¥n", sqrt(
/*WYCH*/
),
/*WYCH*/
);
6
7
8
9
10
奇数と偶数の判定
条件分岐の例題
奇数と偶数
• 整数のうち 2 で割り切れるのが偶数、2 で割
り切れないのが奇数
• 2 で割り切れるとは?
• 整数を 2 で割った余り(剰余)が 0
• 2 で割り切れないとは
• 整数を 2 で割った余り(剰余)が 0 以外
C言語の剰余
• 剰余は二項演算子 %
• i % 2 で 2 で割った余りが求まる
• 同じかどうか比較するのは二項演算子 == や !=
• i % 2 == 0 は i を 2 で割った余りが 0 なら真、そ
れ以外は偽
• i % 2 != 0 は i を 2 で割った余りが 0 以外なら真、
それ以外は偽
• i % 2 != 0 は単項演算子 ! を用いて
!(i % 2 == 0) としても同じ意味になる
演算子の優先度
演算子 結合規則 備考
( ) [ ] -> . 左から右→
! ~ ++ -- + - * & (type) sizeof 右から左← 単項演算子 * / % 左から右→ 二項演算子 + - 左から右→ 二項演算子 << >> 左から右→ bitシフト < <= > >= 左から右→ 関係演算子 == != 左から右→ 等値演算子 & 左から右→ bit毎のAND ^ 左から右→ bit毎のXOR | 左から右→ bit毎のOR && 左から右→ 論理演算子(AND) || 左から右→ 論理演算子(OR) ?: 右から左← 三項演算子 = += -= *= /= %= &= ^= |= <<= >>= 右から左← 代入演算子 , 左から右→ [1] p.65. より 高 低 優先度
演習: 偶数かどうか判定する
• 標準入力から入力された整数値が奇数かど
うか判別して、奇数であれば入力された数値
に続けて " is even number¥n" そうでなければ
" is not even number¥n" と表示するプログラ
ムを作成せよ。奇数でない場合は何も表示し
なくて良い。
• if_practice_even.c の
/*WYCH*/
の個所を修
演習: 奇数かどうか判定する
• 標準入力から入力された整数値が奇数かど
うか判別して、奇数であれば入力された数値
に続けて " is odd number¥n" そうでなければ
" is not odd number¥n" と表示するプログラム
を作成せよ。奇数でない場合は何も表示しな
くて良い。
• if_practice_odd.c の
/*WYCH*/
の個所を修正
演習: 奇数か偶数か判定する
• 標準入力から入力された整数値が奇数かど
うか判別して、奇数であれば入力された数値
に続けて " is odd number¥n"、偶数であれば
入力された数値に続けて "is even number¥n"
と表示するプログラムを作成せよ。
• if_practice_evenodd.c の
/*WYCH*/
の個所を
閏年の判定
入れ子の条件分岐の例題
閏年(leap year)とは
• 地球の平均回帰年は365日+約1/4日である
ため1年を356日にしているとカレンダー上の
日付と季節が4年で1日ずつずれてしまう。こ
れを防ぐのが4年に1回設ける2月29日(閏日)。
• 閏年の求め方
• 西暦を4で割り切れるなら閏年?
閏年 判定式 閏年 平年 真 偽閏年(leap year)の判定
• 4で割り切れる(割れる)とは?
• 4で割った余りが0ということ
• 4で割り切れる
: year % 4 == 0
• 4で割り切れない : year % 4 != 0
• C言語では0は偽、0以外は真だったから
• 以下のようにも書けるが・・・
• 4で割り切れる
: !(year % 4)
• 4で割り切れない : (year % 4)
ぱっと見て意味の分かり易い書き方をしましょう % : 剰余算演算子 等値演算子 == : 等しい != : 等しくない ! : 論理否定演算子演算子の優先度
演算子 結合規則 備考
( ) [ ] -> . 左から右→
! ~ ++ -- + - * & (type) sizeof 右から左← 単項演算子 * / % 左から右→ 二項演算子 + - 左から右→ 二項演算子 << >> 左から右→ bitシフト < <= > >= 左から右→ 関係演算子 == != 左から右→ 等値演算子 & 左から右→ bit毎のAND ^ 左から右→ bit毎のXOR | 左から右→ bit毎のOR && 左から右→ 論理演算子(AND) || 左から右→ 論理演算子(OR) ?: 右から左← 三項演算子 = += -= *= /= %= &= ^= |= <<= >>= 右から左← 代入演算子 , 左から右→ [1] p.65. より 高 低 優先度
演習: 4 で割り切れるか表示する
• leap_year_practice_1.c の
/*WYCH*/
を書き
換えて、year が 4 で割り切れる場合 "can be
divided by 4."、割り切れない場合 "can not be
divided by 4." と表示するプログラムを完成せ
よ。
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. 2015-05-11修正 誤:dividec 正:divided
閏年(leap year)の定義
• グレゴリオ暦における閏年の定義
• 判定したい年を西暦で表した際
• 4で割り切れる場合は閏年
(条件1)
• 但し100で割り切れる場合は平年 (条件2)
• 但し400で割り切れる場合は閏年 (条件3)
• 地球の平均回帰年は約365.242199日であ
るため、上記ルールだと約3320年で1日ずれ
る程度で済む。
閏年(leap year)の判定
4で 割れる 平年 真 偽 閏年 条件1閏年(leap year)の判定
4で 割れる 平年 真 偽 100で 割れる 閏年 真 偽 平年 条件1 +条件2閏年(leap year)の判定
4で 割れる 平年 真 偽 100で 割れる 閏年 真 偽 400で 割れる 閏年 平年 真 偽 条件1 +条件2 +条件3 完成演習: 閏年(leap year)の判定
• leap_year_practice_2.c の
/*WYCH*/
の
部分を書き換えて閏年か判定するプログラムを
完成せよ。
• /*WYCH*/
の部分には year に格納された西
暦が閏年であれば変数 leap_year_flag に
1 を閏年でなければ 0 を代入するコード作成
すれば良い。これは前のページのフローチャート
を参考に if 文を 3 重の入れ子にすれば出
来る。
• なおここでは紀元前については考慮する必要は
ない。
演習: 閏年(leap year)の判定
• ヒント1
• 前述の条件1~3のフローチャートをif,else文
で書くと以下のようになる。
条件1 if (/*条件1*/) { /*閏年*/ } else { /*平年*/ } 条件1+条件2 if (/*条件1*/) { if (/*条件2*/) { /*平年*/ } else { /*閏年*/ } } else { /*平年*/ } 条件1+条件2+条件3 if (/*条件1*/) { if (/*条件2*/) { /*条件3のif,else文*/ } else { /*閏年*/ } } else { /*平年*/ }演習: 閏年(leap year)の判定
• ヒント2
• 閏年なら leap_year_flag に 1 そうでなけれ
ば 0 を代入すれば良い。
条件1 if (/*条件1*/) { leap_year_flag = 1; /*閏年*/ } else { /*平年*/ }月の名前
多分岐の例題
演習: 月の名前の表示
• 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
繰り返し(ループ)
for文, while文, do-while 文によるループと
ループの再開と脱出
• continue 文
• while, do while, for 文内で使用可能
• 以降の処理を中断してループ末尾から再開する
• for文では後処理(第3パラメータ)も実行する
• break 文
• while, do while, for, switch 文内で使用可能
• 以降の処理を中断してループを脱出する
• switch文の場合はswitch文から脱出する
後判定ループ (do while 文)
• 真偽値による繰り返し
do {
// 条件式 が真の場合の処理
} while (条件式);
条件式 処理 真 偽 continue break do while 文は、ループ内の処理を 1回以上実行する(=最低1回は実行する)。前判定ループ (while 文)
• 真偽値による繰り返し
while (条件式) {
// 条件式が真の場合の処理
}
教科書 pp.119-122. 条件式 真 偽 処理 continue break while 文は、ループ内の処理を 0回以上実行する(=実行しない場合もある) 。 2015-06-04修正 誤:式2 正:条件式初期化・更新処理付きループ (for 文)
• 真偽値による繰り返し
for (式1; 式2; 式3) {
// 式2が真の場合の処理
};
式2 式1 真 偽 処理 式3 continue break for 分は前判定ループで 式1による初期化と 式3による更新処理を ひとまとめにしてコンパクトに書ける。for文と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 = 0;
while (i < 10) {
// ループ内の処理
i++;
};
i < 10 i = 0 真 偽 処理 i++ for文の continue break 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++; } 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
continue 文
• 以下のループ内に更に小さなループが含ま
れない場合の continue は goto contin と同義
for (...) { // ... contin: ; } do { // ... contin: ; } while (...); while (...) { // ... contin: ; }
goto文
• 指定した名札付き文へ移動(ジャンプ)する
• 名札(label)は以下のように設定出来る
ラベル名: 文
[1] p.281.
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: ; goto 文は 余程理由がない限り使わないこと
簡易版 seq コマンドの作成
前判定ループの演習
seq コマンド
• UNIX系のOSに標準で搭載されているコマンド
• 書式:
• seq FIRST LAST
• 機能:
• FIRST から LAST までの整数を 1 刻みで小さい順に表示す
る。
• 他にも詳細な機能があるが、ここでは省略
• 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
素数判定
素数
• 1と自分以外に正の約数を持たない自然数
(正整数)で1でない数
• 調べたい数をiとすると、2以上i/2以下の整
数jの全てについてiが割り切れないことを確
認すれば良い。
• ※厳密には2以上 𝑖以下の整数jについて調
べれば良いが、平方根の計算は除算に比べ
かなり遅いのでここではi/2以下について確
認する。
演習: 素数判定
• 標準入力から入力された正整数が素数かど
うか判定するプログラムを作成せよ。
• isprime_practice_1.c の
/*WYCH*/
の箇所を修正すれば良い。
mintty + bash $ gcc is_prime_practice_1.c $ ./a i = 11 is not prime number $ ./a i = 2 2 is prime number $ ./a i = 3 3 is prime number $ ./a i = 4