計算機通論・情報工学通論：C 言語入門
九州工業大学・情報工学部・生命情報工学科
松山明彦
2017 年 4 月

2
はじめに：
生命情報工学科の編入生向けの情報工学通論と計算機通論のテキストです。内容は、C 言語入門です。Java 言語も少し紹介します。1 回の授業で 1 章を進みます。講義ではいろいろ図も書いていく予定なので、テキストに無い説明などは各自で書き入れてください。各自でノートを作るのも良いでしょう。テキストには必要最小限のことしか書いてありません。他の C 言語の参考書を買う
ことをお勧めします。
半期で通常の一年分は進みます。しっかり復習をしましょう。

Moodle に，演習問題の回答例などを載せていきます。
では、がんばってください。

2017 年 4 月
生命情報工学科 松山明彦

3目次
第 1 章 Ｃ言語の第一歩

1.1
1.2
1.3

Ｃ言語の基本 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
プログラミングの過程 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
プログラミングの基本 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.3.1
1.3.2

コメント文 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.3.3
1.3.4
1.3.5

挨拶プログラム . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1.3.6
1.3.7
1.4

和を求めるプログラム . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

printf() 関数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
文字列入力をするプログラム

9
9
10
10
10
10
11
11

. . . . . . . . . . . . . . . . . . . . . . . . .
たし算のプログラム . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1212

1.3.8 scanf() 関数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
条件による分岐 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.4.1 ２つの整数の差の絶対値を求めるプログラム . . . . . . . . . . . . . . . . .

13
13
13

閏年の判定プログラム . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
switch–case 文 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14
15

1.4.4 四則演算プログラム (switch-case) . . . . . . . . . . . . . . . . . . . . . . .
1.4.5 プリプロセッサ命令 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
演習問題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

16
17
18

1.4.2
1.4.3

1.5

プログラムの基本構造 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9

第 2 章 繰り返しの処理と配列

2.1

while 文 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

21
21

2.2

2.1.1 １から５までの２乗を求める . . . . . . . . . . . . . . . . . . . . . . . . .
for 文 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

21
22

2.3

2.2.1 １から５までの 3 乗を求める . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.2 for の入れ子構造 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1 次元配列 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

22
22
23

2.3.1
2.3.2
2.4

入力の逆順に数値を表示する. . . . . . . . . . . . . . . . . . . . . . . . .
ソート（整列） . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

２次元配列

23
24

2.4.1
2.4.2

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
２次元配列を用いた簡単なプログラム . . . . . . . . . . . . . . . . . . . .
素数を求める . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

25
25
27

2.4.3

多次元配列 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

28

4
2.5

演習問題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

第 3 章 実数型変数と標準関数

3.1

29
29
29

実数型と整数型の混在 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
台形の面積 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2930

3.1.5 平方根を求める . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
標準関数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2.1 ｃ言語での関数の呼び出し . . . . . . . . . . . . . . . . . . . . . . . . . . .

30
32
33

標準関数による平方根を求める . . . . . . . . . . . . . . . . . . . . . . . .
２次方程式の根 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

33
33

3.2.4 sin と cos の表を作る . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Ｃ言語のいろいろな演算子 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3.1 インクリメント演算子 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

34
35
35

3.2.2
3.2.3
3.3

3.3.2
3.3.3

3.4

29

実数型データ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.1 float 型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.2 double 型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.1.3
3.1.4
3.2

28

入力の逆順に数値を表示する. . . . . . . . . . . . . . . . . . . . . . . . .
複合代入演算子 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

36
36

3.3.4 printf() 関数の書式指定 . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3.5 エスケープ文字 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
演習問題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

36
37
37

第 4 章 文字データの取り扱い

4.1

文字型データ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

39
39

4.2

4.1.1 文字型変数の宣言 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
文字型と整数型 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

39
39

文字コード . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

getchar( ) と putchar( ) 関数 . . . . . . . . . . . . . . . . . . . . . . . . . .
判定プログラム . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

40
40
41

4.2.4 文字型データの算術演算 . . . . . . . . . . . . . . . . . . . . . . . . . . . .
文字列の扱い . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

41
42

4.3.1
4.3.2
4.3.3

文字型配列の宣言 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

42
42
43

4.3.4
4.3.5

文字の長さを答えるプログラム . . . . . . . . . . . . . . . . . . . . . . . .

4.2.1
4.2.2
4.2.3
4.3

文字列定数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
文字列の入出力 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
文字列のコピー . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

43
44

4.4

4.3.6 文字列の接続 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
２次元の文字型配列 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.4.1 ２次元文字型配列の宣言 . . . . . . . . . . . . . . . . . . . . . . . . . . . .

45
46
46

4.5

演習問題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47

5
第 5 章 ポインタの取り扱い

5.1

5.2

ポインタ (番地） . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5.1.1

ポインタ変数の宣言 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5.1.2
5.1.3

ポインタ演算子 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
ポインタを使った四則演算 . . . . . . . . . . . . . . . . . . . . . . . . . . .

49
51

5.1.4 配列とポインタ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
ポインタによる文字列の取り扱い . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2.1 文字列のコピー . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

51
53
53

文字列の分割 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
配列とポインタ変数の違い . . . . . . . . . . . . . . . . . . . . . . . . . . .

54
55

5.2.4 見掛け上のコピー . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2.5 ２つの文字配列を接続する . . . . . . . . . . . . . . . . . . . . . . . . . . .
演習問題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

55
56
57

5.2.2
5.2.3

5.3

第 6 章 ファイルの入出力

6.1

6.2

ファイルの基本操作. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

6.1.1
6.1.2

ファイル・ポインタ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

6.1.3
6.1.4
6.1.5

ファイルのクローズ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

6.1.6
6.1.7

ファイルの書き込み . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

ファイルのオープン . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
ファイルの読み出し . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
文字数を数える . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
ファイルをコピー . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

データの書式付き入出力 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

59
59
59
60
60
61
61

ファイルを読み込んで表示する。 . . . . . . . . . . . . . . . . . . . . . . .

演習問題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

65

ファイルに書き込むプログラム . . . . . . . . . . . . . . . . . . . . . . . .

第 7 章 関数の作り方

7.1

59
59

63
63
64

6.2.1
6.2.2
6.3

49
49
49

関数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7.1.1

引数を持たない関数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7.1.2
7.1.3
7.1.4

引数を持つ関数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

67
67
67

引数と返り値を持つ関数 . . . . . . . . . . . . . . . . . . . . . . . . . . . .

68
69
70

7.2

7.1.5 関数のプロトタイプ宣言 . . . . . . . . . . . . . . . . . . . . . . . . . . . .
変数の特性：スコープ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

70
71

7.3

7.2.1 int 型と double 型の関数（スコープ） . . . . . . . . . . . . . . . . . . . .
7.2.2 変数の記憶クラス . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
文字列を渡す . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

72
73
74

7.4

演習問題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

75

引数の受け渡しの例 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

6
第 8 章 関数とポインタ
ポインタ渡し . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

77
77
77

8.2

8.1.2 複数のデータを返す関数 . . . . . . . . . . . . . . . . . . . . . . . . . . . .
関数の引数と配列 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

78
79

8.3

8.2.1 配列の受け渡し . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8.2.2 配列とポインタ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
main() 関数の引数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

79
80
81

8.4

8.3.1 main() 関数の引数渡し . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
関数を指すポインタ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

82
83

関数へのポインタの配列 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

84
85

8.1

関数の引数とポインタ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8.1.1

8.5
8.6

演習問題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

第 9 章 構造体と列挙型

9.1

9.2

構造体 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9.1.1 複素数を扱うプログラム . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9.1.2
9.1.3

新しいデータ型の定義 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9.1.4
9.1.5
9.1.6

構造体と関数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

構造体を使ったプログラム . . . . . . . . . . . . . . . . . . . . . . . . . . .
構造体とポインタ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
アロー演算子： ー＞ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

列挙型とデータの定義 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9.2.1

データ型の定義 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9.3

自己参照構造体 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

9.4

9.3.1 自己参照構造体の宣言 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
演習問題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

87
87
87
88
88
89
91
91
93
95
95
96
99

第 10 章 Java 言語の基本

101
10.1 Java プログラムの実行 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101

10.1.1 Hello プログラム . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
10.2 Java プログラムの形式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
10.3 変数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
10.4 クラス・メソッド . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
10.5 インスタンス . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
10.5.1 インスタンスの生成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
10.6 オブジェクトの引き渡し . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
10.7 継承 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
10.8 演習問題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
第 11 章 Java の制御文・配列・関数・その他

111

7
第 12 章 アプレット・グラフィックス

113
12.1 画面の基本 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
12.2 正方形・長方形 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
12.3 円・楕円・直線 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
12.4 多角形と折れ線グラフ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
12.5 関数を描く . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
12.6 アニメーション . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
12.7 演習問題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122

第 13 章 高分子のランダムウォーク

123
13.1 正方形の壁で囲まれた２次元領域をランダムに動く高分子 . . . . . . . . . . . . . 123
13.2 演習問題 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127

9

第 1 章 Ｃ言語の第一歩

1.1

Ｃ言語の基本現在，広く使われているプログラミング言語として，FORTRAN, COBOL, LISP, BASIC,C,

C++, Java などがあります。その中でもＣ言語は UNIX の基本言語であり，豊富なライブラリーを利用できるなど，いろいろな利点があります。最近ではオブジェクト指向の機能を追加した C++
や，JAVA などが主流になりつつありますが，Ｃ言語はそれらの基礎としても重要です。基本的には言語は何でも構わない，一つの言語をしっかりと学べばやりたいことはある程度出来るとおもいます。プログラム言語は英語の学習とよくにているところがあります。実際やってみること，自分でどんどん勉強していくこと。これが上達への近道です。

1.2

プログラミングの過程

プログラムを（１）編集・
・・vi や emacs などの編集ソフトでプログラムを記述する。
（２）コンパイル・・・ｃ言語などの高級言語で書かれたソース・プログラムはそのままではコン
ピューターに理解してもらえません。ソース・プログラムはコンパイラと呼ばれる翻訳プログラム
によって機械語に翻訳されます。機械語に翻訳されたプログラムを、オブジェクト・プログラムという。
（３）リンク・・・ｃ言語ではよく用いる機能や関数は標準関数として用意されています。標準関数を取り出し、オブジェクト・プログラムに付加する作業をリンクと呼びます。
（４）実行・・・プログラムの実行

Unix での操作例% cc -o test test.c ・・・・コンパイルとリンク% test ・
・
・
・
・
・
・
・プログラムの実行
あるいは、% cc test.c ・・・・コンパイルとリンク% a.out または、./a.out ・・プログラムの実行

第1章

10

Ｃ言語の第一歩プログラミングの基本

1.3

コメント文

1.3.1

/＊ コメントの開始＊/コメントの終了/＊この中の文章はコンパイルされないで無視される。メモがわりに使う＊/
コメント文を書くことで，後でプログラムを見たときに理解しやすい。

プログラムの基本構造

1.3.2

main() 関数の定義、プログラムは exit(0)，又は, return (0) で終了する。
/***********************
program.c 2003.12.10・・プログラムに関する情報などを書く（プログラム・ヘッダー）**********************/#include <stdio.h>
int main(void){/* ここに実行文などを書く。*/
exit(0);}

1.3.3

挨拶プログラム

0:/***101.c*********/
1: #include <stdio.h>
2:
3:
4;
5:
6:

int main(void){
printf("Hello\n");return(0);7:}
8: /***********************/
1: ヘッダーファイルである stdio.h を読み込むことを示す。printf() 関数などを使うために必要。
2: main() 関数の定義。ここで、int は main() 関数の返す値が整数値であることを指定している。
また void は main() 関数に渡されるデータがないことを示す。
3: main() 関数の始まりを示す。
4: printf() 関数で文字を出力する。

1.3. プログラミングの基本

11

6: main() 関数にゼロを返して、終了する。正常終了の時はゼロ、異常終了の時は１を返す。１の場合はエラーメッセージが出る。
7: main() 関数の終了。

1.3.4

和を求めるプログラム

0:/**** 102.c *********************/
1: #include <stdio.h>
2: int main(void)3:
4:{
int answer;

5:
6:
7:

answer=10+20;
printf("ans=%d\n",answer);

8:
9:

return(0);}10: /***********************/
4: 整数型変数 answer の宣言。
5: 10+20 を変数 answer に入れる。(数字を answer という箱に入れるイメージを持つとよい。必ず
箱は左に，入れる数字は右に書く。）

6: 結果を出力する。

1.3.5

printf() 関数

図 1.1: printf() 関数の書式

printf() 関数は文字を出力する関数である。” ”内の文字はそのまま出力される。
\n（または、￥ n) は改行の意味。
変換方法を指定する例
1. printf(”ans= %d \n”, nn); 変数 nn の値を%d に入れ、” ”内をそのまま出力。
2. printf(”%d \n”, nn); ３０を出力してから、改行する。
3. printf(”%d ”, nn); ３０を出力するだけ、改行しない。

第1章

12

Ｃ言語の第一歩%d などを変換仕様という。%d は１０進数、%x は１６進数、%f は浮動小数点、%c は文字、%s
は文字列を出力する時に使い分ける。

文字列入力をするプログラム

1.3.6

1: /****103.c*********************/
2: #include <stdio.h>
3:
4:
5:

int
{

main(void)char ss[80];6:7:

printf("Input ss>>");

8:
9:
10:

scanf("%s", ss);

11:
12:

return(0);printf("Moji_Retsu=%s\n",ss);}

13:/***********************/
5: 文字型変数 ss[] の文字宣言。[80] は８０文字格納出来る。
7: ” ”内の文字を出力する。
8: scanf(”%s”, ss) 関数はキーボードから文字列を入力する関数。文字列を入力する時は、%s を使う。
10: 文字列を出力

1.3.7

たし算のプログラム

0:/*****104.c********************/
1: #include <stdio.h>
2:
3:
4:
5:
6:

int
{

main(void)
int first_num, second_num;
int answer;

7:
8:

printf("Input Number 1 >>");
scanf("%d", &first_num);

9:
10:
11:

printf("Input Number 2 >>");
scanf("%d", &second_num);1.4. 条件による分岐
12:
13:
14:

13

answer=first_num+second_num;
printf("%d+%d=%d \n",first_num,second_num,answer);

15:
16:

return(0);}17: /***********************/
8, 10: scanf() 関数はキーボードからデーターを読み込む関数。整数を入力する時は、%d を使う。

1.3.8

scanf() 関数

scanf() 関数はキーボードからデーターを読み込む関数である。変数名の前に＆（アンパサンド）を付けること。ただし、配列を読み込むときは、この＆はいらないことに注意（1.3.6 を参照）。

図 1.2: scanf() 関数の書式

1.4

条件による分岐1.4.1

２つの整数の差の絶対値を求めるプログラム

0:/***105.c**********************/
1: #include <stdio.h>
2:
3:
4:
5:
6:

int
{

main(void)
int first_num, second_num;
int answer;

7:
8:
9:

printf("Input Number 1 >>");
scanf("%d", &first_num);
printf("Input Number 2 >>");

10:
11:

scanf("%d", &second_num);

第1章

14
12:
13:
14:

if(first_num>second_num){answer=first_num-second_num;
}

15:
16:

else{
answer=second_num-first_num;

17:
19:
20:

Ｃ言語の第一歩}
printf("|%d-%d|=%d \n",first_num,second_num,answer);

21:
22:

return(0);23:}
24: /***********************/

図 1.3: if()–else 文の構文; 条件が真の時文１，２を実行、条件が偽の時文３を実行

1.4.2

閏年の判定プログラム

閏年は４で割り切れるが、１００で割り切れない。ただし４００で割り切れる場合も閏年である。「４で割り切れる」「１００で割り切れない」「４００で割り切れる」という３つの部分にわけると簡単な論理演算子で書けます。

0:/*****106.c********************/
1: #include <stdio.h>
2:
3:
4:
5:
6:
8:
9:

int
{

main(void)
int year;printf("Input Year 1>>");
scanf("%d", &year);1.4. 条件による分岐

15

10:
11:
12:

if( (year % 4 ==0 && year % 100 !=0) || year % 400 ==0){
printf("%d : Uruu-Doshi ! \n",year);
}

13:
14:

else{
printf("%d : Futuu-no toshi ! \n",year);

15:
16:
17:

}
return(0);18:}
19: /***********************/
10: 西暦を４で割った余りがゼロは「year % 4 ==0」と書けます。「year % 100 !=0」は１００で割って余りがゼロでないを示します。

図 1.4: 論理演算子

1.4.3

switch–case 文

１から３の整数を入力すると、それに対応する英語を出力するプログラムを以下に示す。

0:/****107.c*********************/
1: #include <stdio.h>
2:
3:
4:
5:
6:

int
{

main(void)
int select;/* 入力される整数 */printf("Input Number 1 or 2 or 3 >>");

8:9:

scanf("%d", &select);

10:
11:

switch(select){case 1: printf(" One \n ");

第1章

16
12;
13:
14;

break;
case 2: printf(" Two \n ");break;

15:
16:

case 3: printf(" Three \n ");break;

17:
17:
18:

default:

Ｃ言語の第一歩}
return(0);19:}
20: /***********************/
12: break 文がないと１３行目に進む。
14: break 文がないと１５行目に進む。
switch の後ろのカッコ内に書かれた変数や整数の値が case に続く定数と一致すると、その case 以
降に書かれた実行文が全て実行される。どの case に続く定数とも一致しない場合は case で書かれた文を全部飛ばして、下（default) に行く。break 文は if 文や switch-case 文などの分岐や繰り返し制御から強制的に抜け出す働きをする。

図 1.5: switch-case 文と break

1.4.4

四則演算プログラム (switch-case)

１から４の整数番号を入力して、２つの整数の四則演算を行うプログラムです。ただし、１は和、２は差、３は積、４は商に対応しているとする。

0:/****108.c*********************/
1: #include <stdio.h>
2: int main(void)
3:
4:

{
int

first_num, second_num;/* 入力される 2 整数 */1.4. 条件による分岐
5:
6:
7:

17
/* 演算の種類 *//* 答え */

int operation;
int answer;

8:
9:printf("Input Integer Number 1 >");
scanf("%d", &first_num);

10:
11:
12:

printf("Input Integer Number 2 >");
scanf("%d", &second_num);
printf("operation (1) +, (2) -, (3) *, (4) / >>");

13:
14:

scanf("%d", &operation);

15:
16:
17:

switch(operation){case 1: answer=first_num+second_num;
printf(" %d+%d=%d \n ", first_num, second_num, answer);

18;
19:

break;
case 2: answer=first_num-second_num;

20:
21;
22:

printf(" %d-%d=%d \n " , first_num, second_num, answer);
break;
case 3: answer=first_num*second_num;

23:
24;

printf(" %d * %d=%d \n " , first_num, second_num, answer);
break;

25:
26:
27;

case 4: answer=first_num/second_num;
printf(" %d / %d=%d \n " , first_num, second_num, answer);
break;

28:
29:

default: printf("Error: Operation (1-4) \n");}30:
return(0);31:}
32: /***********************/
28: １−４以外の数字を選ぶと 28 行目に飛ぶ。

1.4.5

プリプロセッサ命令

前のプログラムの定数のようにプログラム中に現れる見ただけでは意味のわからない定数を、マジック・ナンバーと呼びます。case 1 case2 などの数字です。マジックナンバーはプログラムを難解にします。１が和、２が積に対応しているのは、プログラムの中身を読まないとわからない。そこで、プリプロセッサ命令による定数定義を用いるとプログラムが理解しやすくなります。

/*************************/#include <stdio.h>

第1章

18

Ｃ言語の第一歩図 1.6: #define 命令。セミコロン；はいらない。定数名は大文字にするのが習慣#define#define#define

ADD
SUB
MULT

1
2
3

/* 足し算 *//* 引き算*//* かけ算 */#defineDIV

4/* 割り算 */int main(void){略switch(operation){略}
return(0);}/***********************/

1.5

演習問題

1. 1.3.7 を参考にして、printf() と scanf() 関数を用いて２つの整数の四則演算を行うプログラ
ムを作りなさい。
2. 1.4.1 を参考にして、if()— else 文を使って、２つの整数を入力し、その大きい方を小さい方で割った商と余りを求めるプログラムを作りなさい。
3.

試験の成績が９０以上ならば A, ９０点未満７０点以上なら B, ７０点未満６０点以上ならC, ６０点以下は不可 (Out) として、試験の点数を入力すると、評価を出力するプログラムを作りなさい。if()— else if —- else を用いて考えてください。

4. 1.4.4 のプログラムでゼロで割るとどうなるか？またこれを避けるようなプログラムを書いてください。

