C言語入門

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)