1.5. 演習問題

19

5. 1.4.5 を参考にして#define 命令を用いて 1.4.4 の四則演算プログラムを書き換えて下さい。
6.

#include <stdio.h>などの、.h が付くファイルをヘッダーファイルという。C 言語で使う、main() や printf() などを使うためにプログラム中へリンクしている。では、この<stdio.h>は Unix のツリー構造のなかのどこにあるでしょうか？絶対パス名で答えてください。

21

第 2 章 繰り返しの処理と配列

2.1

while 文2.1.1

１から５までの２乗を求める

図 2.1: while() 文の構文

0:/***201.c*********/
1: #include <stdio.h>
2:
3:
4;

int
{

5:
6:
7:

int number;/* ２乗する数

int answer;/* 答え */*/

number=1;
while(number<=5){/* while 文の始まり */answer=number*number;

8:
9:
10:
11:
12:
13:
14:

main(void)

printf(" %d^2=%dnumber=number+1;}\n", number, answer);/* number に１加える *//* while 文の終わり */exit(0);}

15: /***********************/
7: number に初期値１を与える。
8: while( 条件) 文：条件が真なら while 文に書かれた文 が繰り返し実行される。つまり、number

第2章

22

繰り返しの処理と配列が５以下であるとき、while 文を繰り返す。

11: number に１を加える。

2.2

for 文

2.2.1

１から５までの 3 乗を求める

0:/***202.c*********/
1: #include <stdio.h>
2: int main(void)3:
4;{
int number;/* 3 乗する数

5:
6:
7:

int answer;/* 答え */

8:
9:

for(number=1; number<=5; number=number+1){
answer=number*number*number;

10:
11:
12:
13:
14:

printf(" %d^3=%d}*//* for 文の始まり */\n", number, answer);/* for 文の終わり */exit(0);}

15: /***********************/

図 2.2: for() 文の構文

2.2.2

for の入れ子構造

以下は、九九の表を出力するプログラムである。

for(初期値設定；反復条件；変更処理）｛ 文１；分２；｝が入れ子構造になっています。

2.3. 1 次元配列

23

0:/***203.c*********/
1: #include <stdio.h>
2: int main(void)3:
4;{
int i,j;5:
6:
7:

for(i=1; i<=9; i=i+1){
for(j=1;j<=9;j=j+1){

8:
9:
10:
13:
14:

printf(" %d\n", i*j);}}exit(0);}

15: /***********************/

2.3

1 次元配列

変数はデーターを入れる箱の様なものであるが、配列はデータを入れる箱がたくさん並んだものと考える事が出来る。配列の宣言は、例えば、

int sss[5];/* １次元配列*/である。この場合、配列名が sss で、配列の要素が５個ある整数型の配列を宣言している。配列の要素は要素番号によって識別される。Ｃ言語では要素番号は０から始まる整数である。また、この例のように、配列の要素が１次元に並んだ配列を、１次元配列とよぶ。

図 2.3: 配列要素へのデータの代入: sss[2]=10;

2.3.1

入力の逆順に数値を表示する

0 から４の５つの整数を入力すると、入力された順番と逆順で表示するプログラム。for 文と配列を使ってある。

第2章

24

繰り返しの処理と配列

0:/***204.c*********/
1: #include <stdio.h>
2: int main(void)3:
4;{
int array[5];

5:
6:
7:

/*

入力するデータ数は５個 */int count;for(count=0; count<5; count=count+1){

8:
9:

printf("Input number %d >", count);
scanf("%d", &array[count]);

10:
11:
12:

}
for(count=4; count>=0; count=count-1){

13:
14:

printf("No.%d : %d ", count, array[count]);}

15:
exit(0);16:}
17: /***********************/
4: ５つの要素を持つ int 型の配列 array[5] を宣言している。
9: 配列要素 array[] にデータを入れる。
配列 array[5] には arry[0] から arry[4] までの５つの要素がある。配列番号はゼロから始まることに注意。array[5] は存在しないので count=５はエラーとなる可能性がある。

2.3.2

ソート（整列）

勝手な順序で入力されたデータを大きさの順に並べ替えることをソート（整列）という。隣り合った２つのデータを比較し、正しい順序ならそのままにしておき、逆順であれば入れ替える、という操作を先頭から最後まで繰り返せばよい。これを交換法という。

0:/***205.c*********/
1:
2:#include <stdio.h>#define N 10

3:
4:
5:

int main(void) {int i,j,m,n;

6:
7:

int a[N+1],b;
printf("n=");8:
9:scanf("%d",&n);/* データの個数 N を定義している *//* データの個数 n をキーボードから代入する */2.4. ２次元配列
10:
11:
12:

18:
19:

/* n>N の時強制終了 */if(n>N) exit(1);for(i=1; i<=n; i=i+1){

13:
14:
15:
16:
17:

25

printf("a[%d]=",i);scanf("%d",&a[i]);}printf("*******\n");for(m=n-1; m>=1; m=m-1){

20:
21:
22:

for(j=1;j<=m;j=j+1){
if(a[j]>a[j+1]){b=a[j];

23:24:

a[j]=a[j+1];
a[j+1]=b;

25:
26:
27:
28:
29:

/* 配列の入れ替えをする

*/

}
}
}
for(i=1;i<=n;i=i+1){
printf("a[%d]=%d \n",i,a[i]);

30: }
31: }
32: /***********************/

2.4

２次元配列

int array[3][5];/* 2 次元配列

３行５列*/

配列のデータ型 (int) と配列名（array)、２次元両方向にそれぞれどれだけの要素を持つかを示す
２つの要素数を示す。この例の場合、５列３行の配列を宣言する。

2.4.1

２次元配列を用いた簡単なプログラム

３行３列の数表を読み込み、縦の和と横の和を計算するプログラム。

0:/***206.c*********/
1:#include <stdio.h>
2:
3:

int main(void){
int table[3][3];4:

int y_sum[3],x_sum[3];/* ３行３列の配列の宣言 *//* 縦と横の和 */

第2章

26

繰り返しの処理と配列

図 2.4: ２次元配列要素へのデータの代入

5:6:

int x;
int y;/* 行のためのカウンター変数 *//* 列のためのカウンター変数 */7:8:

for(x=0; x<3;x=x+1){/* 数表の入力 */

9:
10:
11:

for(y=0;y<3;y=y+1){
printf("[%d}[%d]=",x,y);
scanf("%d",&table[x][y]);

12:
13

}

14:
15:
16:

y_sum[x]=0;
x_sum[x]=0;
}

17:
18:

/****和の計算*************/
for(x=0;x<3;x=x+1){

19:
20:
21:
22:
23:
24:
25:
26:

/* 初期化：始めにゼロを入れておく */for(y=0;y<3;y=y+1){
y_sum[x]=y_sum[x]+table[x][y];x_sum[x]=x_sum[x]+table[y][x];}}/***** 結果の出力 ******/
for(y=0;y<3;y=y+1){

27:
28:
29:

}
printf("********************\n");

30:
31:

printf("%d \t %d \t %d \n", x_sum[0],x_sum[1],x_sum[2]);printf("%d \t %d \t %d \n", y_sum[0],y_sum[1],y_sum[2]);

32:
33:

printf("%d\t %d \t %d \n",table[y][0], table[y][1],table[y][2]);exit(0);2.4. ２次元配列
34:
35:
36:

27

}
/**************************/

printf() 文を使って、分かりやすいように表示するようにする。

2.4.2

素数を求める１００までの素数をすべて求めるプログラムを示します。
考え方（エラトステネスのふるまい）：（１）まず２以上の整数を並べておきます。２は素数です。
２の倍数はすべて２で割り切れるから，ｘ印をつけて除外します。（２）次に２をのぞく最小の素数は３です。３の倍数にｘ印をつけて除外します。（３）３より大きい整数でｘ印の着いていない
最小の数は５です。５の倍数にｘ印をつけて除外します。考慮する最大の数までこれを繰り返せば，ｘ印の付いていないのが素数になります。

0:/***207.c*********/
1:#include <stdio.h>/* 素数チェックの最大数 *//* 素数 */

2:
3:

#define MAX 100#define PRIME

4:
5:
6:

#defineint main(void) {7:
8:

int number[MAX+1];
int i,j;

9:
10:
11:

/******* 素数配列の初期化********/
for(i=1;i<=MAX; i=i+1){
number[i]=PRIME;

12:
13:
14:
15:
16:

0

NOT_PRIME

1

/*

素数でない

/* 素数をチェックする配列 *//* カウンター変数 */}/****エラトステネスのふるまい***/number[1]=NOT_PRIME;
for(i=2; i<=MAX; i=i+1){

17:
18:

if(number[i]==PRIME){
for(j=2*i ; j<=MAX ; j=j+i ){

19:
20:
21:

*/

number[j]=NOT_PRIME;
}
}

22:
23:

}

24:
25:

/***素数の表示*********/
for(i=1 ; i<=MAX ; i=i+1){

第2章

28
26:
27:
28:

if(number[i]==PRIME){
printf("%d \n",i);
}

29:
30:

}

繰り返しの処理と配列

31:
exit(0);32: /***********************/
3,4: ０を素数に１を否素数に対応させるために PRIME と NOT PRIME という定数を定義する。
11: 配列の要素番号を調べる数に対応させる。
18: j=j+i であることに注意！

2.4.3

多次元配列

int array[5][3][4];/* 配列宣言例*/

２次元以上の配列をあつかうことが出来る。(上の例は３次元の配列）
配列はコンピューター上のメモリに確保されます。たとえば，配列をint array[1000][1000][1000]と定義すると，int 型変数は４バイトであるので，４（バイト）× 1000（要素）× 1000（要素）×1000（要素）＝ 4 × 109 バイト=4G バイトのメモリーが必要になります。

2.5

演習問題

1. 2.2.1 を参考にして、１から１００までの整数の和を計算するプログラムを書いてください。
2. フィボナッチ数列は、a0 =0, a1 = 1 で始まり、n=1,2,3, · の順に
an+1 = an + an−1 ,という規則で生成される数列である。任意のｎを与えたときのフィボナッチ数列を計算するプログラムを作りなさい。配列 a[n] を用いること。また、n の値が最大いくつまで正確な値が出るか調べよ。なぜ正確な値が出なくなるか？その理由を考えよ。（ヒント：ビットとバイト）

3. 3× 3 の行列を２つ読み込んで、２つの行列の和と積を求めるプログラムを作りなさい。

29

第 3 章 実数型変数と標準関数

実数型データ

3.1

1,2 などは整数型変数ですが，1.0 や 2.0 とすると実数型変数になります。小数点をつけると実数型の変数になる。

3.1.1

float 型

float

japan

/*float 型実数 japan の宣言 */最大値 3.4×1038 , 最小値 -3.4×1038 の実数をあつかえる。７桁の精度を持つ。

3.1.2

double 型

doublejapan/*double 型実数 japan の宣言 */最大値 1.7×10308 , 最小値 -1.7×10308 の実数をあつかえる。15 桁の精度を持つ。

printf() や scanf() 関数で用いる実数型のための書式文字：%ffloat 型%lf
double 型

3.1.3

実数型と整数型の混在

0:/***301.c*********/
1:
2:
3:
4:
5:

#include <stdio.h>
int main(void){
int count;double a,b;

6:
7:
8:

count=7;
a=1.0;
b=a+count/2;

9:
10:

printf("%lf",b);

第3章

30

実数型変数と標準関数

11:exit(0);12:}
13: /***********************/
8: 変数 count や 2 は整数型であるので，count/2 は３となる。従って b=4 となる。

台形の面積

3.1.4

0:/***302.c*********/
1: #include <stdio.h>
2:
3:
4:

int
{

main(void)
float a,b,h,s;

5:6:

printf("a=\n");/*上辺の長さ*/

7:
8:
9:

scanf("%f",&a);printf("b= \n");scanf("%f",&b);/* 下辺の長さ */

10:
11:

printf("h= \n");scanf("%f",&h);

12:
13:
14:

s=(a+b)*h/2.0;/*台形の面積*/
printf("%f",s);

15:
16:

/* 高さ*/

exit(0);}

17: /***********************/
12: 実数型の計算式に使う定数には小数点を付ける。

3.1.5

平方根を求める

入力した実数の平方根を求めるプログラムを作る。
２分法：たとえば２の平方根を求める場合を考えよう。２の平方根は f (x) = x2 − 2 の２次曲線
が x 軸と交わる交点である。f (2) = 2 が正，f (0) = −2 が負であることから平方根はは０と２の間にあることがわかります。次に x = 0 と２の間の中点１を考える。f (1) = −1 は負ですから，ｘ軸との交点は x = 1 と x = 2 の間にあります。さらに１と２の中点 x = 1.5 を考えて，２次曲線のｘ軸の交点が x = 1 と x = 1.5 の間にあるか，x = ２ と x = 1.5 の間にあるかを調べる。この手続
きを繰り返していくと，２次曲線とｘ軸の交点を挟む２つの数はどんどん近づき，平方根の近似値が得られます。

3.1. 実数型データ

31

2 分法で任意の数 a の平方根を求める手続きは上の方法と同じです。２次曲線 f (x) = x2 − a を
０と a の間で探すことになります。
f(x) = x2 - a

a1
0

a2

x

-2

図 3.1: f (x) = x2 − 2 のグラフ。２分法によって解に無限に近づく。

0:/***303.c*********/
1:#include <stdio.h>2:#define MAX 1000/* 繰り返しの最大数 */3:4:#define
ACCURACY
int main(void){

5:
6:
7:

doubledoublea;a1,a2;

8:
9:

doubleint

tyu_ten;count;

10:
11:
12:

/**** 数値入力と初期化*****/
printf("Input data >>");

13:
14:
15:
16:
17:

scanf("%lf",
a1=0.0;a2=a;/* 近似の精度 */1.0e-10/*入力する数: この値の平方根を求める*//* 解をはさむ２点 ； a1<a2 とする*//* 中点 *//* カウンタ変数 */&a);/* 0.0 と a の間で解を探す。*//*****２分法の繰り返し *****/
for(count=0; count<MAX; count=count+1){

18:
19:
20:

tyu_ten=(a1+a2)/2.0;
if(tyu_ten*tyu_ten==a){break;

21:
22:}
if(tyu_ten*tyu_ten-a>0.0){

23:
24:
25:

a2=tyu_ten;else{
a1=tyu_ten;}}/* 解である場合 条件１*/

第3章

32
26:
27:
28:

実数型変数と標準関数

if( ((a2-a1)/a1<ACCURACY) && ((a2-a1)/a1>-ACCURACY) ){break; }/* 条件２ */

29:
30:

}

31:
32:
33:

printf("sqrt(%lf)=%lf \n", a, tyu_ten);exit(0);}34: /***********************/
2: 繰り返しの回数が最大 MAX 回で終了する。
3: 1.0 × 10−10 の精度で解を求めるように変数 ACCURACY を定義している。
23: 中点の値が f (tyu ten) > 0 なら a2 を中点にする。
25: 中点の値が f (tyu ten) < 0 なら a1 を中点にする。
27: a1 と a2 の差（絶対値）が ACCURACY=1.0 × 10−10 以下になった時，解とする。

3.2

標準関数

ｃ言語では標準関数として多くの数学関数を用意している。標準関数のマニュアルなどを参考にしてください。
代表的な標準関数例 (以下の x や y は double 型の実数）
平方根
指数関数
自然対数
常用対数

sqrt(x)exp(x)
log(x)

sin 関数log10(x)sin(x)

cos 関数tan 関数
階乗 (x の y 乗）

cos(x)
tan(x)
pow(x, y)

これらの標準関数を使う場合は，プリプロセッサ命令に#include <math.h>を取り込まなければいけない。また，プログラム中で標準関数を使うには，コンパイラーで標準関数をまとめたライブラリーにリンクしなければいけない。これは −lm というオプションをつけてcc コマンドを実行しなければいけない。
% cc sample.c -lm

または，% cc -o sample -lm sample.c

3.2. 標準関数

3.2.1

33

ｃ言語での関数の呼び出し

a. 引数を持たない関数
function( ) ;b. 引数がある場合
function(x,y) ;/* 関数名を書くだけ*//*カッコ内に引数を書く*/

c. 返り値を変数 (answer) に代入する場合
answer=function(x,y); /*function(x,y) の返り値はプログラムのなかで利用できる*/

3.2.2

標準関数による平方根を求める

0:1:/***304.c*********/#include <stdio.h>2:3:#include <math.h> /* math.h を呼び出す */

4:
5:
6:

int

doublea;7:
8:

doubleanswer;

9:
10:
11:

/**** 数値入力*****/
printf("Input data >>");
scanf("%lf", &a);

12:
13:

answer=sqrt(a);

14:
15:
16:

printf("sqrt(%lf)=%lf \n", a, answer);exit(0);}17:/***********************/

3.2.3

２次方程式の根

main(void){/*入力する数*//* 平方根*/２次方程式，ax2 + bx + c = 0 (ただし a ̸=0) の根を求めるプログラムを作りましょう。

0:1:/***305.c*********/#include <stdio.h>2:3:#include <math.h> /* math.h を呼び出す */

4:
5:
6:

int

main(void){

doublea,b,c;

/*係数/

第3章

34
7:
8:
9:

doubled,r,s,bunbo;
doublex,x1,x2;
printf(" a= "); scanf("%lf",&a);

10:
11:

printf(" b= "); scanf("%lf",&b);printf(" c= "); scanf("%lf",&c);

12:
13:
14:

d=pow(b,2.0)-4.0*a*c;
bunbo=2.0*a;

15:
16:

if(d>0){x1=(-b+sqrt(d))/bunbo;

17:
18:
19:
20:
21:

x2=(-b-sqrt(d))/bunbo;
printf("x1=%lf \n ", x1);
printf("x2=%lf \n ", x2);}
else if(d==0){

22:
23:
24:
25:
26:

x=(-b)/bunbo;
printf("x=%lf \n ", x);}else{
r=(-b)/bunbo;

27:
28:
29:

s=sqrt(-d)/bunbo;
printf("x1=%lf + %lf i \n ", r,s);
printf("x2=%lf - %lf i \n ", r,s);

30:
31:

}
exit(0);

32:
33:

}
/***********************/

3.2.4

sin と cos の表を作る

0:1:/***306.c*********/#include <stdio.h>

2:
3:
4:

#include <math.h>#defineMAX
20 /*繰り返しの回数*/#definePAI
3.1415926535/*円周率*/

5:
6:

int

7:
8:
9:

int count ;doubletheta;

main(void){/*カウンタ変数*/

実数型変数と標準関数

3.3. Ｃ言語のいろいろな演算子
10:
11:
12:

double35

sin_ans, cos_ans;/** ０から 2PAI の間を２０等分して各点での sin と cos を計算 **/

13:
14:

for(count=0; count<=MAX; count=count+1){
theta=2.0*PAI*(double)count/(double)MAX;

15:
16:
17:

sin_ans=sin(theta);
cos_ans=cos(theta);

18:
19:

printf("%lf (rad): %lf, \t %lf \n", theta, sin_ans, cos_ans);

21:
22:
23:

}
exit(0);}24:/***********************/

14: 変数 count と MAX をキャスト演算子によって型変換している。すべてを実数型変数として宣言してもよい。その場合，小数点を忘れないように。

3.3

Ｃ言語のいろいろな演算子

3.3.1

インクリメント演算子

インクリメント演算子は数値などの値を１だけ増やしたりする演算子です。

a. インクリメント演算子：++
count++ ;はcount=count+1 ;b. インクリメント演算子：-count--;

はcount=count-1;

と等価。
と等価。

前置きインクレメント演算子

count=2;
number=++count;/* count の値を１増やして，number に代入する。結果は count=3, number=3 となる。*/

後置きインクレメント演算子

count=2;
number=count++;/* count の値を number に代入する。その後，count の値を１増やす。結果は count=3, number=2となる。*/

第3章

36

3.3.2

入力の逆順に数値を表示する

0:/***307.c*********/

1:
2:
3:

#include <stdio.h>

4:
5:

int array[5];int count;

6:
7:
8:

for(count=0; count<5; count++){printf("Input No.%d >>", count);

int

9:
10:
11:

実数型変数と標準関数

main(void){/*カウンタ変数*/

scanf("%d",&array[count]);/* 整数型変数 */

}

12:
13:

for(count=4; count>=0; count--){printf("No. %d : %d \n", count, array[count]);

14:
15:
16:

}
exit(0);}17:/***********************/

3.3.3

複合代入演算子

複合代入演算子+=

使用方法

count+=5;

等価な文
count=count + 5;

-=
*=
/=

count-=5;
count*=5;count/=5;count=count - 5;
count=count * 5;
count=count / 5;%=

count%=5;count=count % 5

3.3.4

printf() 関数の書式指定/* １０進整数４桁で右寄せで表示する。 *//* １０進整数４桁で左寄せで表示する。 */"%4d""%-4d"

実数型の小数表示"%f"/* 実数型の小数表示；小数点以下６桁で表示 */表示例）
1.234560, 12345600.000000"%e"/* 浮動小数点表示；小数点以下６桁で表示 */表示例）
1.234560e+00, 1.234560e+8

3.4. 演習問題

37/* 有効桁数６桁で表示 */表示例）
1.23456, 1.23456e+8"%10.3f" /* １０は表示桁数で３は小数点以下桁数を示す
"%g"

*/

表示例）

"%10.3e"1.234,
123456.000/* １０桁で小数点以下３桁で表示 */表示例）

1.234e+00"%10.3g" /* １０桁で有効桁数３桁で表示
表示例）
1.23
1.23e+10

3.3.5

エスケープ文字エスケープ文字名称
バックスペース

\b
\f
\n

機能
カーソルを１文字左へ動かす

改頁

次の頁の先頭へ移動

改行

次の行へ移動

タブ

\t

3.4

*/

空白を挿入する

演習問題(ちょっと多いですが、出来るところまで頑張りましょう。）
1. 3.1.3 のプログラムで，答えが実数型の 4.5 となるようにプログラムを書き換えよ。
2. 3.1.5 のプログラムで精度（ACCURACY の値）をいろいろ変えて平方根を求めてみよう。また，次の節で示す標準関数を用いて求められる平方根の精度はどのくらいか調べよう。
3. 3.2.3 のプログラムで，a = 0 の場合も解を与えるようにプログラムを書き換えよ。
4.

変数 number は，double 型の実数で 1.2345e6 が格納されている。次の printf() 関数を実行した場合どのような出力が得られるか。

5.

(a)

printf( "%10.3lf",number);(b)printf("%10.3le", number);

実数型の 3× 3 の行列を２つ読み込んで、２つの行列の和と積を求めるプログラムを作りなさい。９個の行列要素には 1.1 から 1.9 までの実数を 0.1 ずつ増やして入れることにしましょう。

6. (a) a1 から an 個のｎ個の実数型データーの和
sum =

n
∑
i=1

ai ,

第3章

38

実数型変数と標準関数と平均値 (s = sum/n)、さらに分散、

1∑(ai − s)2 ,
n i=1
n

ss =

√
ss を求めるプログラムをつくりましょう。
n 個のデータとして、ai = i−2 , (i = 1, 2, . . . , n) を与えてください 1 。
と、標準偏差 sss =(b) n を増やしていくと和（sum) の値はどうなるか？
(c) 時間のある人は、
sum =

∞
∑
1
i=1

nk

,

(k=2,3,4,. . . , 10) の場合の関数の和も求めて見ましょう。
7.

「じゃんけん」するプログラムを作って下さい。（考え方）グー、チョキ、パーを１，２，３に対応させる（switch() 文などを使う）。見られないように二人が交互にキーボードから
１，２，３の内のどれかの数値を打ち込んで、その値を a , b とします。この数値を調べて、「a の勝ち」「b の勝ち」「引き分け」を出力するプログラムを作る。

1 自然数の逆数の冪の級数＝ツェーター関数と呼ばれている。

39

第 4 章 文字データの取り扱い

文字型データ

4.1

Ｃ言語では，整数型や実数型のデータとして数値が扱えるように，文字型のデータとして，文字を取り扱うことができる。文字型の宣言は char 型で，char 型で扱えるデータは１文字の英数字といくつかの記号です。

4.1.1

文字型変数の宣言

char moji;moji=’a’;
文字変数 moji に a を格納する。シングル・クォート’ a’ で文字を囲む。printf() や scanf() 関数で文字型データを入出力する場合は，%c を使う。

printf("%c", moji);scanf("%c", &moji);

文字の入出力プログラム

0:
1:/***401.c*********/#include <stdio.h>

2:
3:
4:

int

5:
6:

main(void){char moji;
printf("Input Character >>");

7:
8:
9:

scanf("%c",&moji);

printf("%c \n ", moji);

10:
11:

exit(0);}12:/***********************/

4.2

文字型と整数型

コンピュータでは文字を直接扱うかわりに，文字に番号（文字コードという）が付けられていて，その番号によって文字を表します。代表的な文字コードは ASCII コードや EUC コードなどがあ

第4章

40

文字データの取り扱い

る。char 型変数はー１２８から１２８の整数をつかって文字に対応させています。文字コードには整数を使いますから，文字型のデータは整数です。アスキー・コード表などで確認してください。

4.2.1

文字コード

以下は入力された文字コードを出力するプログラムです。

0:/***402.c*********/

1:
2:
3:

#include <stdio.h>
int

main(void){/* 入力される文字*//* 文字コード */4:5:char moji;
int c_code;

6:
7:
8:

printf("Input Character >>");
scanf("%c", &moji);

9:10:

c_code=(int)moji;

11:
12:
13:

printf("%c : %d \n ", moji, c_code);exit(0);}14:/***********************/

10: 文字型変数 moji の内容を int 型に変換して, int 型変数 c code に代入する。

4.2.2

getchar( ) と putchar( ) 関数

scanf() や printf() 関数でも文字型変数の入出力はできますが，文字型データの入出力には getchar() と putchar( ) 関数があります。両方とも返り値は int 型である。
文字データの入力

getchar();/* 引数はない */putchar(char moji) ; /* 引数は char 型 */文字データの出力
文字の入出力プログラム

0:
1:/***403.c*********/#include <stdio.h>

2:
3:

int

4:
5:
6:

main(void){char moji;/* 文字型変数の宣言 */printf("Input Character >>");

4.2. 文字型と整数型

41

7:
8:
9:

putchar(moji);

10:
10:

putchar(’\n’);exit(0);11:12:

moji=(char)getchar();/* 改行の出力 */}/***********************/

7: getchar() 関数の返り値をキャスト演算子で char 型に変換している。

4.2.3

判定プログラム

入力された英文字が大文字か小文字かを判定するプログラムを作りなさい。英文字以外が入力されたときは、エラーメッセージが表示されるようにしなさい。
（考え方）：ASCII コードでは英文字は順番に番号付けされています。大文字’A’ が６５番で’B’ が６６番、順番にいって、’z’ が９０番、小文字は’a’ の９７番から始まって、’z’ は１２２番で終わり
ます。

0:
1:

/***404.c*********/#include <stdio.h>

2:
3:
4:

int main(void){char moji;/* 文字型変数の宣言 */5:
6:

printf("Input Character >>");

7:
8:
9:

moji=(char)getchar();if(moji>=’A’

10:
11:

printf("%c : Large \n", moji); }else if(moji>=’a’ && moji<=’z’){

12:
13:
14:

printf("%c : Small \n", moji); }&& moji<=’Z’){

else{
printf("%c : Not Alphabet \n", moji); }

15:
16:

exit(0);

17:
18:

}

4.2.4

文字型データの算術演算/***********************/

英文字の大文字と小文字の変換を行うプログラムを作りましょう。
（考え方）：ASCII コード表によれば、大文字と小文字のアルファベット順に並んでいて、大文字

第4章

42

文字データの取り扱いと小文字の差は３２です。英小文字の ASCII コードから３２を引くと大文字になります。つまり英小文字の文字コードから’a’-’A’ を引くと英大文字のコードになります。

0:/***405.c*********/

1:
2:#include <stdio.h>

3:
4:
5:

int main(void){char moji;/* 文字型変数の宣言 */6:
7:

printf("Input Character >>");
moji=(char)getchar();

8:
9:
10:

if(moji>=’A’ && moji<=’Z’){
moji += (’a’-’A’);
}

11:
12:

else if(moji>=’a’ && moji<=’z’){
moji -= (’a’-’A’);
}

13:
14:
15:

putchar(moji);putchar(’\n’);

16:
17:

/* 改行*/

exit(0);}18:/***********************/

4.3

文字列の扱い1 文字ではなく、単語のように文字が並んだデータを文字列と呼ぶ。文字列は、文字型の配列によって取り扱います。

4.3.1

文字型配列の宣言

charstring[5];char: 配列のデータ型、配列名 string,

4.3.2

要素数 5 の定義。

文字列定数

文字列のデータをプログラム中に定数として書き込むために、文字列定数を用います。文字列定
数は文字の並びをダブル・クォート’\0’で囲んで表します。ただし、文字の最後に自動的に

4.3. 文字列の扱い

43（ヌル記号）が代入されます。これは、文字の終わりを示します。

"cat"=======> ’c’ ’a’ ’t’ ’\0’

ヌル記号も配列に格納されるので、要素数は文字数より１以上多い値を定義しなければいけない。
長い文字列定数を複数行にわけて書く場合は改行の前にバック・スラッシュ「￥」を置く。

4.3.3

文字列の入出力
string[0]

[1]

[2]

[3]

'c'

'a'

't'

'\0'

[4]

[5]

[6]

図 4.1: 配列 string[i] への文字列の格納。

printf() や scanf() 関数による文字列の入出力は、書式文字”％ s”を使う。
char string[100];/* １００個の配列要素をもつ文字列 string の宣言 */scanf("%s", string);
printf("%s", string);
第二引数に「＆」はいらない。

0:
1:
2:

/***406.c*********/#include <stdio.h>

3:
4:

int main(void){char string[100];/* 文字列の宣言 */

5:
6:
7:

printf("Input string >>");
scanf("%s", string);

8:
9:

printf("%s \n", string);

10:
11:
12:

}

13:

/***********************/

4.3.4

文字の長さを答えるプログラム

0:/***407.c*********/

exit(0);

第4章

44
1:
2:
3:

文字データの取り扱い#include <stdio.h>
int

main(void){/* 文字列の宣言 *//* 文字列の長さ */4:5:char string[100];
int s_length;

6:
7:
8:

printf("Input string >>");
scanf("%s", string);

8:9:

for(s_length=0; string[s_length] != ’\0’; s_length++){

10:
11:
12:

;
}
printf("%d \n",s_length);exit(0);

13:
14:

}
/***********************/

9: s length の値をゼロから１ずつ増やしながら string[s lenght] の内容を繰り返し、ヌル記号であるとき for() 文から抜ける。
10: 何もない空文

4.3.5

文字列のコピー

文字配列 a[ ] に格納されている文字列を、文字配列 b[ ] にコピーするプログラムを考えよう。

0:1:/***408.c*********/#include <stdio.h>

2:
3:
4:

int main(void){char a[100];/* 入力される文字列の宣言 */5:5:char b[100];
int count;/* コピー先の文字列の宣言 *//* カウンタ変数 */

6:
7:
8:

printf("Input string >>");
scanf("%s", a);8:9:for(count=0; a[count] != ’\0’; count++){

10:
11:
12:
13:
14:

b[count]=a[count] ;}b[count]=’\0’;
printf("input : %s \n", a);4.3. 文字列の扱い

45

15:
16:
17:

printf("copy : %s \n", b);exit(0);}18:/***********************/

12: 文字列の終了を示すヌル記号を入れる。

4.3.6

文字列の接続入力された２つの文字列をつなぐプログラムを作りましょう。
（考え方）：文字列 b[ ] に格納された文字列に文字列 a[ ] に格納された文字列を接続する場合を考えましょう。b[ ] に格納された文字列の終端を見つけ、その後ろに文字列のコピーと同様な処理を用いて、コピーしていく。

0:
1:
2:

/***409.c*********/#include <stdio.h>

3:
4:

int main(void){char a[100];/* 入力される文字列の宣言 */

5:
5:
6:

char b[100];/* コピー先の文字列の宣言 */int a_count, b_count;/* カウンタ変数 */7:8:

printf("Input string 1 >>");
scanf("%s", a);

9:
10:
11:

printf("Input string 2 >>");
scanf("%s", b);/*** 文字列の終端を探す***/

12:
10:

for(b_count=0; b[b_count] != ’\0’; b_count++){
;
}

11:
12:
13:

/***文字列のコピー *****/
for(a_count=0; a[a_count] != ’\0’; a_count++){

13:
14:

b[b_count+a_count]=a[a_count];
}

15:
16:
17:

b[b_count+a_count]=’\0’;
printf("string : %s \n", a);printf("copy : %s \n", b);

18:
19:

exit(0);}20:/***********************/

第4章

46

文字データの取り扱い

２次元の文字型配列

4.4

文字型の配列でも２次元の配列を用いることが出来る。

4.4.1

２次元文字型配列の宣言

charstring[3][5];char string[3][5][0][0][0][1][0][2][0][3]

'a'

'b'

'c'

' \0 '[0][4][1][0][1][1][1][2][1][3][1][4]

'y'

'a'

'm'

'a'

' \0 '[2][0][2][1][2][2][2][3][2][4]

'k'

'a'

'w'

'a'

' \0 '

図 4.2: 文字型の２次元配列

string[0] は文字列 ’abc’ を、string[1] は文字列 ’yama’ を、string[2] は文字列 ’kawa’ を表す。printf(”％ s ￥n”, string[0]) は第０番目の文字列を表す。
入力された整数に対応する英単語を答えるプログラムを考えましょう。
（考え方）：”one”, ”two”, ”three”をあらかじめ配列 name[][] に格納しておく。name[0], name[1],name[2] によって文字列を呼び出す。
0:
1:/***410.c*********/#include <stdio.h>

2:
3:

int

main(void){

4:
5:
6:

char
int

7:
8:

printf("Input number (1 or 2 or 3) >>");
scanf("%d", &number);

9:
10:
11:

printf("%s \n", name[number-1]);
exit(0);12:13:

name[3][6]={"one", "two", "three"};number;/* 入力される数 *//* 英語の格納 */}/***********************/

4: 英単語をあらかじめ、初期化付き宣言で２次元文字配列 name[][] の中に格納しておく。
10: number-1 は０行目から２行目までの文字列に対応している。

4.5. 演習問題

4.5

47

演習問題

1. 4.2.1 のプログラムを使って，いろいろな文字コードを調べてみよう。
2. 4.2.1 のプログラムをヒントに、a から z までと、A から Z までの文字と文字コードを出力する
プログラムを書いてください。文字は文字型変数で扱えるので、for(c code=’a’ ; c code⟨=’z’;c code++) を使える。

3. 4.4.1 のプログラムを参考にして、１から１０までの数に対応する英語を答えるプログラムを作りなさい。またこの範囲外の入力があった場合に、何らかのエラー処理を行うようにしな
さい。
4. 文字コードのプログラムを参考に, ０から１２７までの数字を入力した時に，その数字（文字コード）に対する文字を出力するプログラムを作ってください。
5. ２次元文字型配列のプログラムを参考にして、日曜から土曜までに対応する英語を答えるプ
ログラムを作りなさい。
6. 入力されたでたらめな文字列を、ABCD 順で出力するプログラムを作ってください。
7. 文字列 a と文字列 b を繋いだものを文字列c に入れるプログラムを作ってください。

49

第 5 章 ポインタの取り扱い

ポインタ (番地）

5.1

Ｃ言語では，データは変数に格納されます。変数はメモリ上に置かれています。メモリ上で変数が置かれている番地を示しているのがポインタです。ポインタは変数のメモリ上の番地（アドレス）を示します（整数で表された番号）。ポインタを格納する変数をポインタ変数とよぶ。
int number=123;
address

100

105

106

(1 byte)

data

123

variablenumber

図 5.1: int 型の 4 バイトのデータが 100 番地に格納されている。変数 number の先頭番地が 100 である。番地はコンピューターシステムが自動的に決める。

5.1.1

ポインタ変数の宣言

100 番地（アドレス）という数字のかわりに、番地に自分の好きな名前（たとえば、int ptr) を付ける事が出来る。
int/* int 型のデータを示すポインタ変数 int_ptr の宣言を示す。**/*int_ptr;

int *int ptr; の意味は, アドレス (int ptr) に入っている数値は int 型の整数であることを示す。
＊（アステリスク）は int ptr がポインタ変数であることを示す。

5.1.2

ポインタ演算子

演算子名演算子

アドレス演算子＆ （アンパサンド）

ポインタ演算子＊（アステリスク）

使用例

int_ptr=&number;number=*int_ptr;

第 5 章 ポインタの取り扱い

50

変数 number にアドレス演算子 (&) が適用されると、変数の置かれたアドレス（番地）が得られる。また、ポインタ変数 int ptr にポインタ演算子 (*) が適用されると、ポインタ変数に格納されているデーターが得られる。
実際にポインタがどんな値になっているか表示して見ましょう。

0:/***501.c*********/

1:
2:
3:

#include <stdio.h>

4:
5:

int
n=7;
double h=20.0;

6:
7:
9:

double10:
11:

printf(" &n= %d \n", &h);printf(" &n= %d \n", &k);

12:
13:
14:

int

main(void){

k=1.23;printf(" &n= %d \n", &n);exit(0);}/***********************/

以下は、アドレス演算子と ポインタ演算子の使用例を示します。int val は宣言しただけで何も代入していないが、結果は２を表示するのがおもしろい。

0:1:/***502.c*********/#include <stdio.h>

2:
3:
4:

int main(void){
int
int_val;/* int 型の変数 */

5:
6:

int

/* int 型のポインタ変数 */

7:
8:
9:

int_ptr=&int_val;*int_ptr=2;

10:
11:

printf(" int_val= %d \n", int_val);printf(" *int_ptr= %d \n", *int_ptr);

18:
19:
20:

*int_ptr;

exit(0);}/***********************/

7: 変数 int val のアドレスをポインタ変数 int prt に代入している。
8: ポインタ演算を行ったポインタ変数 int ptr に２を代入している。
int val は宣言しただけで何も代入していないが、結果は２を表示する。

5.1. ポインタ (番地）51

ポインタを使った四則演算

5.1.3

ポインタを使って２＋３＝５の計算をやってみましょう。

0:
1:
2:

/***503.c*********/#include <stdio.h>

3:
4:

int main(void){int*a, *b, *c;/* int 型のポインタ変数 */

5:
6:
7:

int

/* int 型の変数 */8:9:*a=2; *b=3;*c=*a+*b;

10:
11:
12:

printf(" *a=%d, *b=%d, *c=%d \n", *a, *b, *c);exit(0);ia, ib, ic;

a=&ia;

b=&ib;

13:
14:

}

5.1.4

配列とポインタ

c=&ic;/* 番地の格納 *//***********************/

ポインタは、メモリ上の変数を指し示すことが出来るだけでなく、配列の要素を指し示す事も出来る。ｃ言語では、「配列名は配列の先頭の要素のアドレスを表す」という約束になっています。
つまり、

int

array[5]の array は配列の最初のアドレス（番地）を示しているのです。例えば int 型変数は４バイトなので、array が１００番地、array[1] は１０４番地、array[2] は１０ 8 番地の様になります。以下のプログラムで確かめて見ましょう。

0:1:/***504.c*********/#include <stdio.h>

2:
3:
4:

int main(void){
int a[5]; /* int 型の配列 */5:
6:

printf(" a= %d \n", a);

7:
8:
9:

printf(" &a[0]= %d \n", &a[0]);printf(" &a[1]= %d \n", &a[1]);printf(" &a[2]= %d \n", &a[2]);第 5 章 ポインタの取り扱い

52
10:
11:
12:
13:
14:

printf(" &a[3]= %d \n", &a[3]);printf(" &a[4]= %d \n", &a[4]);exit(0);}15:/***********************/
int array[3]int *int_ptr

address

100

104

108

112

[0]

[1]

[2]

int_ptr

116

120

data

図 5.2: 配列とポインター

ポインタと配列の関係を以下のプログラムで確認してみましょう。図 5.2 を参照。

0:/***505.c*********/

1:
2:
3:

#include <stdio.h>
int

main(void){/* int 型の配列 *//* int 型のポインタ変数 */

4:
5:

int
int

array[3];*int_ptr;

6:
7:
8:

int

count;

int_ptr=array;9:10:for(count=0; count<3; count++){

11:
12:
13

/*

カウンター変数 *//* ポインタ int_ptr は１００番地を示すようになる*/*int_ptr=count;
int_ptr++;/*１００番地から順番にデータ（count）か格納される*/

}

14:
15:

for(count=0; count<3; count++){printf(" array[%d]=%d \n", count, array[count]);

16:
17:
18:

}
exit(0);}19:/***********************/

8: ポインタ変数 int ptr には配列 array[ ] の先頭要素 [0] のアドレスが代入される。
11: ポインタ変数を用いて数値を代入する。array[0] に count が代入される。
12: ポインタ変数にインクリメント演算子を適用すると、次の配列要素 [1] を指し示す。

5.2. ポインタによる文字列の取り扱い

5.2

53

ポインタによる文字列の取り扱い

ポインタの得意な技として、文字列の処理があある。

5.2.1

文字列のコピー

4.3.5 節で取り扱った文字列のコピーをポインタを使って作りましょう。（配列の要素を直接操作するかわりに、ポインタ変数に格納されたポインタを用いて、配列の要素を操作する。）

0:
1:
2:

/***506.c*********/#include <stdio.h>

3:
4:

int main(void){char a_str[100];/* 入力される文字列 */b_str[100];/* コピー先の文字列*/*a_ptr, *b_ptr;/* char 型のポインタ変数の定義 */

5:
6:
7:

char
char

8:
9:

a_ptr=a_str;
b_ptr=b_str;

10:
11:
12:

printf("Input String >> ");scanf("%s", a_ptr);

13:
14:

for(

/* 文字配列の先頭アドレス */; *a_ptr != ’\0’ ; a_ptr++, b_ptr++){*b_ptr=*a_ptr; }

15:
16
17:

*b_ptr=’\0’;

18:
19:

printf(" Copy : %s \n", b_str);

20:
21:
22:

printf(" Input : %s \n", a_str);

exit(0);}/***********************/

8, 9: ２つのポインタに２つの char 型配列の先頭アドレスを代入している。ポインタ変数 a ptr とb ptr はそれぞれ、a str[0] と b str[0] を示すことになる。
11: scanf() 関数の引数として、ポインタ変数が配列名と同じようにつかえる。
13: for 文の第１フィールドには何も書かれていない。第２フィールドの繰り返し条件は a ptr のポインターが指し示すデータが「ヌル ￥０ 」文字でないことである。
14: ポインタのコピー

第 5 章 ポインタの取り扱い

54

5.2.2

文字列の分割入力された氏名を名前と名字に分割するプログラムを作りましょう。ただし名前を代入するときには「Ichiro/Suzuki」の様に、名前と名字の間にスラッシュ「/」を入れるようにする。
（考え方）：char 型配列 name[ ] と２つのポインタ変数 a ptr, b ptr を用意する。配列 name[ ] に名前を格納する。
「/」の位置に「ヌル’￥０’」を書き込めば、文字列は２つに分かれます。あとは、文字列の開始をポインタで指定すればよい。
a_ptr

b_ptr

100 101102

103 104 105 106 107 108 109 110 111 112 113

s

u

z

u

name[0][1]

Address

k

i

/

i

c

h

i

r

o

\0

[12]
\0

図 5.3: 文字列の分割

0:
1:/***507.c*********/#include <stdio.h>

2:
3:

int

main(void){
name[100];/* 氏名の配列 */*a_ptr, *b_ptr;/* char 型のポインタ変数の定義 */

4:
5:
7:

char
char

8:
9:

printf("Input First name/Family name >> ");scanf("%s", name);

10:
11:
12:

a_ptr=name;b_ptr=name;

13:
14:

while( *b_ptr != ’/’b_ptr++;

15:
15:
16

if(*b_ptr == ’/’){*b_ptr= ’\0’ ;b_ptr++;

16
17:

}

*b_ptr != ’\0’){printf(" First Name : %s \n", a_ptr);

18:
19:
20:
21:
22:

&&
}

printf(" Familly Name : %s \n", b_ptr);
exit(0);}/***********************/

5.2. ポインタによる文字列の取り扱い

55

15: 氏名を区切る’/’ が見つかると、その文字を文字列の終端をしめす、「ヌル」 ’￥０’ に置き換える。この時点で、文字列 name [] の終端は’/’ が格納されていた番地で終り、分割される。
17: printf() 関数の引数として文字列の開始アドレスを格納したポインタ変数を与えても、文字列を表示できる。

配列とポインタ変数の違い

5.2.3

配列の宣言、

char

array[5];array[0]=’a’;
では、配列のデータ型と要素数に合わせてデータの格納場所が確保されます。しかしポインタ変数
の宣言

char

*point;*point=’a’;
では、ポインタの値（アドレス）の格納場所が確保されるだけで、データの格納場所は確保されま
せん。

5.2.4

見掛け上のコピー

以下のプログラムは、ポインタ変数 a ptr から b ptr へ、ポインタの値（アドレス）がコピーされ、両者が同じ文字列を示すようにしただけです。文字列データ自体がコピーされたプログラムではない。
a_ptr = b_ptr
100 101102

103 104 105 106

s

u

z

u

string[0][1]Addresski\0

図 5.4: プログラムの動作

0:/***508.c*********/

1:
2:
3:

#include <stdio.h>

4:
5:

int

main(void){

char
char

string[100];/* 入力される配列 */*a_ptr, *b_ptr ;/* char 型のポインタ変数 */第 5 章 ポインタの取り扱い

56
7:
8:
9:

a_ptr=string;
printf("Input String >> ");

10:
11:

scanf("%s", a_ptr);

12:
13:
14:

b_ptr=a_ptr;
printf(" Input : %s \n", a_ptr);

15:
16:
17:
18:
19:

printf(" copy : %s \n", b_ptr);
exit(0);}/***********************/

8: string の先頭アドレスをポインタ変数 a ptr に代入する。
10: scanf() 関数で a ptr に文字列を代入する。
12: b ptr に a ptr の内容を代入する。
14: printf() 関数で表示。

5.2.5

２つの文字配列を接続する

ここでは、２つの文字列たとえば、{a,b,c,d} と {a,b,c,d} を接続して、{a,b,c,d,a,b,c,d} と出力するプログラムをポインタを使って作ってみよう。
a_ptr
char a[ ]

a

b

c

d

\0

[0]

[1]

[2]

[3]

[4]

a

b

c

d

\0

[0]

[1]

[2]

[3]

[4]

b_ptr
char b[ ]図 5.5: 文字配列の接続とポインタ操作

0:
1:/***509.c*********/#include <stdio.h>

2:
3:
4:

#define

5:
6:

char
char

int

MAX 100

main(void){a[MAX];b[MAX];

5.3. 演習問題

57/*

char 型のポインタ変数の定義 */

7:
8:
9:

char
*a_ptr, *b_ptr;
int i, a_count;
/*

10:
11:

a_ptr=a;b_ptr=b;

12:
13:
14:

printf("Input String 1>> ");scanf("%s", a_ptr);
printf("Input String 2>> ");

15:
16:

scanf("%s", b_ptr);

17:
18:
19:

for( ; *a_ptr != ’\0’; a_ptr++){/* 文字配列 a の終端’\0’ を探す */
}

20:
21:

*a_ptr=*b_ptr;
for(i=1;i<MAX; i++){

22:
23:
24:

*(a_ptr+i)=*(b_ptr+i); /* 配列 b を配列 a に代入する*/
if(*(b_ptr+i)==’\0’){ /* 文字配列 b の終端’\0’ で終了する */break;

カウンター変数 *//* 文字配列の先頭アドレス *//* 文字配列の先頭アドレス *//* a の終端に配列 b の先頭を代入する */

25:
26:

}
}

27:
28:
29:

printf(" Connect : %s \n", a);exit(0);}30:/*******************/

5.3

演習問題

1. 5.1.3 のプログラムを参考にして、ポインタを使って四則演算を行うプログラムを作りなさい。，
2. 5.1.4 のプログラムを参考にして、int 型、float 型、double 型の配列 a[5] の番地を表示して下さい。

3. int 型の配列 array[5]={1,2,3,4,5} に対して、array[i] と *(array+i) を出力するプログラムを作り両者を比べなさい。何がわかるか？
4. 5.2.5 のプログラムを参考にして、接続後に２つの文字列の間に’/’(スラッシュ）を入れるよ
うにプログラムを改良してください。
5. キーボードから文字を１文字ずつ入力し出力するプログラムを，配列とポインタを使って作ってください。ただし，実行結果例が以下のようになるようにしてください。（実行結果例の

cat などの文字や文字数は何でもよい）第 5 章 ポインタの取り扱い

58%./a.out
１文字入力してください>>c
a[0]=c１文字入力してください>>aa[1]=a１文字入力してください>>t

a[2]=t
文字列 a=cat%
6. 509.c を参考に，3 つの文字配列を接続するプログラムを作ってください。
7. 名字と名前を入力し，名字 名前の順と名前 名字の順で出力するプログラムをポインタを使って作ってください。ただし，名字と名前の間にスペースをアスキー文字コードを使って１個入れるようにしてください。

（スペースのアスキー文字コードは３２です。）

59

第 6 章 ファイルの入出力
これまで用いてきたプログラムは、入力や出力はキーボードやディスプレーを用いたものでした。しかし、コンピューターのハードディスクに直接データを保存したり、プログラム上に読み込んだりする場合もあるでしょう。ここではファイルについて学びましょう。

6.1

ファイルの基本操作

6.1.1

ファイル・ポインタ

様々なデータをテキストファイルとして保存しておくと、読んだり、変更したり出来て便利です。ファイルの操作は FILE 型のポインタ変数（ファイル・ポインタ）を用いて行われる。

/** ファイル・ポインタの宣言 ****/FILE *inp_file;
fopen() 関数：ファイルを開く標準関数、fgetc() や fputc() はファイルを読んだり書いたりするための標準関数、fclose() 関数は、ファイルを閉じる。

6.1.2

ファイルのオープン

ファイルを開くには fopen() 関数を使う。使い方は、

FILE

*fp;

/* ファイル・ポインタ*/

fp=fopen(t_file, "r");第一引数”t file”は開きたいファイルの名前、第２引数”r”はファイルを読むためのもの。書き込む
時は”w”を使う。fp という名前の FILE 型ポインタ変数に fopen() 関数の返り値（ファイル・ポインタ）を代入する。

6.1.3

ファイルのクローズ

ファイルを閉じるには、fclose() 関数を使う。fclose() 関数は引数として閉じたいファイルのファ
イル・ポインタを使う。

FILE

*fp;

/* ファイル・ポインタ

int flag;flag=fclose(fp);*/第 6 章 ファイルの入出力

60

fclose() 関数は、戻り値として、成功すれば（０）を失敗すれば（１）を返す。ファイルを開いたら必ず fclose() 関数でファイルを閉じるようにする。バッファド I/O 方式 1 。

6.1.4

ファイルの読み出し

fgetc() 関数：ファイルから一文字の文字の読み出しを行う標準関数。fgetc() 関数は、呼び出される度に、ファイルの先頭から順に１文字ずつ文字を読み出します。fgetc() 関数の引数は、読みたいファイルのファイル・ポインタを指定する。返り値は読み出された文字か EOF を返す。

int moji;
FILE *fp;
moji=fgetc(fp);
何らかの理由で文字が読み出せない場合、fgetc() 関数は EOF の値を返します。EOF はファイルの終わり (End of File) を意味する。一般には（-１）という int 型の値を持っています。このような事情で、fgetc() 関数は int 型の変数を返す様に定義されている。

6.1.5

文字数を数えるファイルに含まれる、文字数を数えるプログラムを作りましょう 2 。
考え方：fgetc() 関数でファイルを読んでいって、返り値が EOF になるまでの回数を数えればよい。

0:
1:
2:

/***601.c*********/#include <stdio.h>#define FOREVER 1

3:
4:

int

5:
6:
7:

char f_name[100];
FILE *inp_file;
int count;/* ファイル名 *//* ファイル・ポインタ *//* 文字数を数える */8:9:int eof_flag;/*読み込んだ文字を代入するところ */main(void){

10:
11:
12:

printf("Input file name:");scanf("%s", f_name);
inp_file=fopen(f_name, "r");

13:
14:

if(inp_file==NULL){

15: printf("%s does not exist \n", f_name);
1 入出力が行われるたびに実際にファイルへ出力するのではなく、バッファと呼ばれる一時記憶領域にデータを貯めておく
2 このプログラムを動かすには、あらかじめ、適当な名前のファイルを作りその中に文字を書き入れておく必要がある。
数字を適当に並べたファイルを作りましょう。ファイル名は file1 としておきましょう。

6.1. ファイルの基本操作
16:
17:
18:

exit(0);}count=0;19:20:while(FOREVER){/* 文字の終点を探す */eof_flag=fgetc(inp_file);

21:
22:
23:

if(eof_flag==EOF){
break;
}

24:
25:

}

26:
27:
28:

fclose(inp_file);
printf("%s: %d chars \n", f_name,count);
exit(0);count++;

61/*文字数を数える*/

29: }
30:
/*******************/

11: scanf() 関数でファイル名を読み込む
12: 読み出しモード”r”でファイルを開く
14: ファイル・ポインタが NULL（ファイルが存在しないときは NULL を返す）の時、ファイルのオープンに失敗したので exit(0) 関数でプログラムを終了する。
20: fgetc() 関数で文字を読み込んで、その文字を eof flag に代入する。
21: eof flag が EOF の時終了する。

6.1.6

ファイルの書き込みファイルへ１文字の書き込みを行うには、fputc() 関数を使う。fputc() 関数は呼び出される度に、ファイルの末尾に順に１文字ずつ書き込んでゆく。

int flag;char moji;
FILE *fp;
flag=fputc(moji, fp);/* moji: 書き込みたい文字、fp: 書き込みたいファイルのファイル・ポインタ*/fputc() 関数は帰り値として、int 型を返す。

6.1.7

ファイルをコピーファイルの内容をコピーするプログラムを作りましょう。
考え方：6-1.c のプログラムと同様にファイルから文字を読み出して EOF を探す。ただし、繰り返

第 6 章 ファイルの入出力

62

しの中で、読み出した文字をそのまま別のファイルに書き込めばよい 3 。

0:/***602.c*********/

1:
2:#include <stdio.h>#define FOREVER 1

3:
4:
5:

int main(void){char in_name[100];6:
7:char out_name[100];
FILE *in_file;/* 出力ファイル名 *//* ファイル・ポインタ */

8:
9:
10:

FILE *out_file;
int moji;/* ファイル・ポインタ *//* 読み込んだ文字 */

11:
12:
12:

printf("Input file name:"); /*入力ファイル名 (file1) を入力 */scanf("%s", in_name);
in_file=fopen(in_name, "r"); /*入力ファイルのオープン */

13:
14:

if(in_file==NULL){

15:
16:
17:

printf("%s does not exist \n", in_name);exit(0);
}

18:
19:

printf("Output file name:");

20:
21:
22:
23:
24:

26:
27:

/*出力ファイルのオープン,"w"で書き込む */if(out_file==NULL){
printf("%s does not exist \n", out_name);exit(0);}while(FOREVER){/* 文字の終点を探す */moji=fgetc(in_file);

21:
22:
23:
24:
25:

/*出力ファイル名 (file2) を入力 */scanf("%s", out_name);
out_file=fopen(out_name, "w");

25:
26:
27:
19:
20:

/* 入力ファイル名 */if(moji==EOF){
break;}
fputc((char)moji, out_file);/*文字 moji を書き込む*/}
fclose(in_file);
fclose(out_file);

3 入力ファイル名には 6-1.c で用いたファイル名を使うとよい。出力ファイル名は file2 としておきましょう。

6.2. データの書式付き入出力
28:
29:
30:

63

exit(0);}/*******************/

21: EOF は int 型の値を返すので、moji は int 型変数として定義してある。
24: 文字 moji を out file へ 1 文字ずつ書き込む。

データの書式付き入出力

6.2

前の章では１文字の入出力を行うための関数を扱いました。１文字だけでなく、様々なデータの入出力を行うために、fscanf() と fprintf() 関数があります。書式は以下のようになります。printf()
や scanf() 関数の引数に、ファイル・ポインタ (in file や out file) が付け加わっただけです。

flag=fscanf(in_file, "%d," &val);
flag=fprintf(out_file, "%d", val);
fscanf() 関数の返り値は、int 型で、ファイルが終了して読み込むデータが無い場合は EOF となる。
fprintf() 関数の返り値は、通常はファイルの書き込まれた文字数です。しかしファイルへの書き込
みを失敗すると EOF を返します。

6.2.1

ファイルに書き込むプログラム

sin 関数と cos 関数の０から 2π まで π/10 刻みで関数表を作り、ファイルに書き込むプログラムを示します。4
0:/***603.c*********/

1:
2:#include <stdio.h>#include <math.h>

3:
4:
5:

#define#define

6:
7:

int

MAX
PAI

20
3.1415926535

main(void){

8:
9:
10:

int count;/* カウンタ変数 */doubletheta;/*
角度 */doublesin_ans, cos_ans;/* sin,cos の値 */

11:
12:

char
FILE

out_name[100];*out_file;/* 出力ファイル名 *//* 出力ファイル・ポインタ */4 実行した結果をグラフにしてみよう。％ gnuplot を使って、gnuplot ＞ plot ”data1” using 1:2 with lines,￥＞”data1” using 1:3 with lines

第 6 章 ファイルの入出力

64
13:
14:
15:

/* 出力ファイル名の入力 */printf("Output file name: ");scanf("%s", out_name);

16:
17:

out_file=fopen(out_name, "w");/*出力ファイルのオープン */

18:
19:
20:

if(out_file ==NULL){
printf("file open error <%s> \n", out_name);exit(0);

21:
22:

}

23:
24:
25:

for(count=0; count<=MAX; count++){theta=2.0*PAI*(double)count/(double)MAX;
sin_ans=sin(theta);

26:
27:

cos_ans=cos(theta);

28:
29:
30:

fprintf(out_file, "%lf \t%lf \t %lf \n",theta, sin_ans, cos_ans);

}

31:
32:

fclose(out_file);exit(0);

33:
34:

}
/****************************/

14: 出力ファイル名は”data1”としておきましょう。
18: オープン失敗の場合は終了。
28: ”書式に注意”

6.2.2

ファイルを読み込んで表示する。

テキスト・ファイルに書かれた整数のデータを全て加算して、その結果をディスプレーに表示するプログラムを作りましょう。(このプログラムを実行させるあたって、入力ファイルには１行に１文字ずつの整数データがテキストファイルですでに書かれている必要がある。演習問題２でファイルを作りましょう。）

0:
1:
2:

/***604.c*********/#include <stdio.h>#define FOREVER 1

3:
4:

int main(void){

5:
6:char in_name[100];
FILE *in_file;/* 入力ファイル名 *//*入力ファイル・ポインタ*/6.3. 演習問題

65/* 読み出した整数 *//* EOF のチェック *//*読込んだ整数の総和 */

7:
8:
9:

int number;
int flag;int total;

10:
11:

printf("Input file name:");

12:
13:
14:
15:
16:

scanf("%s", in_name);
in_file=fopen(in_name,"r");/* 入力ファイル名の入力 *//*入力ファイルのオープン */if(in_file==NULL){/* オープン失敗の場合 */printf("%s does not exist. \n", in_name);

17:
18:
19:

exit(0);
}

20:
21:

total=0;
while(FOREVER){/* 初期値 */

22:
23:
24:

flag=fscanf(in_file, "%d", &number);if(flag==EOF){
break;

25:
26:}
total += number;

27:
28:
29:

}
fclose(in_file);
printf("total=%d \n", total);

30:
31:

exit(0);
}

32:

6.3

/* 整数の和を求める *//****************************/

演習問題

1. 6.1.5 のプログラム 601.c を参考にして、テキスト・ファイル中での行数を数えるプログラムを作りなさい。テキスト・ファイル中では、行末には改行文字（’￥n’) があることを利用してください。

2. 1 から１００までの int 型整数を１行に１つずつ書き込むプログラムを作ってください。ただしファイルの名前は”data2”としていきましょう。604.c のプログラムの実行の時につかってみよう。

3. y = (x − 1)2 − 3 を x の値をー１から３の間で 0.01 ずつ計算し，ｘと y の結果を２列でファ
イル data3 に書き込んで下さい。さらに gnuplot を用いてグラフ表示してください。

4. ファイル data2 を読み込んで，各データに１を加えて，その結果を同じファイル data2 に第 6 章 ファイルの入出力

66

書き込むプログラムを作りましょう。（実行結果を cat コマンドや less コマンドで見てみましょう。）

5. ファイル data2 を読み込んで，各データを２乗して，その結果をファイル data4 に書き込むプログラムを作りましょう。

6. プログラム 602.c を参考にして、２つのテキスト・ファイルの内容をつないで、別のファイ
ルに書き込むプログラムを作ってください。

67

第 7 章 関数の作り方
ｃ言語では一連の手続きを関数という形でまとめておきます。関数の形にまとめたた手続きは、その関数を呼び出すことで実行されます。すでに、printf() 関数や、sin() 関数などの標準関数などを利用してきました。main() も関数です。printf() 関数は出力の手続きをまとめた関数で、これを呼び出すことでデータの出力が行えます。便利な関数を自分で作っていきましょう。

関数

7.1

関数の基本的な構造は以下の様になります。

/********************
function() 関数 の定義*********************/
function() {関数のプログラム

return;}関数の行う手続きは return 文によって終了します。外見上は main() 関数を書いたときとまったく同じ文法に従って、プログラムを書けばよい。任意の関数は main() 関数から呼び出して使うことが出来ます。

7.1.1

引数を持たない関数

関数呼び出しの様子を理解するために、簡単なプログラムで考えていきましょう。以下のプログ
ラムは printf() 関数を用いて、コメントを出力するだけですが、main() 関数から function() 関数
を呼び出して、実行していることがわかります。

0:1:/***701.c*********/#include <stdio.h>

2:
3:
4:

int

5:
6:

main(void){
printf("Main >>1 \n");printf("Main >>2 \n");function();/*function() 関数の呼び出し*/

第 7 章 関数の作り方

68
7:
8:
9:

printf("Main >>3 \n");exit(0);
}

10:
11:

/*******************/
function(){

12:
13:
14:

printf("Function >>1 \n");printf("Function >>2 \n");return;15:16:}/***********************/

7.1.2

引数を持つ関数

function() 関数に２つの整数型引数 a , b を与える例。引数は何個でも定義できる。
function(int a,関数のプログラム

int

b) {

return;}main() 関数から function() 関数を呼び出す時，引数のデータ型が一致していなくてはいけない。
以下の例で考えてみよう。main() 関数から test fn() 関数を呼び出すプログラムです。

0:/***702.c*********/

1:
2:#include <stdio.h>

3:
4:
5:

int

main(void){
int main_val;
main_val=2;

6:7:

printf("p1: main_val=%d \n", main_val);
test_fn(main_val);

8:
9:
10:

printf("p4: main_val=%d \n", main_val);exit(0);
}

11:
12:
13:
14:
15:
16:
17:

/**********************
関数名： test_fn
引数 :int test_va;***********************/
test_fn(inttest_val){printf("p2: test_val=%d \n", test_val);
test_val++;7.1. 関数
18:
19:
20:
21:

69
printf("p3: test_val=%d \n", test_val);
return;}/***********************/

4: int 型変数 main val を定義。
7: 変数 main val を実引数として test fn() 関数が呼び出される。
14: 呼び出された test fn() 関数では，仮引数 test val に値を引き取る。
15: 引数の受け渡しが成功しているかを確かめるために，printf() 関数で仮引数 test val の値を表示します。
18: test fn() 関数が終了して main() 関数の 8 行目に戻ります。

7.1.3

引数の受け渡しの例

２つの整数を入力し，大きい方を表示するプログラムを作りましょう。２つの整数を引数として受け取り，大きい方を表示する max out() 関数を作り，main() 関数から呼び出すプログラムにし
ます。

0:
1:
2:

/***703.c*********/#include <stdio.h>

3:
4:

int

main(void){
int a, b;/* 入力する２整数

5:
6:
7:

printf("Input Number
scanf("%d", &a);printf("Input Number

8:
9:

scanf("%d", &b);

10:
11:
12:

max_out(a, b);exit(0);*/1>> ");

2>> ");}

13:
14:/**********************
関数名： max_out

15:
16:
17:

引数 :
int a, int b***********************/
void max_out(inta, int b){

18:
19:

if(a>b){printf("%d \n", a);

20:
21:

}
else{

第 7 章 関数の作り方

70
22:
23:
24:

printf("%d \n", b);}
return;25:
26:}/***********************/

17: 返り値を持たない void 型 max out() 関数の宣言

7.1.4

引数と返り値を持つ関数

関数から計算結果などのデータを返す方法として，返り値を用いることが出来る。例えば，標準関数 sin(x) 関数は引数が double 型変数 x で，返り値として sin(x) の値を返す関数です。返り値を返すためには，関数定義で関数のデータ型を宣言するとともに，return 命令で返り値として返す
データを指定する。もし，引数や，返り値が無い場合は，void 型を用いる。

int

function(int a,
int count;b) { /*int 型の変数を返り値として持つ function() 関数 */int

count=0;
return(count);/* 返り値 count */

}

7.1.5

関数のプロトタイプ宣言

関数はプロトタイプ宣言を行うこと。関数を呼び出す前にプロトタイプ宣言を行うと，コンパイ
ラはあらかじめ関数の型や引数について知ることが出来，データの引き渡しの間違いを避けます。

#include と main() 関数の間に以下の書式で書く。
int

function(int,int ) ;/***関数のプロトタイプ宣言 ***/

２整数の差を求めるプログラムと作りましょう。ただし，２整数の差を求める関数 num diﬀ() を作り，main() 関数から呼び出すようにしましょう。

0:1:/***704.c*********/#include <stdio.h>

2:
3:
4:

int
int

num_diff(int, int);
main(void){

7.2. 変数の特性：スコープ
5:
6:
7:

71

int a, b; /* 入力する２整数 */int
answer;printf("Input Number 1 >> ");8:9:scanf("%d", &a);printf("Input Number

10:
11:
12:

scanf("%d", &b);

13:
14:
15:
16:
17:
18:
19:
19:
20:
21:

2

>> ");

answer=num_diff(a, b);
printf(" Difference=%d \n ", answer);exit(0);}/**********************
関数名： num_diff
引数 :
返り値

int
diff

a, int b***********************/
int num_diff(inta, int b){
int diff;22:
23:

if(a>b){diff=a-b;

24:
25:
22:

}
else{
diff=b-a

26:
27:

}
return(diff);

28:
29:

}
/***********************/

2: int 型の返り値をもつ num diﬀ(int, int) 関数のプロトタイプ宣言。引数は int 型の変数２個。
12: num diﬀ(int, int) 関数を呼び出す。20 行目に行く。
27: int 型の返り値 (diﬀ) を返し，その値が 12 行目の answer に代入される。

7.2

変数の特性：スコープ

宣言された変数があどの範囲で使用できるかを示すのがスコープといいます。変数名は重複しな
いように定義しなくてはいけない。関数｛ ｝内で定義された変数はローカル変数とよばれ，その関数内だけで使える。これに対して，main() 関数の上で定義された変数はグローバル変数とよばれ，プログラムの全範囲で有効である。

第 7 章 関数の作り方

72

7.2.1

int 型と double 型の関数（スコープ）

以下のプログラムは int 型と double 型の関数を呼び出す例です。a と b の値を関数で計算してmain() 関数内で a と b の和を計算しています。
0:
1:/***705.c*********/#include <stdio.h>

2:
3:

int
baiint(int);
double baidbl(double);

3:
4:
5:
6:
7:

double s;//グローバル変数
int main(void){
int a;// ローカル変数
double// ローカル変数b,c;s=1.0;

8:9:10:

a=baiint(123);
printf("%d \n",a);

11:
12:
13:

b=baidbl(1.2);
printf("%lf \n",d);c=(double)a+b;

14:
15:

printf("%lf \n",c);exit(0);

16:
17:
18:
19:
20:
21:
22:
23:

}
/**********************
関数名： baiint()
引数 :
返り値

int
ib

a_val

***********************/
int baiint(int a_val){
int ib;24:25:

ib=2*a_val*s;
return(ib);

26:
27:
28:

}
/**********************
関数名： baidbl()

29:
30:
31:
32:
33:
34:

// ローカル変数// ローカル変数

引数 :
返り値

doubledbb_val// ローカル変数// ローカル変数***********************/
double baidbl(double b_val){
double db;
db=2.0*b_val*s;7.2. 変数の特性：スコープ
35:
36:
37:

return(db);}/***********************/

7.2.2

変数の記憶クラス73

変数のスコープと並んで，変数の重要な性質として記憶クラスがあります。例えば，グローバル
変数は全ての関数から呼び出される可能性があるため，プログラムの実行から終了までずっと有効です。このように実行中ずっと有効であるような変数は記憶クラスが恒久的 (static 変数) であると呼びます。
一方，ローカル変数は，ローカル変数を定義したブロック内 { } だけで有効であり，ブロック内のプログラムが終了すると破棄されます。このように変数の寿命が限られている変数は，記憶クラスが一時的 (auto 変数) であると呼びます。ローカル変数の記憶クラスを auto にすることは，メモリの有効利用に効果的です。

staticint a; /* static 変数 a の宣言
auto
int b; /* auto 変数 b の宣言*/*/

自分が呼び出された回数を返り値として返す関数を作り，その関数を１０回呼び出すプログラムを作りましょう。

0:1:
2:/***706.c*********/#include <stdio.h>
int
call_num(void);

3:
4:

int

5:
6:
7:

main(void){/* 関数の呼び出し回数

int num;
do{

8:
9:

num=call_num();
printf("call_num >> %d \n", num);

10:
11:
12:

}while(num<10);

13:
14:

}
/**********************

15:
16:
17:
18:
19:

*/

exit(0);

関数名：
引数 :
返り値

call_numなし
c_num***********************/
int call_num(void){第 7 章 関数の作り方

74
20:
21:
22:

static23:
24:

return(c_num);

25:

int

c_num=0;c_num++;}/***********************/

20: static 変数 c num の定義。static 変数は関数が呼び出されていない間もデータを保持し続けます。このため，static 変数の c num は，前回呼び出された時の値を保持しています。また static 変数の初期化はプログラムの実行時に一度だけ行われます。

7.3

文字列を渡すchar 型の文字列変数を関数に渡す例を示します。strout() 関数を宣言して，引数を文字列型変数
string にして文字を渡します。
0:1:/****707.c*******************/#include <stdio.h>

2:
3:

void

4:
5:
6:

int main(void){char string[100];

7:8:strout(charss[]);

printf(" Input string >>");

9:
10:
11:

scanf("%s",string);
strout(string);

12;
13:

exit(0);
}

14:
15:
16:

/********************/
void strout(char ss[]){int i;

17:
18:

printf("ss=%s \n",ss);for(i=0; ss[i] !=’\0’; i++){

19:
20:
21:

printf("%x\t",ss[i]);

22:
23:

}
printf("\n");return;}7.4. 演習問題
24:75/***********************/

11: 文字配列 string を引数として渡す。文字列 string の先頭アドレスが渡される。
18: 文字列の最後は’￥0’ で終わることを利用して，strout() 関数を終了する。
19: 文字コードを１６進数で表示する (21: 改行をしめす。

7.4

演習問題

1. 7.1.5 を参考にして，３つの doube 型変数の和を求めるプログラムをつくりましょう。ただし，3 つの double 型変数の和を求める関数 num sum() を作り，main() 関数から呼び出すよ
うにしましょう。
2. 7.2.2 のプログラムで, call num() 関数中の変数 c num を，static 変数ではなく，auto 変数として宣言して見てください。プログラムを実行すると何が起こるか？
3. ２つの整数 m と n をキーボードから入力して，大きい方の数字を表示するプログラムを関
数 imax(m,n) を使って書いてください。
4. 実数型引数 x について，x の範囲を-1 から 2 まで 0.01 ずつ変化させた時，f (x) = x+exp(−x2 )の値を返すプログラムを関数を用いて書いてください。ただし，x の値と f (x) の値をファイ
ル data4 に保存するようにしてください。得られた結果を gnuplot で表示して下さい。

5. ax2 + bx + c = 0 の解を求めるプログラムを関数を用いて書いてください。ただし，a,b,c はキーボードから入力し，a = 0 の場合も解を与えるようにして下さい。また，解が存在しな
い時は，なんらかのメッセージを出すようにして下さい。
6. 関数を使ったプログラムを作ってください。テーマはなんでも構いません。

77

第 8 章 関数とポインタ

8.1

関数の引数とポインタ

ここでは，関数を呼び出す際の引数として，変数のかわりにアドレス（ポインタ）を引数として渡す場合を考えましょう。

8.1.1

ポインタ渡しアドレスを引数として渡す例を以下に示します。

/***************/
int main(void){
int number;
プログラム略
function(&number);exit(0);}/***********/
void function(int *arg){*arg=*arg*2;
return;}mani() 関数で&number を実引数として，function() 関数を呼び出している。実引数&number は
int 型の変数 number のアドレスです。function() 関数は，仮引数として int 型のポインタ引数 arg
を用いて，実引数のアドレスを受け取ります。ポインタ引数 arg には main() 関数の変数 numberのアドレスが格納されています。したがって，*arg には main() 関数の変数 number の値を示すことになり，*arg への代入は number への代入と同じ効果になります。
以下は入力された数値を２倍にする「ポインタ渡し」によるプログラムです。

0:/***801.c*********/

1:
2:#include <stdio.h>voidfunc(int *);

3:
4:

int

main(void){/* ポインタを引数とした関数のプロトタイプ宣言 */

第8章

78
5:
6:
7:

int

/* 入力される数値 */printf("Input Number >>");

8:9:

scanf("%d",&number);

10:
11:
12:
13:
14:

number;関数とポインタ

func(&number);printf("answer=%d \n",number);exit(0);}/**********************
関数名：

15:
16:
17:

func()
引数 :int *arg
２倍するデータのポインタ***********************/

18:
19:

func(int*arg){*arg=*arg*2;

20:
21:
22:

return;}/***********************/

8.1.2

複数のデータを返す関数

ポインタ渡しを用いた関数の呼び出しを利用すると，データを１つしか返せない（返り値が１つ）というｃ言語の関数宣言を解決することが出来ます。ポインタ渡しの方法で，複数の変数のポインタを渡すことによって，呼び出した側の変数を書き換えることが可能になります。
２つの int 型の変数の割り算を行い，商と余りを求める関数を作りましょう。
考え方：２つの int 型の変数を引数にとり，商と余りをポインタ引数で返すようにする。

0:/***802.c*********/

1:
2:#include <stdio.h>
void
warizan(int , int,

3:
4:
5:

int

int *,

int*);

main(void){
int a, b;/* 入力される２つの数値 *//*商と余りの変数宣言

6:
7:

int div, mod;

8:
9:
10:

printf("Input Number , a >>");
scanf("%d",&a);printf("Input Number, b >>");

11:
12:

scanf("%d",&b);*//* 割り算関数 */8.2. 関数の引数と配列

79

13:
14:
15:

warizan(a,b, &div, &mod);

16:
17:

printf("mod=%d \n",mod);exit(0);

18:
19:
20:

printf("div=%d \n",div);}/**********************
関数名： warizan()
引数 :

21:
22:
23:
24:
25:

***********************/
void warizan(int a_val, int b_val,*div_ptr=a_val/b_val;

26:
27:
28:
29:

int a, b割り算するデータ
int *div_ptr, *mod_ptr
商と余りのポインタ
int *div_ptr,

int *mod_ptr){*mod_ptr=a_val % b_val;
return;}/***********************/

13: アドレスが関数の引数になっている。

8.2

関数の引数と配列

8.2.1

配列の受け渡し関数の呼び出しの際に引数として配列を渡すことが出来ます。
文字列をコピーするプログラムを作りましょう。ただし文字列コピーをする関数 strcopy() を作り，プログラムを作りましょう。

0:/***803.c*********/

1:
2:
3:

#include <stdio.h>void
strcopy(char [],

4:
5:

int

char [] );main(void){char a_str[100];/* 入力される文字列 */

6:
7:
8:

char

/*

9:
10:

scanf("%s",a_str);

11:

strcopy(a_str, b_str);

b_str[100];

コピー先の文字列

printf("Input string 1 >>");

*/

第8章

80
12:
13:
14:

関数とポインタ

printf("Input: %s \n",a_str);
printf("Output: %s \n",b_str);

15:
16:

exit(0);
}

19:
20:
21:

/**********************
関数名： strcopy()
引数 :
char
a_arg[], b_arg[]

22:
23:

a_arg[] から b_arg[]***********************/

24:
25:
26:

void
strcopy(char a_arg[],
int count;にコピーする関数

char b_arg[]){

27:
26:

for(count=0; a_arg[count] != ’\0’ ;b_arg[count]=a_arg[count];

27:
28:
29:

}
b_arg[count]= ’\0’ ;return;30:
31:}/***********************/

count++){

2: 関数の定義で、仮引数の配列にはカッコ（ ）の中に要素数は書かない。
11: 引数として配列を渡すときは、配列の先頭アドレスを渡している。
24: 配列は値渡しで引数が受け渡されるのではなく、ポインタ渡しと同じ方法で、アドレスが渡される。配列のように大きなデータを渡すときは、全てのデータ変数を渡すより、アドレスを渡すことで、メモリ使用の削減や速度のアップを行っています。

8.2.2

配列とポインタ

プログラム 803.c で使ったように、配列を引数として関数を呼び出す場合は、実際には配列のデータではなく配列の先頭アドレスが呼び出される関数に渡されます。この節では、配列の形式で受け取るかわりに、呼び出される関数の引数をポインタ引数として受け取る方法を考えましょう。
次のプログラムは、ポインタを利用した、文字列をコピーするプログラムです。

0:1:/***804.c*********/#include <stdio.h>

2:
3:

void

4:
5:

int

strcopy(char *,main(void){char a_str[100];char *);/* 入力される文字列 */8.3. main() 関数の引数

81

b_str[100];/*

コピー先の文字列

6:
7:
8:

char

9:
10:

scanf("%s",a_str);

11:
12:
13:

strcopy(a_str, b_str);

14:
15:

printf("Output: %s \n",b_str);
exit(0);*/

printf("Input string 1 >>");

printf("Input: %s \n",a_str);

16:
19:
20:

}
/**********************
関数名： strcopy()
引数 :

21:
22:
23:
24:
25:

char
*a_arg,*b_arga_arg[] から b_arg[] にコピーする関数***********************/
void
strcopy(char *a_arg,

26:
27:

for(

char

; *a_arg != ’\0’ ;

26:
27:
28:

}
*b_arg= ’\0’ ;

29:
30:

}

*b_arg){a_arg++, b_arg++){*b_arg=*a_arg;

return;31:/***********************/

2: 関数の引数はポインタになっている。
27: ポインタ演算によってコピーを行っている。

main() 関数の引数

8.3

ファイルをコピーするコマンド (cp) は%

cp

file1 file2

で、file1 の内容が file2 へコピーされます。ｃ言語のプログラムがコマンド・ラインからパラメータを得るためには main() 関数に次のような仮引数を持つことが出来ます。

int main( int

argc,

char

*argv[])

第8章

82

関数とポインタ

仮引数には argc や argv という名前が使われるのは慣例となっている。仮引数 argc は、パラメータの個数が格納されます。プログラムの名前自身もパラメータとして扱われます。前の例では argc は

cp と file1 と file2 で argc=３となります。第２引数の argv は char 型のポインタ引数です。argv[0]は最初のパラメータの文字列”cp”を示しています。同様に argv[1] は file1 を、argv[2] は file2 を指しています。次のプログラムを実行してみましょう。

0:1:/***805.c*********/#include <stdio.h>

2:
3:
4:

int main( int argc, char *argv[]){
int count; /* カウンタ変数 */

5:
6:
7:

for(count=0; count<argc; count++){printf("No. %d : %s \n", count, argv[count]);

8:
9:

}
exit(0);

10:
11:

}
/***********************/％ cc 805.c でコンパイラした場合は％ ./a.out file1 file2
で実行してみる。

8.3.1

main() 関数の引数渡し

入力された２つの数値を合計するプログラムを作りましょう。ただし、プログラム名は 806.c とし、入力する変数をコマンドラインで入力するようにしなさい。

0:/***806.c*********/

1:
2:#include <stdio.h>#include <stdlib.h>

3:
4:
5:

int main( int argc,
int sum;6:
7:

if(argc !=3){

8:
9:
10:
11:
12:

/* atoi() 関数を使うためのヘッダー */char*argv[]){

printf(" Error \n");exit(1); /* エラーならプログラムはここで終了する */}sum=atoi(argv[1])+atoi(argv[2]);printf("sum=%d \n", sum);8.4. 関数を指すポインタ
13:
14:
15:
16:

83

exit(0);}/***********************/

7: 引数の個数をチェックして、argc=3 でない場合は終了する。
11: atoi( ) 関数は文字列を int 型の数値に変換する関数です。
実行例％ cc -o 806 806.c％ 806 100 200

8.4関数を指すポインタ

関数をポインタとして使うには、次のように関数へのポインタの宣言をしなければいけない。

int (*function)();/*function は int 型を返す関数へのポインタである。*/*function は int 型を返す関数を意味するから「function] はそれへのポインタである。
次のプログラムはポインタ function を使いながら、あるときは sum() 関数を実行し、またあるときは div() 関数を実行しています。

0:
1:/***807.c*********/#include <stdio.h>2:
3:void (*function)(int a,

int

b);

4:
5:
6:

void sum(int a_val, int b_val);
void dif(int a_val, int b_val);

7:
8:

int

/*

関数へのポインタ function の宣言

main(void){
int a=100;

9:
10:
11:

int b=100;
function=sum;

/*

12:
13:

function(a,b);function=dif;/* そのアドレスを実行する。 *//* 関数アドレス dif を設定し、 */

14:
15:
16:

function(a,b);

/*

17:
18:

}
/******************/

exit(0);関数アドレス sum を設定し、そのアドレスを実行する。

*/

*/

*/

第8章

84
19:
20:
21:

void sum(int a_val, int b_val){printf("a+b=%d \n", a_val+b_val);
return;22:23:}/******************/

24:
25:
26:

void dif(int a_val, int b_val){printf("a-b=%d \n", a_val-b_val);
return;27:28:

関数とポインタ}/***********************/

3: int 型の引数を２つ使用し、戻り値は void である。

関数へのポインタの配列

8.5関数へのポインタは配列とすることも出来ます。その配列要素はいくつもの関数の入り口アドレ
スを持ちます。たとえば、

void(*keisan[4])(int a , int b );とすると、keisan は、２つの int 型の引数を持ち、返り値が void 型である関数への入り口アドレ
スを４つ持つことが出来る、ポインタ配列となります。
次のプログラムは、ポインタ keisan に、wa, sa, seki, syo という４つの関数を割り当てたもの
です。

0:
1:

/***808.c*********/#include <stdio.h>

2:
3:
4:

void (*keisan[4])(double a, double
void wa(double a, double b);

5:
6:

void
void

sa(double a, double b);
seki(double a, double b);

7:
8:
9:

void

syo(double a, doubleintmain(void){b);/*

関数へのポインタ function の宣言

b);

10:
11:

int i;
keisan[0]=wa;/*********************************/

12:
13:
14:

keisan[1]=sa;
keisan[2]=seki;keisan[3]=syo;/* 4 つの関数のアドレスを設定する *//*********************************//********************************/

15:
16:

for(i=0; i<=3; i++){*/8.6. 演習問題
17:
18:
19:
20:
21:

keisan[i]( 100.0, 200.0);}
exit(0);}/***************************/

22:
23:
24:

void wa(double a, double b){
printf("a+b=%lf \n", a+b);
return;25:
26:}/***************************/

27:
28:
29:

void sa(double a, double b){
printf("a-b=%lf \n", a-b);
return;30:
31:}/***************************/

32:
33:
34:

void seki(double a, double b){
printf("a*b=%lf \n", a*b);
return;35:
36:}/***************************/

37:
38:
39:

void syo(double a, double b){
printf("a/b=%lf \n", a/b);
return;40:41:}/***************************/

8.6

85

演習問題

1. ２つの実数型変数の四則演算を行うプログラムをポインタを使って作りなさい。いろいろな方法があります。
2. 806.c と 807.c を参考にして、コマンドラインから２つの整数を入力して和と積を求めるプロ
グラムを、関数へのポインタを使って作ってください。
3. ２つの文字列を接続するプログラムを作りなさい。ただし、文字列を接続する関数を strjoin() を作り、main() 関数がその関数を呼び出す形式でプログラミングしてください。
(a) 803.c を参考にして配列を用いてプログラムしなさい。
(b) 804.c を参考にして配列とポインタを用いてプログラムしなさい。
4. 定価（税抜き）x 円の商品を，支払い金額 y 円出して購入しました。税込み価格，支払い金額，おつりを表示するプログラムをつくりましょう。ただし，定価（税抜き）と支払い金額

第8章

86

関数とポインタを２つの引数にとり，税込み価格，支払い金額，お釣りをポインター引数で返す関数 buy()を定義して使って下さい。(802.c を参照）

5. ３次元空間上の点 P の位置 (x, y, z) をコマンドラインから実数型で３つ入力して、原点から
点 P までの距離を出力するプログラムを作ってください。文字列を実数型に変換する関数、

atof() を用いる。
6. 任意の関数 f (x) の x = a での１回微分、２回微分を”数値微分”で計算するプログラムを作
りましょう。ただし、関数のポインタ配列*bibun[ ] に first()、 second() という関数を割り当て、それぞれ１回微分, ２回微分を代入して下さい。数値微分は
f ′ (x) = (f (x + δx) − f (x))/δx,f ”(x) = (f (x + δx) − 2f (x) + f (x − δx))/(δx)2 ,を用いてください。例として、関数 f (x) = log(1 + x) の x = 0 での１回微分と２回微分の値を表示してください。δx の値は 0.001 ぐらいにしておきましょう。

7. 任意の関数 f (x) は x = a の近傍で、テーラー級数
f (x) = f (a) + f ′ (a)(x − a) +f ”(a)(x − a)2 + · · ·2!

に展開することが出来る。前のプログラムを発展させ、a の値を入力したときに関数 f (x) のテーラー展開を x の二次まで表示するプログラムを作ってください。１例として、f (x) = log(1+x)としましょう。その後，任意の関数でやってみましょう。

87

第 9 章 構造体と列挙型整数を取り扱うには int 型や long 型、実数は float 型や double 型、文字を扱うには char 型など
様々なデーター型を用いてきました。ｃ言語ではこのようなデータ型を作り出す機能が備わっています。この章では、データ型を新たに作り出し、プログラムをスマートに書く方法について学びま
しょう。

9.1

構造体

9.1.1

複素数を扱うプログラム

ｃ言語では、複素数 (3+2i など) を扱う複素数型は存在しません。まずは、２つの複素数の足し算を行うプログラムを作りましょう。この例では、実部と虚部をそれぞれ、変数に入力し、その後、実部どうしを足し、虚部どうしを足すことで、複素数の足し算を行っています。

0:
1:
2:
3:
4:

/***901.c*********/#include <stdio.h>
int main(void){
double a_re, a_im;/*入力される複素数の実部と虚部 */b_re, b_im;/*入力される複素数の実部と虚部 */ans_re, ans_im;/*答えの複素数の実部と虚部 */5:
6:doubledouble7:8:9:printf("Input complex 1 re >>");
scanf("%lf",&a_re);

10:
11:

printf("Input complex 1 im >>");
scanf("%lf",&a_im);

12:
13:
14:

printf("Input complex 2 re >>");
scanf("%lf",&b_re);printf("Input complex 2 im >>");

15:
16:

scanf("%lf",&b_im);

17:
18:
19:

ans_re=a_re+b_re;
ans_im=a_im+b_im;

88

第9章

20:
21:
22:

printf("Answer: %lf+%lf i = \n", ans_re, ans_im) ;exit(0);/***********************/

9.1.2

新しいデータ型の定義

構造体と列挙型

プログラム 901.c では複素数のデータを２つの実数型のデータに分けて表しました。しかし、複素数は複素数のためのデータ型で扱った方が合理的です。ｃ言語では構造体を用いて、新しいデー
タ型を定義できます。配列は同じデータ型のデータをまとめたものです。これに比べて、構造体は異なるデータ型をもつデータを自由にまとめておくことが出来ます。構造体にまとめられた個々の成分はメンバと呼ばれます。
構造体の定義は以下のように行います。

structcomplex {/*複素数型の宣言 */doublere; /* 実部 */doubleim;/* 虚部 */};
一行目は構造体名 complex の定義、２行目は構造体のメンバを示します。ここでは、メンバ名をre と im としました。この例では、complex 構造体は、２つの double 型のメンバ re と im を含んでいます。このように定義された complex 構造体は、新しいデータ型として、そのデータ型をもつ変数を宣言できます。プログラム中での構造体の宣言は以下のように行います。

structcomplexcmplx_val;

complex 構造体型で、変数 cmplx val を宣言しています。
構造体を用いる場合、そのメンバを自由に操作出来なくてはなりません。構造体のメンバは、メ
ンバ名によって識別されます。これを操作するには
cmplx_val.re=1.0;のように構造体型の変数名の後ろに「.(ピリオド）またはドット演算子」を挟んでメンバ名を書きます。「構造体変数名. メンバ名」の形で普通の変数や配列などと同様に扱えます。

9.1.3

構造体を使ったプログラム

プログラム 9-1.c を構造体を使って書き換えましょう。

0:
1:
2:
3:

/***902.c*********/#include <stdio.h>
structcomplex{ /* 複素数型 */double re;/* 実部 */

9.1. 構造体
4:
5:
6:
7:
8:

89
doubleim;/* 虚部 */};
int main(void){
struct complex
complexa_val, b_val;
ans;/*入力される複素数 *//*答えの複素数 */

9:
10:
11:

struct12:
13:

scanf("%lf",&a_val.re);printf("Input complex 1 im >>");

11:
12:
13:

scanf("%lf",&a_val.im);
printf("Input complex 2 re >>");
scanf("%lf",&b_val.re);

14:
15:

printf("Input complex 2 im >>");
scanf("%lf",&b_val.im);

printf("Input complex 1 re >>");

16:
17:
18:

ans.re=a_val.re+b_val.re;
ans.im=a_val.im+b_val.im;

19:
20:

printf("Answer: %lf+%lf i \n", ans.re, ans.im) ;

21:
22:

/* 実部の足し算 *//* 虚部の足し算 */exit(0);/***********************/

2: complex 構造体の定義。構造体の定義はプログラムの冒頭部分で行うようにする。
8: complex 型変数 a val と b val と ans の宣言。各変数は re と im の２つのメンバを持つ。
20: 実部 (ans.re) と虚部 (ans.im) の変数名を使って答えを書き出す。

9.1.4

構造体と関数構造体として宣言された変数は、関数への引数として渡したり、関数からの返り値として返した
りできます。プログラム 902.c を、構造体を引数や返り値とする関数を用いて書き換えた例を、以下のプログラムで示します。

0:
1:
2:
3:
4:
5:
6:

/***903.c*********/#include <stdio.h>
struct complex{/* 複素数型 */doubledouble};struct complex

re;
im;

/* 実部 *//* 虚部 */

get_cmp(void);

第9章

90
7:
8:
9:

struct
void

10:
11:

int main(void){
struct complex

12:
13:
14:

complexstructadd_cmp(struct complex, struct complex);
put_cmp(struct complex);

complex/*入力される複素数 */a_val, b_val;
ans;/*答えの複素数 */a_val=get_cmp();

15:
16:

b_val=get_cmp();

17:
18:
19:

ans=add_cmp(a_val,b_val);
put_cmp(ans);

20:
21:

}

22:
23:
24:

/******************************************
get_cmp()複素数の入力のための関数
引数： なし25:
26:

返り値：struct complex 入力された複素数

27:
28:
29:

exit(0);******************************************/
struct complex get_cmp(void){
struct complex inp_val;/*複素数の入力値*/

30:
31:

printf("Input complex re >>");
scanf("%lf",&inp_val.re);

32:
33:
34:

printf("Input complex im >>");
scanf("%lf",&inp_val.im);
return(inp_val);

35:
36:

}
/******************************************

37:
38:
39:
40
41:
42:

add_cmp(struct complex, struct complex) 足し算をする関数
complex a_val と struct complex b_val
返り値：struct complex
足し算の答え。
引数：struct*******************************************/
struct complex add_cmp(struct complex a_arg, struct complex
struct complex answer;/*足し算の答え */

43:
44:
45:
46:

構造体と列挙型

answer.re=a_arg.re+b_arg.re;answer.im=a_arg.im+b_arg.im;
return(answer);}/* 実部の足し算 *//* 虚部の足し算 */b_arg){

9.1. 構造体
47:
48:
49:

91

/**************************************
put_cmp(struct complex) 複素数の出力のための関数
引数：struct complex
out_data
返り値：なし50:
51:***************************************/

52:
53:
54:

void put_cmp( struct complex
printf("Answer: %lf+%lf i
return;out_data ){\n", out_data.re, out_data.im) ;

55: }
56:
/****************************************************/

6-8: complex 型の関数（３つ）の宣言。
11-12: complex 型の変数（３つ）の宣言。
14-15: データ入力のための get cmp() 関数の呼び出し。
17: 足し算のための、add cmp() 関数の呼び出し。
19: 結果の出力のための、put cmp() 関数の呼び出し。

9.1.5

構造体とポインタ

int 型や char 型のポインタと同様に、構造体に対するポインタを考えることが出来る。構造体のポインタ変数の宣言は、以下のように行う。
a. 構造体のポインタ変数の宣言
structcomplex*cmp_ptr;
構造体名 ポインタ変数名

b. 構造体のポインタ変数によるメンバの操作(*cmp_ptr).re=10.0;(*ポインタ変数名). メンバ名
c. 構造体のアロー演算子によるメンバの操作
cmp_ptr ->re =10.0;
ポインタ変数名 -> メンバ名

9.1.6（->はアロー演算子と呼ぶ）

アロー演算子： ー＞

アロー演算子は構造体変数がポインタによって示される時に使います。例として次のような構造
体名 seiseki を考えましょう。

第9章

92

構造体と列挙型

struct seiseki{char name[100];
int butsuri;
int program;}
メンバーが char 型の name と、int 型の butsuri と program になってます。構造体変数は通常の変数と同じようにポインタで操作することが出来ます。例えば、

structseisekistructgakusei;seiseki*sp=&gakusei;と宣言すると、ポインタ sp は構造体変数 gakusei の先頭アドレスを示します。(*sp) は値を示すので、ドット演算子でメンバを参照できます。例えば、(*sp).butsuri=80;と書くことが出来ます。アロー演算子を使うと上の文はsp->butsuri=80;となります。アロー演算子を使うと面倒な（）記述が不要になります。

次のプログラムはアロー演算子の利用例です。少しくどいプログラムですが、いろいろな使い方を覚えてください。

0:/****904.c*****************************/

1:
2:#include <stdio.h>

3:
4:
5:

struct seiseki{char name[30];float butsuri;

6:
7:

float program;
};

8:
9:
10:

int main(void){
struct seiseki
struct seiseki

11:
12:

float

13:
14:
15:

printf("Your Name >>");
scanf("%s",sp->name);printf("Score of Busturi >>");

16:
17:

scanf("%f",&sp->butsuri);/* アロー演算子を使ったメンバへの入力
printf("Score of Program >>");

*/

18:

scanf("%f",&sp->program);/* アロー演算子を使ったメンバへの入力*/

gakusei;/* seiseki 型をもつ変数 gakusei を宣言 */*sp=&gakusei;/*** ポインタ sp に変数 gakusei の ****/

total_mem, total_dot, total_arro;/***

先頭アドレスを与える ***/

9.2. 列挙型とデータの定義
19:
20:
21:

93

printf("Name: %s \n", sp->name);printf("Butsuri: %f \n", sp->butsuri);

22:
23:

printf("Program: %f \n", sp->program);

24:
*/
25:

total_mem=gakusei.butsuri+gakusei.program;

26:*/

total_arro=(*sp).butsuri+(*sp).program;/*アロー演算子を使った和の計算*/

total_dot=sp->butsuri+sp->program;

27:
28:
29:

/*ドット演算子を使った和の計算/*ポインタ演算子を使った和の計算

printf("Total_mem: %f \n", total_mem);
printf("Total_dot: %f \n", total_dot);

30:
31:

printf("Total_arro: %f \n", total_arro);

32:
33:
34:

exit(0);}/*******************************************/

9.2

列挙型とデータの定義

列挙型 (enum) は名前付き定数（列挙定数）を使って、データの取りえる値を全て列挙したものです。意味の解りづらい数値データを、名前付き定数で操作出来るようにします。
テストの点数を引数として受けて、成績 A,B,C,D を返り値（数字ではない）として返すプログ
ラムを作りましょう。列挙型の定義は

enum

seiseki{A, B, C, D};

で、列挙型名 (seiseki) の後ろに列挙型が取りうる値であるタグ名を並べて書きます。列挙型変数
の定義は

enum

seisekiclass;です。この例では、seiseki 列挙型の変数 class の定義を行っています。変数 class は、A, B, C, Dの４種類の値だけを取ることが出来ます。

0:/****905.c*****************************/

1:
2:
3:

#include <stdio.h>enum

seiseki{A, B, C, D};

4:

enum

seiseki/** 成績のクラスの列挙型**/

get_class(double);/**成績を返す関数の宣言**/

第9章

94
5:
6:
7:

int main(void){
double point;/**成績**//**seiseki 列挙型の変数 class の定義**/

8:9:enum seiseki class;

10:
11:
12:

printf("Input point >> ");scanf("%lf", &point);

13:
14:

class=get_class(point);
switch(class){

15:
16:
17:

case A :18:
19:/**引数 point で成績を返す**/

case B :printf("Class A \n");break;
printf("Class B \n");case C :break;
printf("Class C \n");

20:
21:
22:

case D :23:
24:

default :}break;
printf("Class D \n");break;
printf("Error \n");

25:
26:
27:

}

28:
29:

/*********************
get_class(double point )

30:
31:
32:

引数：double pointclass**********************/

33:
34:

enum seiseki get_class(double point){if(point<25.0){

exit(0);

成績を返す関数

返り値：enum

35:
36:
37:

return(D); /***返り値として D を返す***/}
else if(point<50.0){
return(C); }

38:
39:
40:

else if(point<75.0){
return(B); }else if(point>75.0){

41:
42:

return(A); }43:

構造体と列挙型}/*******************************************/

9.3. 自己参照構造体

9.2.1

95

データ型の定義

Ｃ言語ではデータ型の定義で、データ型に別名を付ける事が出来る。typedef によるデータ型の定義を行う。

typedefstructcomlpexcmplx;

データ型 (struct comlpex) に新しい名前 (cmplx) を付ける。以降、名前 (cmplx) を用いてデータ型を宣言することが出来る。
プログラム 903.c の main() 関数をデータ型の定義で書き換えた例を示します。

0:
1:
2:

/************/#include <stdio.h>
typedef struct complex{

3:
4:
5:
6:
7:

doubledouble
cmplx
cmplx

re;
im;

} cmplx ;get_cmp(void);
add_cmp(cmplx,/* 複素数型 *//* 実部 *//* 虚部 *//*** <===ここで別名を定義できる*********/

cmplx);

8:
9:

void

put_cmp(cmplx);

10:
11:
12:

int main(void){
cmplx a_val, b_val; /*入力される複素数 */cmplx ans;/*答えの複素数 */

13:
14:
15:

a_val=get_cmp();b_val=get_cmp();

16:
17:

ans=add_cmp(a_val,b_val);

18:
19:
20:

put_cmp(ans);exit(0);

21:
22:

}
/*******************************************/

9.3

自己参照構造体

構造体の中に自分自身を指し示すポインタを含ませることができる。これを自己参照構造体と呼びます。自己参照構造体は同じタイプの構造体を次々に繋いでいき、データーのチェーンを作るときに使われます。
例えば大量のデータを大きい順に配列

第9章

96

構造体と列挙型

int data[1000];に格納していたとします。２番目のデータを削除したい場合、データ自体は残したまま、図 12.1の (b) の様にデータ自体は残したまま、１番目と３番目を結ぶことで削除したことと同じことになります。また２番目と３番目の間に新しいデータ６を挿入したいときは (c) のように順番を変えてやればいいのです。
(a)

1

2

3

4

(b)

1

2

3

4

(c)

1

2

3

4

6

図 9.1: データのチェーン構造9.3.1

自己参照構造体の宣言

次の例は、名前 (name) と物理の点数 (butsuri) からなるデータをチェーンできる自己参照構造
体 (person) を宣言しています。

typedef struct person{char name[30];doublestructbutsuri;person *next;}Person;struct person *next; は次の person 構造体のアドレスが入る。つまり、ポインタ next が次の構
造体のアドレスを示すことが出来ます。チェーンの終了は NULL を返すようにします。図 12.2 の(3) の next に NULL が格納されているときここでチェーンは終了を意味します。
次のプログラムは、キーボードから入力されたデータ（名前と点数）を次々に自己参照構造体に繋いでいき、点数の高い順に出力するようにします。

0:/****906.c*****************************/

1:
2:#include <stdio.h>#include <stdlib.h>/* atof()

3:
4:
5:

#include <string.h>/* strcpy() 関数を使うためのヘッダー */6:
7:

typedef structperson{char name[30];double butsuri;

malloc() 関数を使うためのヘッダー *//* 構造体の宣言*/

9.3. 自己参照構造体

97

struct person(1) startname(2) count->next

Mike

name

(3) count->next

Peter

name

Mark

busturi 100

busturi 90

busturi 80

next

next

next

xxxxx

xxxxx

xxxxx

図 9.2: 構造体のモデル

8:

structperson/* 自分自身を指し示すポインタ next */*next;/* person 構造体の別名 Person */9:10:}Person;

11:
12:
13:

int main(void){
Person mark={"", 0, NULL}; /*** NULL を終端マークにする***/
Person *start=&mark; /** ポインタ start に先頭アドレスを与える**/
PersonPerson

16:
17:
18:

char
while(1){

19:
20:
21:

/*****キーボードからデータを入力する****/
printf("Name=");gets(name);

22:
23:

if(strcmp(name,"")==0){break;}

24:
25:
26:

printf("Butsuri=");gets(butsuri_data);/*****構造体１個分のメモリを確保する *******/

27:
28:
29:
30:
31:

*ptr;
*count;/** Person 構造体のポインタ宣言 (作業用）**//** Person 構造体のポインタ宣言 (ループ制御用）**/

14:
15:

name[30], butsuri_data[10];/**データ入力用**//* 改行が入力されると入力が終了する***/

ptr=(Person *)malloc(sizeof(Person));if(ptr==NULL){/**メモリの確保が失敗したとき Error を出力 **/printf("Error \n");exit(1);}

32: /**確保した構造体にデータを入れる*/33:
strcpy(ptr->name, name);34:
ptr->butsuri=atof(butsuri_data);
35:
36: /**作成した構造体 ptr を既存のチェーンにはめ込んでいく**/37:

for(count=start; count->next != NULL; count=count->next){

第9章

98
38:
39:
40:

if(ptr->butsuri > count->next->butsuri){ /**次のデータより点数が高いとき**/ptr->next = count->next;/** チェーンをつなぎ変える。**/
count -> next= ptr;

41:
42:
43:
44:
45:

break;}}/**最低点の時チェーンの最後につなぐ**/if(count->next == NULL){/*チェーンが NULL に行き着いたとき**/

46:
47:

count->next=ptr;/*最後尾に構造体をつける**/
ptr->next=NULL; /* 新たに最後尾マークを付ける */

48:
49:
50:

}

51:
52:

}

/********

while() 文はここまで繰り返す******/

for(count=start->next; count != NULL; count=count->next){

53:
54:
55:

}

56:
57:

exit(0);}58:構造体と列挙型

printf("%s %f \n", count->name, count->butsuri);/*******************************************/

21: gets(ss) 関数: 1 行入力して ss に格納する。
27: malloc(size) 関数：size バイトのメモリを確保する。malloc() 関数の返り値は void 型のポインタであるので、使いたいデータ型にキャストする必要がある。ここでは Person 型にキャストしている。

33: strcpy(ss, ”test”): 文字列 ss に”test”をコピーする。
34: atof(ss) 関数：文字列 ss を double 型に変換して ptr->butsuri に代入する。（４０行目）
struct person
countcount->next

name Mikebusturi 100
nextname Peter
CUT !!!!

xxxxx

busturi 90
next

xxxxx

ptr
name Jamesbusturi 95
next

xxxxx

図 9.3: チェーンのつなぎ替え

36: チェーンの繋ぎ替えは（１)ptr->butsuri=95 が count->next->butsuri(=90) より大きいとき、(38 行目）

9.4. 演習問題

99（２）Mike と Peter のラインを切り,Peter のアドレスを ptr->next に代入する。（３９行目）（ptr->next = count->next;)（３）ptr のアドレスを count->next に代入し、Mike→James へのラインを作る。

9.4

演習問題

1. プログラム 903.c を参考にして、複素数の引き算と掛け算を行う関数と加えて、足し算、引き算、掛け算を出力するプログラムを作りなさい。

2. 9.2.1 を参考にして、問１をデータ型の定義を用いて書き換えよ。
3. 試験の成績のデーター処理をしたい。氏名と、物理、化学、生物の点数を次の構造体の配列
で扱い、各教科の平均値と最大値、最小値を求めるプログラムを作ってください。

struct seiseki_data{char name[20];doublebutsuri;doubledoublekagaku;seibutsu;};
構造体の初期化（データー入力）はたとえば、

structseiseki_data

data1[20]={{ "田中", 100, 90,90},{ "鈴木", 80, 90,90},{ "松本", 100, 80,70},
・
・
・
・
・,

};
のようにする。

4. プログラム 906.c を参考にして、物理、生物の点数を入力してその合計点の高い人順に出力するプログラムを作りなさい。
5. 3 次元座標上の２点 A(x1 , y1 , z1 ) と B(x2 , y2 , z2 ) の間の距離を求めるプログラムを作りましょう。ただし，２点の座標はキーボードから入力する。3 次元座標上の任意の点の座標を
構造体

point_3d{ }をつかって扱うように。

101

第 10 章 Java 言語の基本
あと３回の授業で，Java の基本と簡単なアニメーションについて学びましょう。Java はＣ言語を発展させた言語なので，C 言語と同じ部分が沢山ありますが，Java 独自の書き方も覚えましょう。Java は 1995 年に Sun マイクロシステム社が発表した、OS を選ばないプログラミング言語で，Sun 社のホームページからフリーでダウンロード出来るフリーソフトです。自分のパソコンにインストールして使って下さい。

10.1

Java プログラムの実行

10.1.1

Hello プログラム

次のプログラムを入力して「s1001.java」という名前でファイルを保存してください。

0:
1:
2:

//***s1001.java*********//
public class s1001{//** クラス名は s1001 ***/
public static void main(String args[]){ //**決まり文句

3:
4:
5:

System.out.println("Hello");}}6://*******************////**出力命令 **/Java のファイルを保存するときはクラス名（プログラム）と同じ名前にしなければいけない。
コンパイルするには、%javacs1001.javaとします。コンパイルが成功すれば「s1001.class」というファイルが自分のディレクトリーに出来ているはずです。ls コマンドで確かめてください。
プログラムの実行命令は

%java

s1001

です。上の例でプログラム名「s1001」のところが、いろいろなプログラム名に置き変わります。
次のプログラムを入力し実行してください。

第 10 章 Java 言語の基本

102
0:
1:
2:

//***s1002.java*********//
import java.awt.*;
public class s1002{

3:
4:

public static void main(String args[]){Frame t=new Frame("Sample");

5:
6:
7:

t.setSize(300,100);t.setVisible(true);
}

8:
9:

}
//*******************///*****又は， t.show(); *****/

1: java.awt というパッケージ内 1 にある全てのクラスをインポートする。
4: クラス Frame のインスタンス t を生成。
5: 画面のサイズを指定するメソッド setSize(i, j)。引数は幅 i=３００，縦 j=１００ピクセルを int型で指定している。
6: 表示の実行。
小さなウインドが現れましたね。Java はこういったグラフィックスが容易に出来るのも特徴です。

10.2

Java プログラムの形式

Java にはアプリケーションとアプレットという２つの実行形式があります。アプリケーション
は普通の使い方で自分のコンピューターに結果を表示する使い方です。一方、アプレットはイン
ターネットのホームページ上で実行する形式を持ちます。遠く離れたインターネット上の他人のコンピューター上で実行されます。

Java の基本的な部分はＣ言語とほとんど同じなので、Ｃ言語を勉強した皆さんにとっては理解しやすいでしょう。最近は Java に関して多くの参考書がでています。どれを使うか迷ってしまう程です。自分に合ったで参考書を１冊選んで見て下さい。

Java プログラムの形式は、public class クラスの名前{
public static void main(String args[]){
実行プログラム}}です。Java ではこの class(クラス）という単位でプログラムを作成していきます。class の次に書
かれた s1000 などがこのクラスの名前になります。
1 java.awt には様々なクラスやメソッドが標準で準備されている。

10.3. 変数103

クラスはさらに メソッド という単位にわかれます。前の例では、s1002 というクラスの中には
main という名前のメソッドが１つあることになります。
クラスやメソッドの前に着いている public, static, void などは修飾子 2 と言います。

10.3

変数

Ｃ言語と同じように Java でも変数をにいろいろな値を代入できます。基本データの型の変数をまとめておきます。
表 10.1: 基本データ型の変数の種類
種類

型

値

代入方法

byte

-128 ∼ 128 (8 ビット）

a=3

short
int

-32768 ∼ 32768 （１６ビット）−231 ∼ 231 − 1（３２ビット）

a=2
a=2

long

−263 ∼ 263 − 1（６４ビット）

a=2

実数

float
double38

約 −10 ∼ 10 (３２ビット）-約 −10308 ∼ 10308 (６４ビット）

a=3.0fa=2.0

論理値

booleantrue, false （１ビット）

a=true, b=false

文字

char

文字 （１６ビット）

a=’A’

整数

38

文字列を表示するには、

System.out.println("表示したい文字列");を使います。System.out.println(” ”) の場合は表示後に改行しますが 3 、System.out.print(” ”) の場合は改行しません。
次のプログラムはプリント文による表示の例を示します。

0:1://***s1003.java*********//
public class s1003{

2;
3:public static void main(String args[]){int a, b, sum;

4:
5:
6:

a=2; b=3;
sum=a+b;
System.out.println("a+b="+sum);

7:

System.out.println(sum);// **a+b=5 と表示される****//// **5****//

2 public: どのクラスからもこのメソッド main を呼び出せる； static: このメソッド main はクラスの名前と結びついている；void : 返り値を持たない
3 Ｃ言語では printf() 文中の￥n に対応しています。

第 10 章 Java 言語の基本

104
8:
9;
10:

System.out.println("a"+"b");System.out.println("Sum"+a+b+sum);
}

11:
12:

// **ab ****//// **Sum235 ****//}//*******************//

6-9: System.out.println() 関数のいろいろな使い方。

10.4

クラス・メソッド

クラスを理解するために、２つのクラスを持つプログラム例を示します。まずクラス「s1004」のメソッド main から計算を始めます。５行目でクラス「t1004」のメソッド keisan を呼び出して
いる。

0:
1:
2:

//***s1004.java*********//
public class s1004{
public static void main(String args[]){

3:
4:
5:
6:
7:

//**クラス s1004 ***////** メソッド main **//

double a,b;a=2.0;
b=t1004.keisan(a);System.out.println("a^2="+b);}8:}
9://*******************//

5: t1004.keisan(a) は引数「a」をクラス「t1004」のメソッド「keisan」に引き渡している。Ｃ言語でたとえると、t1004.keisan(double a) という関数を定義したのがメソッド「t1004.java」である。
0:1://***t1004.java*********//
public class t1004{

2:
3:
4:

public static double keisan(double x){ //** メソッド keisan の宣言**//double y;y=Math.pow(x,2.0);

5:
6:
7:

return y;}}8://*******************////**クラス t1004 の宣言***//

10.5. インスタンス

105

2: double 型の返り値を返すメソッド keisan() の宣言。引数は double 型。
4:クラス「Math」を用いた数値計算。始めから Java に入っている標準関数である。Math.pow(x,2.0)
は x の２乗を計算する。
実行は以下のように s1004.java をコンパイルする。t1004.class は自動的にできる。

% javac s1004.java% java s1004

static がついたメソッドを「クラスメソッド」、static が付かないメソッドを「インスタンスメソッ
ド」といいます。

10.5

インスタンス

先の例では、入力した数字を２乗して答えを返すクラス「keisan」を作りました。今度は入力した数字の平方根を返す機能もほしくなったとします。先程と同じように新しく平方根を求めるクラスを作るという方法もありますが、クラスの数だけソースファイルが必要になりますから、その手間は大変です。このクラスの変わりに、ここではインスタンスを使ったプログラムを紹介します。

10.5.1

インスタンスの生成

この例では、s1005 と keisan という２つのクラスを使います。keisan クラスに沢山のインスタン
スを作るこつが出来ます。java では任意のクラスの型で変数を宣言できます（以下の４行目：オブジェクト型の変数）。

0://***s1005.java*********//

1:
2:

public class s1005{//**クラス s1005 の宣言***//
public static void main(String args[]){//** メソッド main の宣言**//

3:
4:
5:

double b;
keisan s;//クラス名 (keisan) 型のオブジェクト変数 s を宣言する ////インスタンスはクラスの前に new を作用させると生成される////インスタンス s のメソッド nijou を引数３をつけて呼び出す//6:7:

s=new keisan();b=s.nijou(3.0);

8:
9:
10:

System.out.println("a^2="+b);

11:
12:

b=s.root(3.0);
System.out.println("a^{0.5}="+b);

13:
}
14:
}
************************************

第 10 章 Java 言語の基本

106

4: 任意のクラス名を型とする変数をオブジェクト型の変数と呼び，宣言した型のクラスのインス
タンスが代入される。
6: 引数無しでインスタンスに渡す。new により生成したインスタンスを変数 s に代入する。これにより，s を参照することで，生成したインスタンスを呼び出せるようになる。

************************************
0:
1:
2:

//***keisan.java*********//
public class keisan{//**クラス keisan の宣言***//
public double nijou(double x){//インスタンスメソッド nijou の宣言//

3:
4:

double y;y=Math.pow(x,2.0);

5:
6:
7:

return y;}//**************//

8:
9:

public double root(double x){ //インスタンスメソッド root の宣言//
double y;

10:
11:
12:

y=Math.sqrt(x);return y;
}

13:
14:

//*******//}

15://*******************//
16:

10.6

オブジェクトの引き渡し

引数として int 型や double 型などの変数だけでなく，オブジェクト型の変数も引き渡せます。以
下の例では，s1006 と plus の２つのクラスを使います。２数の和をオブジェクト変数 pa, pb を用いて渡しています。

0:
1:
2:
3:
4:
5:

//***s1006.java*********//
public class s1006{
public static void main(String args[]){plus pa, pb;/* plus のインスタンス (オブジェクト変数）pa, pb の宣言 */pa=new plus(2); /*2 を引数に plus のインスタンス pa を生成する*/
pb=new plus(5); /*5 を引数に plus のインスタンス pb を生成する*/

6:
7:s1006 a=new s1006();

8:
9:
10:

a.hyouji(pa);a.hyouji(pb);}/* s1006 のインスタンス a を生成する。引数無し*//*pa を引数にメソッド hyouji を呼び出す *//*pb を引数にメソッド hyouji を呼び出す */10.7. 継承

107

11:
12:
13:

void hyouji(plus p){ /*plus 型の変数 p で引数 pa を受け取る*/
System.out.println(p.pl(3)); //クラス plus のメソッド pl を引数３で呼び出す//
}

14:
15

}
://*******************//

8: 11 行目の p にオブジェクト pa を渡す。p=pa0:1:2//***plus.java*********//
public class plus{
int y;
plus(int x){ /* s1006 クラスの４行目 plus(2) の２がここに渡される。x=2
y=x;/*y=2 */

3:
4:
5:
6:
7:

}
public int pl(int w){
int z;

8:
9:
10:
11:
12

*/

//int 型の pl( ) メソッドの宣言。引数は整数型 w//

z=w+y;
return z;}}://*******************//

6: s1006 クラスの 12 行目 p.pl(3) の 3 がここに渡される。w=3

10.7

継承

すでにあるクラスの内容を少しだけ変更したり，付け加えたりして，新しいクラスを作りたい場
合などがあります。Java には既存のクラスを変更して新しいクラスを作る仕組みがあります。親となる既存のクラスをスーパークラス，新しく作る子供のクラスをサブクラスといいます。スーパークラスで定義した変数やメソッドはサブクラスの中で自由に使えます。
以下では３つのクラス s1007, Wa, Super1 が登場します。２数の和を求めるサブクラス Wa を
スーパクラス Super1 から作る例を示します。
クラス s1007 のプログラム

0:1://***s1007.java*********//
public class s1007{//クラス s1007 の宣言 //

2:
3:
4:

public static void main(String args[]){int a,b;//整数型変数 a,b の宣言 //a=10; b=20;

5:
6:

Wa s;
s=new Wa();//クラス Wa のインスタンス s を宣言 ////Wa のインスタンスを生成し s に代入 (引数は無し） //第 10 章 Java 言語の基本

108
7:
8:
9:

s.yset(a);//s のメソッド yset を引数 a で呼び出す。//
System.out.println("a+b="+s.pl(b)); //s のメソッド yset を引数 b で//

10:
11:

}

11

://*******************////呼び出し結果を出力//}

8: s のメソッド yset(a) を呼び出し y=a とする。
スーパークラス Super1 のプログラム

0:
1:
2:
3:
4:

//***Super1.java*********//
public abstract class Super1{//abstract が付いている//

public int y; //public を付けると，どのクラスからでも呼び出しが出来る//public void yset(int x){//メソッド yset() の宣言//
y=x;

5:
}
6:
}
7://*******************//
1: クラス Super1 はサブクラスである Wa に共通機能を付けるために存在します。このように自分自身ではインスタンスを生成しないクラスを抽象くらすといい，abstract をつけて宣言します。
スーパークラスは１つしか使用できない。
サブクラス Wa のプログラム

Wa extends Super1 の宣言により，Wa は Super1 のサブクラスとして作られたことをしめす。これにより，クラス Wa はメソッド pl() の他に，スーパクラス Super1 で定義されている変数 y と，メソッド yset() を持つようになる。従って s1007.java の 8 行目で s.yset() でクラス Wa の変数 s でクラス Super1 の中のメソッド yset() が呼び出せる。

0:1://***Wa.java*********//
public class Wa extends Super1{ //extends がついている//2:
3:

public int pl(int w){
int z;

4:
5:
6:

z=w+y;
return z;}

7:}
8://*******************//
1: Wa extends Super1 は「クラス Wa はクラス Super1 のサブクラスとして作られたことをしめす。

10.8. 演習問題

109

実行方法
％ javac s1007.java (自動的に Wa.class と Super1.class が出来るはず）％ java s1007

a+b=3010.8

演習問題

1. 自分の名前を表示するプログラムを作りましょう。姓名と名前を改行する場合と、改行しない場合などいろいろ作ってみましょう。

2. s1005.java と keisan.java を参考にして，四則演算を行うプログラムを作りましょう。ここでは wa, sa, seki, waru の４つのインスタンス・メソッドを作ってプログラムを書いてください。
（あくまでもクラスとインスタンスの練習のためです。実際，４則演算をするプログラム
はｃ言語で関数を使うように書くことができる，あるいは，そうするべきです。）

3. s1006.java を参考にして，以下の seki クラスを用いて，２つの整数のかけ算を行うオブジェ
クトクラス ensyu1003.java を作ってください。
public class seki{
int y;
seki(int x){y=x;}
public int pl(int w){
int z;
z=w*y;
return z;}}
4. s1007.java を参考にして，2 つの整数の和，差，積，割り算を計算するサブクラス Wa, Sa,
Seki, Waru をスーパークラス Super1 からつくり，表示するプログラムを作りましょう。

111

第 11 章 Java の制御文・配列・関数・その他
制御文 if( ) , for( )，while() などや, 配列 a[100], 関数 func(int a, int b) など，”ほとんど”Ｃ言語と同じように使用できます。ただし，宣言のやりかたなどに多少の違いはあります。ここでは、次の章のプログラムを参考に使い方を覚えることにしておきます。詳しいことはこの授業では省略することにします。
余白スペースがあるので、キーボードからデータを入れる方法を書いておきます。

//キーボードからデータを入れる
import java.io.*;public class keybord1{
public static void main(String[] args) {try{
int a,b,c;//InputStreamReader クラスのインスタンスを生成する
InputStreamReader reader=new InputStreamReader(System.in);//BufferedReader クラスのインスタンスを生成する
BufferedReader bufreader = new BufferedReader(reader);//キー入力
System.out.print("number1 =");
String s1=bufreader.readLine();a=Integer.parseInt(s1);System.out.print("number2 =");
String s2=bufreader.readLine();b=Integer.parseInt(s2);c=a+b;
System.out.println("a+b="+c);}catch(Exception e){
System.out.println(e+"Error");}}}第 11 章 Java の制御文・配列・関数・その他

112
おまけ；

//データーをファイル (data1.txt) にセーブする
import java.io.*;public class copy{
public static void main(String[] args) {try{
int c;
double b;
PrintWriter fw =new PrintWriter( new FileWriter("data1.txt"));for(c=1; c<100; c++){b=Math.pow(c,0.5);
fw.println(c+}"\t"+b+"\t"+c+ "\t"+b);

fw.close();}catch(Exception e){
System.out.println(e+"Error");
}
}
}

113

第 12 章アプレット・グラフィックス

Java の最大の魅力はホームページ上でプログラムを実行できたり，アニメーションやグラフィッ
クをわりと簡単に出来ることです。しかもホームページ上で！
！これを実現してくれるのがアプレッ
トという形式です。まず次のプログラムを実行しましょう0:1://***s1101.java*********///*<APPLET CODE="s1101.class" WIDTH=300 HEIGHT=300>

2:
3:
4:

</APPLET> */5:6:import java.awt.Graphics;

import java.applet.*;//図形を表示するために 4,5 行目が必要//

public class s1101 extends Applet{//アプレットクラス s1101 の宣言//
public void paint(Graphics g){ //メソッド paint の宣言 //g.drawString("Hello", 100,100);

7:
8:
9:
10:
11:

}
}

12://*******************//
1,2: appletviewer コマンドでアップレットを読み込むための宣言
7: クラス s1101 はスーパクラス Applet のサブクラスである。クラス Applet には様々なメソッド 1
が java 言語に標準で用意されている。
8: 画面 (Graphics) の名前として g を宣言した。
9: 図を表示するメソッド。drewString は” ”で挟まれた文字を書くメソッド。
実行方法％ javac s1101.java％ appletviewer s1101.java
図 12.1 のような画面が現れる。
ホームページ上で図 12.1 を表示するには以下のような HTML 文書を書いて，例えば

s1101.htmlという名前で保存しておく。その後，WWW ブラウザを立ち上げ，s1101.html を読み込む。

0:1:<!-----s1101.html-------><HEAD>HTML のコメント文

1 直線を引く drawLine(), 長方形を描く drawRect() など。

第 12 章114

アプレット・グラフィックス

図 12.1: s1101 の実行結果。Hello と書いた画面が現れる。Mac OSX の場合

2:
3:</HEAD><BODY>

4:
5:<APPLET CODE="s1101.class" WIDTH=300 HEIGHT=100></APPLET>

6:</BODY>
7:<!-------------------------->
4: s1101.class を幅３００ピクセル高さ１００ピクセルで呼び込む 2 。
以下のプログラムは，％ appletviewer コマンドを用いて実行しましょう。

12.1

画面の基本

図 12.2 の様に，幅 w=６０ピクセル，高さ h=３０ピクセルの長方形を，左端が x=２０ピクセ
ル，上端が y=３０ピクセルの位置にくるこうに表示するプログラムは以下のようになります。

図 12.2: 表示領域の高さは９０ピクセル，幅１００ピクセル。

0:
1:
2:
3:
4:

//***s1102.java*********///*<APPLET CODE="s1102.class" WIDTH=100 HEIGHT=90></APPLET> */import java.applet.*;import java.awt.Graphics;
2 ピクセルはパソコンの画面の単位。現在のパソコンの画面の大きさはおそらく，１０２４ピクセル（幅）×７６８ピク

セル（高さ）である。アプレットの表示サイズはそれよりも小さくしておくのが安全です。

12.2. 正方形・長方形
5:
6:
7:

public class s1102 extends Applet{public void paint (Graphics g){

8:
9:g.drawRect(20,30,60,30);
}

10:
11:

115

}
//*******************//

1: アプレットの表示サイズは大きめに指定するのが安全。
8: g.drawRect(x,y,w,h); 長方形を描くメソッド。引数は x,y,w,h の順。図 12.2 を参照。
アプレットを表示するための決まりは/*<APPLET CODE="プログラムの名前.class" WIDTH=300 HEIGHT=300></APPLET> */import java.applet.*;import java.awt.Graphics;
public class プログラムの名前 extends Applet{public void paint (Graphics g){
ここにプログラムを書く

}
}

12.2

正方形・長方形

正方形や長方形を表示するには

g.drawRect(左端座標 x, 上端座標 y, 幅 w, 高さ h)を使います。１辺が１００ピクセルの正方形と，幅が１００ピクセルで高さ８０ピクセルの長方形を描くプログラムを作りましょう。

0:1://*****s1103.java***** ///*<APPLET CODE="s1103.class" WIDTH=300 HEIGHT=300>

2:
3:</APPLET> */import java.applet.*;4:
5:

import java.awt.Graphics;

第 12 章

116
6:
7:
8:

public class s1103 extends Applet{public void paint (Graphics g){
g.drawRect(50,50,100,100);9:10:g.drawRect(200,50,80,100);

11:
12:

アプレット・グラフィックス}}//*******************//

8: 一辺が１００ピクセルの正方形は g.drawRect(左端 x, 上端 y,100,100); で描けます。
9: 幅が１００ピクセルで高さ８０ピクセルの長方形は g.drawRect(左端 x, 上端 y,80,100); で描けます。
あとは２つの図が重ならないように x,y の値を考えればよい。

12.3

円・楕円・直線円を表示するには

g.drawOval(左端座標 x, 上端座標 y, 幅 w, 高さ h)を使います。図 12.3 の様に点 (x0 , y0 ) を中心とする半径 r の円を描く場合，左端座標は x = x0 − r,
上端座標は y = y0 − r, 幅は w = 2r, 高さ h = 2r となります。この計算式を関数を使って汎用にしておけば，便利です。
次の例は，点 (100, 100) を中心とする半径７０ピクセルの円を描くプログラムです。circle1() 関数を定義して，中心と半径を与えています。

図 12.3: 点 (x0 , y0 ) を中心とする半径 r の円を描く

0://s1104.java //

1:
2:
3:

/*<APPLET CODE="s1104.class" WIDTH=300 HEIGHT=300></APPLET> */import java.applet.*;4:
5:

import java.awt.Graphics;

12.4. 多角形と折れ線グラフ
6:
7:
8:

117

public class s1104 extends Applet{public void paint (Graphics g){
circle1(g,100,100,70);

9;
10:

}
//中心と半径を与えた時に円を描く関数

11:
12:
13:

static void circle1(Graphics g, int x0, int y0, int r){
int xa, ya, w, h;
xa=x0-r;14:
15:

ya=y0-r;
w=2*r;

16:
17:
18:

h=2*r;
g.drawOval(xa,ya,w,h);
}

19:
20:

}

直線を引く命令はg.drawLine(始点の横座標 x0, 始点の縦座標 y0, 終点の横座標 x1, 終点の縦座標 y1)です。次の例は 300 × 300 ピクセルの画面上に x 軸と y 軸を引くプログラムを示します。画面の左上の画面座標は（０，０），右下の画面座標が（３００，３００）に対応している。

0:
1:
2:

//s1105.java ///*<APPLET CODE="s1105.class" WIDTH=300 HEIGHT=300></APPLET> */3:4:import java.applet.*;import java.awt.Graphics;

5:
6:
7:

public class s1105 extends Applet{public void paint (Graphics g){

8:
9:g.drawLine(0,150,300,150);
g.drawLine(150,0,150,300);

10:
11:

12.4

}
}

多角形と折れ線グラフ

多角形の頂点 P0 , P1 , P2 , . . ., Pn−1 の座標が (x0 , y0 ), (x1 , y0 ), (x2 , y2 ), . . ., (xn−1 , yn−1 ) の時，

x 軸の座標の配列 x = (x0 , x1 , x2 , . . . , xn−1 ) と y 軸の座標の配列 y = (y0 , y1 , y2 , . . . , yn−1 ) を用いて，多角形を描くことが出来ます。

第 12 章118

アプレット・グラフィックス

g.drawPolygon(x 軸の座標の配列 x, y 軸の座標の配列 y, 点の数)
これは，点 P0 , P1 , P2 , . . ., Pn−1 を順に直線で結んで，最後に点 P0 と Pn−1 を結びます。点の数
は頂点の数を示します。点の数を多くすれば滑らかな曲線になるはずです。

g.drawPolyline(x 軸の座標の配列 x, y 軸の座標の配列 y, 点の数)
これは，点 P0 , P1 , P2 , . . ., Pn−1 を順に直線で結びます。
次の例は，正多角形を描くプログラムです。頂点の個数 n, 半径 r, 中心の座標 (x0, y0) を与えた
時に，正 n 角形を描く関数

polygon1(Graphics g, int n, int r, int x0, int y0)を関数で作ってあります。

0://s1106.java //1:
2:/*<APPLET CODE="s1106.class" WIDTH=500 HEIGHT=500></APPLET> */

3:
4:
5:

import java.applet.*;import java.awt.Graphics;

6:
7:

public class s1106 extends Applet{public void paint (Graphics g){

8:
9:
10:

polygon1(g,3,50,100,100);
polygon1(g,4,50,250,100);
polygon1(g,5,50,400,100);

11:
12:

polygon1(g,6,50,100,250);
polygon1(g,7,50,250,250);

13:
14:
15:

polygon1(g,8,50,400,250);}//*************************************************

16;//頂点の個数 n, 半径 r, 中心の座標 (x0,y0) を与えた時に，正 n 角形を描く関数
polygon1(Graphics g, int n, int r, int x0, int y0)

17:
18:
19:

//**************************************************
static void polygon1(Graphics g, int n, int r, int x0, int y0){int[] x=new int[121];//配列 x の定義。要素数を１２１。
//配列 y の定義

20:
21:

int[] y=new int[121];
int i;

22:
23:
24:

double dt=2.0*Math.PI/(double)n;for(i=0; i<n; i++){x[i]=x0+(int)(r*Math.sin((double)i*dt)); //頂点の x 座標を計算して配列

にいれる。

25:
にいれる。

y[i]=y0-(int)(r*Math.cos((double)i*dt));//頂点の y 座標を計算して配列

12.5. 関数を描く
26:
27:
28:
29:

119

}
g.drawPolygon(x,y,n);}//正 n 角形を描く関数

}

関数を描く

12.5

関数 f (x) = x2 のグラフを x の値を −1 から 1 までで，描くプログラムを示します。例では５００×５００ピクセルの画面上の中心（２５０，２５０）を関数座標の原点 (0,0) に，画面上の右下の座標（５００，５００）を関数座標の (1,-1) に対応させています。

0://s1107.java //

1:
2:
3:

/*<APPLET CODE="s1107.class" WIDTH=500 HEIGHT=500></APPLET> */import java.applet.*;4:
5:

import java.awt.Graphics;

6:
7:
8:

public class s1107 extends Applet{public void paint (Graphics g){
int[] xx=new int[501]; //x 座標の配列の宣言

9:10:int[] yy=new int[501]; //y 座標の配列の宣言
double x, y;//関数の変数 x とその計算値 y ：関数座標 (x,y)

11:
12:
13:

int i, j;for(i=0;i<=500;i++){//501 点のｘ座標点を用意

14:
15:
16:

x=(double)(i)/250.0-1.0;//x の値
y=Math.pow(x,2.0);//x の２乗の計算
j=250-(int)(250.0*y);// 画面座標に変換

17:
18:

xx[i]=i;yy[i]=j;

19:
20:
21:
22:
23:

}

24:

}

の宣言

//画面上の座標 (i,j) の宣言//}
g.drawPolyline(xx,yy,501);
g.drawLine(0,250,500,250);//曲線を描く// ｘ軸g.drawLine(250,0,250,500);//y 軸

1: 画面のサイズは１行目で行っている。
14: x = ai + b で i = 250 の時 x = 0,i = 500 の時 x = 1 を満たすように定数 a, b を決める。
15: java 言語での数学関数は，c 言語の標準数学関数に Math. を付けて呼び出せばよい。

第 12 章120

アプレット・グラフィックス

16: y = cj + d で j = 250 の時 y = 0,j = 500 の時 y = −1 を満たすように定数 c, d を決める。
描きたい画面の大きさや関数の値によって，定数 a,b,c,d を調節する必要がある。画面座標と関数
座標を変換する関数を作っておくとよい。

12.6

アニメーション

Java では画像データを読み込んだり，それを動画（アニメーション）にしたり，ボタンを付けたり，様々な関数（メソッド）が用意されています。
ここでは，その中の一つであるスライドショーについて学びましょう。スライドショーとは，何枚かの画像を一定時間間隔で切り替えて順に見せていく方法です。
「一定時間休む」ために Java では sleep() という関数があり，スレッド (thread) という方式で処理します。

• スレッドを使う時はアプレットの最初を次のように書きます。
public class 名前
Threadextends Applet implementsth;public void start(){th=new Thread(this);th.start();}• 処理の流れは
public void run(){}の中に書きます，• 「一定時間休む」はtry{
Thread.sleep(10);}catch(InterruptedException e){}と書きます。

Runnable{12.6. アニメーション

121• 休止時間はミリセカンド (1/1000 秒）単位の整数値で指定します。
例）1 秒休む Thread.sleep(1000);
5 秒休む Thread.sleep(5000);
0.1 秒休む

Thread.sleep(100);

ここでは，2 つの円を左右から動かすプログラムを考えましょう。まず，（１）円を描く。
（２）それを消して少し右に動かし円を描く（３）それを消して，さらに少し右に動かし円を描く。これを短い時間間隔で繰り返していけば，円が右に動いているように見えるはずです。

0:1://s1108.java ////２つの円を左右から走らせる

2:
3:
4:

/*<APPLET CODE="s1108.class" WIDTH=350 HEIGHT=300></APPLET> */import java.applet.*;5:
6:

import java.awt.Graphics;
import java.awt.Color; //色指定の関数を呼び出す

7:
8:
9:

public class s1108 extends Applet implements Runnable{
int x,x1;

10:
11:

Thread th;

12:
13:
14:

public void start(){th=new Thread(this);th.start();

15:
16:

}

17:
18:
19:

public void run(){while(true){
for(x=0; x<280; x++){//赤玉を右へ動かす

20:
21:

repaint();
try{

22:
23:
24:

Thread.sleep(10);}catch(InterruptedException e){

25:
26:

}
}

27:
28:
29:

for(x=280; x>0; x--){repaint();
try{

30:

//赤玉を左へ動かす

Thread.sleep(10);第 12 章

122
31:
32:
33:

アプレット・グラフィックス}
catch(InterruptedException e){
}

34:
35:

}
} /*** while()***/

36:
37:
38:

}
public void paint(Graphics g){
x1=280-x;

39:
40:

g.drawLine(0,150,300,150);
g.setColor(Color.red); //赤を指定

41:
42:
43:

g.fillOval(x,130,20,20); //円を上の色で塗る
g.setColor(Color.blue);//青を指定g.fillOval(x1,130,20,20);//円を上の色で塗る

44:
45:

}
}

12.7

演習問題

1. 上のプログラムのうちから２つ選んで，それぞれの HTML 文書を書いて，WWW ブラウザからプログラムを実行してみましょう。
2.

小さい正方形を３行３列に規則正しく並べて表示してください。

3.

１０個の円を水平に規則正しく並べて表示してください。

4.

s1106.java を参考にして，drawPolyline() を使って多角形を描いてください。

5.

6.

s1107.java を参考にして，f (x) = x3 のグラフを x の範囲を −2 から 2 までで描いてください。
s1108.java を参考にして，いろいろな速度で２つの円を動かしてみよう。また２つの円が衝突したら跳ね返るようにしてみましょう。

7. s1108.java を参考にして，テーマは何でもいいので，アニメーションを作ってください。WWW
ブラウザからプログラムを実行してみましょう。

123

第 13 章高分子のランダムウォーク

高分子はｎ個のモノマー（セグメントとよぶ）がつながったグニャグニャした分子です。タン
パク質や DNA などの最も簡単なモデルでもあります。熱運動によって各セグメントが，でたらめ（ランダム）に動く様子をアニメーションで実現しましょう。ただし各セグメントはつながっているという制限がついています。

13.1

正方形の壁で囲まれた２次元領域をランダムに動く高分子

次の例は，正方形の壁で囲まれた２次元領域をランダムに動く高分子（ｎ個のセグメントがつながった分子）のランダムウォークを示すプログラム例です。ただし，壁を通り抜けてはいけないという条件がついています。
プログラムの構成（１）まず高分子を平面上に置きます（初期設定）
・・polymer init() 関数（２）乱数を引いてｎ個のセグメントの内の p 番目のセグメントを選び動かす。動かし方は，ボンド長を一定に保って回転させる。この反転させた座標をｐ番目のセグメントの新座標 (x new,

y new) とする。
・・・polymer() 関数（３）新座標が壁を越えたら新座標を元の座標に戻す。
（４）（２）（３）の操作をｎ回繰り返したものを１ステップとする。
（５）新座標を描画する。
・・・paint() 関数（６）（２）-（５）を while() 文で無限に繰り返す。

//rwalk2.java ////反射壁の場合での高分子のランダムウォーク/*<APPLET CODE="rwalk2.class" WIDTH=550 HEIGHT=550></APPLET> */import java.applet.*;import java.awt.Graphics;
import java.awt.Color;//************************
public class rwalk2 extends Applet implements Runnable{
double PI=3.141592;
double b=11.0; //ボンドの長さ
int r=5;//球の半径第 13 章124

(M, 0)(0, 0)b2r
n=3(0, M)( M, M)y

図 13.1: 高分子を配置する

int n=3;//セグメント数

int M =500;//正方形の一辺（ピクセル）
int[] x= new int[n+3]; //配列 x[0], x[1],,,
int[] y=new int [n+3];//配列 y[0], y[1],,,
int i,p, t, xp, yp, xp_new, yp_new;double cos_theta,sin_theta, the;//************************
Thread th;
public void start(){th=new Thread(this);th.start();}//************************
public void run(){x[1]=200;y[1]=200;//1 番目のセグメントの初期座標

polymer_init();//高分子の初期座標設定
while(true){//１ステップは while の終わりまで。
polymer();//各セグメントを動かす//******描画する部分***************
repaint(); //***paint() 関数を呼ぶ*******
try{ Thread.sleep(50); }catch(InterruptedException e){ }//*********************************} //while() の終わり

高分子のランダムウォークx13.1. 正方形の壁で囲まれた２次元領域をランダムに動く高分子} //run() 関数の終わり//************************************//******高分子を描く******************//************************************
public void paint(Graphics g){
g.setColor(Color.blue); ///青色の指定
g.drawRect((int)b,(int)b,M, M);
g.setColor(Color.red);//赤色の指定g.fillOval(x[n]-r,for(i=1;i<n; i++){y[n]-r,

2*r,

2*r);

g.fillOval(x[i]-r,
y[i]-r, 2*r, 2*r);g.drawLine(x[i], y[i], x[i+1], y[i+1]);}//円を塗る//線を引く}//**********************************//**** public void polymer() *********//**********************************
public void polymer(){for(i=1; i<=n; i++){//乱数を引いてｐ番目のセグメントを動かす
p=(int)(((double)n+1.0)*Math.random());
if(p==0){
xp_new=0;//ゴミ

yp_new=0;}
else if(p==1){
theta=2.0*PI*Math.random();
xp_new=x[2]+(int)(b*Math.cos(theta));yp_new=y[2]+(int)(b*Math.sin(theta));}else if(p==n){theta=2.0*PI*Math.random();
xp_new=x[n-1]+(int)(b*Math.cos(theta));yp_new=y[n-1]+(int)(b*Math.sin(theta));}else {
cos_theta=(x[p+1]-x[p])/b;
sin_theta=(y[p+1]-y[p])/b;

125

第 13 章126
xp_new=x[p-1]+(int)(b*cos_theta);
yp_new=y[p-1]+(int)(b*sin_theta);}
if(xp_new<(int)b || xp_new>M-(int)b ||yp_new<(int)b || yp_new>M-(int)b
xp_new=x[p];yp_new=y[p];){}x[p]=xp_new;y[p]=yp_new;}}//**********************************//**** public void polymer_init() **初期座標//**********************************
public void polymer_init(){for(i=1;i<=n; i++){theta=2.0*PI*Math.random();x[i+1]=x[i]+(int)(b*Math.cos(theta));y[i+1]=y[i]+(int)(b*Math.sin(theta));while(true){if( x[i+1]<(int)b || x[i+1]>M-(int)by[i+1]<(int)b || y[i+1]>M-(int)b||){

theta=2.0*PI*Math.random();x[i+1]=x[i]+(int)(b*Math.cos(theta));y[i+1]=y[i]+(int)(b*Math.sin(theta));}else{break; }}}}//************************} //end of class

高分子のランダムウォーク

13.2. 演習問題

127//** end of program ******//

13.2

演習問題

1. 上のプログラムの HTML 文書を書いて，WWW ブラウザからプログラムを実行してみましょう。

2. セグメント数を変えてプログラムを実行してみましょう。たとえば，n = 200.
3. 両端を黒丸で表示するようにしてください。
4. 壁を取り外したプログラムを書いて実行してください。
5. 応用問題（興味があれば，来年私の研究室でその後をやりませんか？）(a) 各ステップ (s) 毎の，高分子の両端の距離の２乗 (Rs2 ) を求めて，１ 0 ０ステップ毎に、ステップ数と、Rs2 の値をファイル (data1.txt) にセーブしてください。s = smax ステップで実験を終了して平均値

max
1 ∑
R2
s s=1 ss

< R2 >=を求めてください。smax = 10000 でまずはやってみましょう。高分子のセグメント数

n を大きくしていくと、< R2 >= b2 n の関係式が成り立つはずであるが、確かめてみてください。

(b) 周期的境界条件を取り入れたプログラムにしてください。
(c) 各セグメントが重ならないように，プログラムを修正してください。