PDF version

計算機通論 IIa
2000 年度版
C 言語の基本
2000 年 度 後 期
埼 玉 大 学 理 学 部
計 算 機 通 論 IIa
1
計算機通論 IIa
2000 年度版
Table of Contents
C 言語の基本................................
言語の基本................................................................
................................................................................................
...........................................................................................
........................................................... 1
TABLE OF CONTENTS ................................................................
................................................................................................
........................................................................
........................................ 2
LIST OF FIGURES
FIGURES................................
................................................................
................................................................................................
................................................................................
................................................ 4
1.
C 言語の基本 ................................................................
................................................................................................
................................................................................
................................................ 5
1.1 C 言語で使用できる文字(文字セット) ....................................................................... 5
1.2 区切り記号......................................................................................................................... 5
1.2.1 空白文字 ...................................................................................................................... 6
1.2.2 演算子 .......................................................................................................................... 6
1.2.3 その他の記号 ............................................................................................................... 6
1.3 名前のつけ方..................................................................................................................... 7
1.4 プログラムの書き方(プログラミング・スタイル)....................................................... 8
2.
データの種類
データの種類 ................................................................
................................................................................................
..............................................................................
.............................................. 10
2.1 データ型 .......................................................................................................................... 10
2.1.1 整数型 ....................................................................................................................... 10
2.1.2 浮動小数点数型......................................................................................................... 11
2.1.3 文字型 ....................................................................................................................... 11
2.2 記憶クラス....................................................................................................................... 11
2.2.1 自動変数(auto 変数)............................................................................................. 11
2.2.2 静的変数(static 変数)........................................................................................... 13
2.3 変数の有効範囲 ............................................................................................................... 15
2.4 定数 ................................................................................................................................. 16
2.4.1 整数定数.................................................................................................................... 16
2.4.2 浮動小数点数表現..................................................................................................... 17
2.4.3 文字表現.................................................................................................................... 18
2.4.4 文字列表現................................................................................................................. 19
3.
C 言語の演算子................................
言語の演算子 ................................................................
................................................................................................
...........................................................................
........................................... 20
3.1 演算子の種類................................................................................................................... 20
3.2 算術演算子....................................................................................................................... 20
3.3 関係演算子....................................................................................................................... 22
3.4 論理演算子....................................................................................................................... 23
3.5 インクリメント演算子とデクリメント演算子................................................................ 24
3.6 代入演算子....................................................................................................................... 26
3.7 キャスト演算子 ............................................................................................................... 27
3.8 順次演算子....................................................................................................................... 28
3.9 条件演算子....................................................................................................................... 28
3.10 演算子の優先順位.......................................................................................................... 29
2
計算機通論 IIa
2000 年度版
処理の流れの制御 ................................................................
................................................................................................
.......................................................................
....................................... 31
4.
4.1 制御のための文 ............................................................................................................... 31
4.2 IF 文............................................................................................................................... 31
4.3 SWITCH 文......................................................................................................................... 36
4.4 WHILE 文........................................................................................................................... 38
4.5 FOR 文............................................................................................................................... 40
4.6 DO∼WHILE 文 .................................................................................................................. 43
4.7 GOTO 文 ............................................................................................................................ 45
基本的な入出力 ................................................................
................................................................................................
..........................................................................
.......................................... 46
5.
5.1 標準入出力関数 ............................................................................................................... 46
5.2 SCANF 関数 ....................................................................................................................... 46
5.3 PRINTF 関数 ..................................................................................................................... 49
配列について ................................................................
................................................................................................
..............................................................................
.............................................. 52
6.
6.1
6.2
6.3
6.4
7.
配列とは .......................................................................................................................... 52
配列の宣言....................................................................................................................... 52
配列の初期化................................................................................................................... 54
配列の参照....................................................................................................................... 56
ポインタについて ................................................................
................................................................................................
.......................................................................
....................................... 59
7.1 ポインタを用いたアドレス演算子と間接演算子 ............................................................ 59
7.2 ポインタを用いた配列の参照 ......................................................................................... 61
8.
関数について ................................................................
................................................................................................
..............................................................................
.............................................. 66
8.1 C言語での関数の書き方................................................................................................. 66
8.2 関数間のデータの受渡し................................................................................................. 67
8.2.1 関数の引数として変数の値を直接渡す方法............................................................. 67
8.2.2 関数の引数として変数のアドレス(pointer)を渡す方法...................................... 69
3
計算機通論 IIa
2000 年度版
List of Figures
FIGURE 2-1
FIGURE 2-2
FIGURE 4-1
FIGURE 4-2
FIGURE 4-3
FIGURE 4-4
FIGURE 4-5
FIGURE 4-6
FIGURE 4-7
FIGURE 4-8
FIGURE 7-1
FIGURE 7-2
FIGURE 7-3
FIGURE 7-4
FIGURE 7-5
FIGURE 8-1
FIGURE 8-2
FIGURE 8-3
FIGURE 8-4
スタックの原理 ......................................................................................................... 12
自動変数の範囲..................................................................................................... 12
IF 文 ...................................................................................................................... 32
IF-ELSE 文 ............................................................................................................ 32
IF 文と ELSE の関係 .................................................................................................. 35
SWITCH 文 ............................................................................................................... 38
BREAK と CONTINUE 文 .............................................................................................. 40
FOR 文の BREAK と CONTINUE .................................................................................... 42
DO~WHILE 文.......................................................................................................... 44
DO~WHILE 文の BREAK と CONTINUE ........................................................................... 44
アドレスとデータ概念 ......................................................................................... 59
変数 A と B のアドレスとデータ............................................................................. 60
変数 A と B の関連................................................................................................... 60
A と B 変数の領域 ................................................................................................. 61
アドレスの加算 .................................................................................................... 61
関数概念 ............................................................................................................... 67
関数間のデータの受渡し...................................................................................... 67
関数の引数として変数の値を直接渡す方法 ........................................................ 68
関数の引数として変数のアドレス(POINTER)を渡す方法 ............................... 69
4
計算機通論 IIa
2000 年度版
1. C 言語の基本
この章では、C 言語でプログラムを書くときに必要な基本的な事項について説明します。
1.1 C 言語で使用できる文字(文字セット)
この節では、C 言語で使用できる文字の種類ついて説明します。
C 言語で使用できる文字には、英文字(大・小)、数字、空白文字、特殊文字の4種類があります。
(1)
英大小文字(52
英大小文字(52 種類)
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
A b c d e f g h i j k l m n o p q r s t u v w x y z
(2)
数字(10
数字(10 種類)
0 1 2 3 4 5 6 7 8 9
(3)
空白文字(3
空白文字(3 種類)
スペース、タブ、改行
(4)
特殊文字(30
特殊文字(30 種類)
,
;
?
“…”
(
{
[
<
/
|
+
#
_
&
=
コンマ
セミコロン
クウェッションマーク
ダブルクオート
ダブルクオート
左カッコ
左大カッコ
左中カッコ
角カッコ
スラッシュ
縦線
プラス記号
シャープ
アンダースコア
アンパーサンド
イコール記号
.
:
!
‘…’
)
}
]
>
\
~
%
^
*
`
ピリオド
コロン
びっくりマーク
シングルクオート
右カッコ
右大カッコ
右中カッコ
角カッコ
バックスラッシュ
ティルド
マイナス記号
パーセント記号
カレット
アスタリクス
アクセント
1.2 区切り記号
ここでは、変数名と変数名の区切りや文の区切りを示すために使用される区切り記号について説明します。C
言語では、空白文字や演算子などが区切り記号となります。
5
計算機通論 IIa
2000 年度版
1.2.1 空白文字
スペース、タブなどの空白文字は区切り記号となります。
(i) abc 口 xyz
abc と xyz に分けられる
区切り記号はスペース(口)
(ii) name [TAB] test
name と test に分けられる
区切り記号はタブ [TAB]
本書では、タブ記号を [TAB]、スペースを □ と書きます。スペースの場合は単に空けて書きますが、プログラ
ムを説明する上で重要な意味をもつようなとき、また注意が必要となるときにのみ □ と書くことにします。
1.2.2 演算子
演算子も区切り記号になります。C 言語の演算子には、加算や減算などの算術演算子、2 つの数の大小関
係などを調べる論理演算子などがあります。これらの演算子については、第3章で詳しく説明します。ここでは、
いろいろな演算子も区切り記号になることを理解してください。
(i) abc + xyz
abc と xyz に分けられる
区切り記号は、プラス演算子(+)
(ii) name > test
name と test に分けられる
区切り記号は、比較演算子(>)
1.2.3 その他の記号
C 言語では、以下に示す記号も区切り記号となります。
記号
;(セミコロン)
:(コロン)
{}(大カッコ)
()(カッコ)
[](中カッコ)
〈〉(角カッコ)
‘’(シングルクォーテーション)
“”(ダブルクォーテーション)
¥(バックスラッシュ)
%(パーセント)
意味
1 つの実行文
ラベル
複文
関数の引数
配列の要素数
#include 文のファイル指定
1 文字
文字列
制御文字
出力書式指定文字
6
計算機通論 IIa
2000 年度版
1.3 名前のつけ方
ここでは、変数や関数に名前をつけるときの規則について説明します。
(1) 変数や関数の名前に使用できる文字は、英文字(大・小)、数字、アンダースコア(_)の計 63 文字です。
これらの文字を自由に組み合わせて名前をつけることになります。ただし、最初の文字は英文字またはア
ンダースコアでなければなりません。
〈正しい名前〉
name
test_1 _abc
〈間違った名前〉
123name
test-
-1 *test
(2) 名前はいくら長いものをつけてもかまいませんが、通常有効な文字数 8 文字目までとなります。ただし、ア
ンダースコア 1 文字だけの名前はつけることはできません。
(i) nametest_1234567
(ii) nametest_abc
(i)と(ii)は、おなじ名前(nametest)となります
(3) C 言語では、英大文字と英小文字は明確に区別されます。通常、C 言語では英小文字を使ってプログ
ラムを書きます。英大文字は特殊な意味に使うことが多いようです。
(i) name
(ii) NAME
(i)と(ii)は、異なる名前となります
(4) C 言語では、他のプログラミング言語と同様に、あらかじめその使い方が決められている名前があります。こ
れらを予約語(キーワード)といいます。この予約語と同じ名前を変数名や関数名に使用することはできま
せん。通常、予約語は 28 個あり、それらを機能別に示したのが表 1.1 です。なお、この予約語は C コン
パイラによって多少異なりますので、使用するコンパイラのマニュアルで確認してください。
データの型
と
記憶クラス
表 1.1 C 言語の予約語
予約語
意味
auto
自動変数の宣言
char
文字型変数の宣言
double
倍精度浮動小数点型変数の宣言
extern
外部変数の宣言
float
浮動小数点型変数の宣言
int
整数型の宣言
long
倍精度整数型の宣言
register
レジスタ変数の宣言
short
短い整数型の宣言
static
静的変数の宣言
7
計算機通論 IIa
2000 年度版
struct
typedef
union
unsigned
break
case
continue
default
do
else
for
goto
if
return
switch
while
entry
sizeof
制御
その他
構造体の宣言
型の定義
共用体の宣言
符号なし整数の宣言
ループからの飛びだし
switch~case の定義
次の繰り返しへ戻る
switch~default の定義
do ループ
if~else
for ループ
無条件分岐
条件判定
関数からの戻り
多条件分岐
while ループ
意味なし
データ型の長さ
1.4 プログラムの書き方(プログラミング・スタイル)
ここでは、C 言語のプログラムの書き方(スタイル)について説明します。プログラムを書くときの約東は次に示す
4 つだけで、他のプログラミング言語に比べると少ないことがわかります。つまり、プログラマーは自分のスタイルで
プログラムを書くことができるわけです。しかし、あまり奇抜な書き方は関心しません。
(1) 文はセミコロン( ; )で終わらなければなりません。
int name ;
printf(
("%c¥
¥n", name)
);
(2) C 言語では、行を意識することなくプログラムを書くことができます。つまり、1 行に複数の文を書いたり、あ
るいは 1 つの文を複数の行に分けて書くこともできます。
1 行に 2 つの文
int
name, test ; printf(
("%c¥
¥n", name)
) ;
1 つの文を複数の行
int
name ,
test ;
printf
("%c¥
¥n" ,
name)
) ;
ただし、次のように名前などの途中で行を変えることはできません。
8
計算機通論 IIa
2000 年度版
int na
me , test ;
(3) スペースやタブなどの空白文字を自由に使って、プログラムをどの位置からでも書き始めることができます。
(i) [TAB] int name [TAB] , □□□ test ;
(ii) int sw1 [TAB] , □□ table [ ] ;
(4) 注釈(コメント)
注釈は、必ず使用しなけれぱならないものではありません。しかし、プログラムを理解しやすくすることを考
えるとコメントを上手に使用することをすすめます。C 言語でコメントを書く場合の唯一の規則は、コメント
の内容を /* と */ で囲むことです。その範囲が複数の行になってもかまいません。
int test;
; /* test switch */
/*
test switch
*/
int test ;
最後に、空白文字を使って書いたプログラムを紹介します。
このプログラムは、1+2 を求めるためのプログラムです。プログラムの内容については、特に説明しません。ここで
は、空白文字を使うことによって見やすく、そして理解しやすいプログラムを書くことができることを知ってくださ
い。
(見やすいプログラミング・スタイルの例)
/*
合計を求める
*/
#include <stdio.h>
main()
{
int a,
,b,
,c ;
a = 1 ;
b = 2 ;
c = a + b ; /* goukei */
printf (“%d” , c) ;
}
(見にくいプログラミング・スタイルの例)
#include <stdio.h>
main() { int a,
,b,
,c ; a = 1 ; b = 2 ; c = a + b ;
printf (“%d” , c) ; }
9
計算機通論 IIa
2000 年度版
2. データの種類
C 言語で扱うことができるデータの種類について説明します。C 言語でプログラムを書く場合、そのプログラムで
使用する変数や定数は、あらかじめ宣言しておかなければなりません。変数とは、プログラムが実行中その値
が変わることができるものです。一方、定数とは、プログラム実行中その値が変わらないものです。
変数を宣言する場合、その変数の性格(属性)を決めなければなりません。その属性には、つぎに示す 3 つが
あります。
1.
2.
3.
その変数が表現できる数の大きさ(データ型)
その変数がメモリのどの位置に置かれるか(記憶クラス)
その変数がプログラムのどの範囲まで使用できるか(有効範囲)
C 言語では変数を宣言する場合、これらの属性をつぎのように指定します。
[記憶クラス]データ型
変数名 ;
[]省略可能
変数が宣言されたプログラム内の位置によって、その変数が使用できる範囲(変数の有効範囲)が決定され
ますので、変数の有効範囲は特に宣言しません。データ型、記憶クラス、有効範囲の順に説明していくことに
しましょう。
2.1 データ型
C 言語では、データの型として、基本的には整数型と浮動小数点数型(実数型)があります。さらに文字を扱
うための文字型も用意されています。
2.1.1 整数型
整数は 1、 2、 -1、 -2 などの数で、小数部分をもたないものです。C 言語では表現する数の大きさによって、
つぎに示す 3 つの種類の整数を宣言することができます。
宣言方法
(i) short
(ii) long
(iii) int
(iv) unsigned short
(v) unsigned long
(vi) unsigned int
意味
通常 2 バイトで表現し、-32768 ~ +32767 の範囲の数を表わすこと方言
できます。
通常 4 バイトで表現し、-2147483648 ~ +2147483647 の範囲の数を表
わすことができます。
表現できる数の範囲は CPU のアーキテクチャに依存しますつまり、16 ビット
系の CPU であれば 2 バイト(short と同じ)で表現し、32 ビット系の CPU で
あれば 4 バイト(long と同じ)で表現します。
0~65535 の範囲の数を表わすことができます。
0~4294967295 の範囲の数を表わすことができます。
16 ビット系の CPU では(iv)と同じ、32 ビット系の CPU では(v)と同じです。
10
計算機通論 IIa
2000 年度版
(i)、(ii)、(iii)は符号(プラス、マイナス)のついた数を表わしましたが、C 言語では符号なしの整数も宣言するこ
とができます。符号なしの整数を宣言するには、つぎのように(i)、(ii)、(iii)で使用した宣言文の前に(iv)、(v)、
(vi)のように unsigned をつけます。
2.1.2 浮動小数点数型
浮動小数点数型を使開すると 0.00000123 といった小数点以下の小さな数や、10 の 20 乗といった大きな数
を表現することができます。C 言語では、表現する数の大きさによって、次の 2 つの種類の浮動小数点を表現
することができます。
表現方法
float
double
意味
通常 4 バイトで表現し、-10E38~ +10E38 の範囲の数を表わすことができます
通常 8 バイトで表現し、-10E306~ +10E306 の範囲の数を表わすことができます
2.1.3 文字型
文字型は a や * などの 1 文字を表現するためのものです。C 言語では、つぎのように書きます。
宣言方法
char
意味
1 バイトで表現します
C 言語では、つぎに示すように 1 文字をシングルクォーテーションで囲んで表現します。
char name = 'a' ;
これは、変数 name に文字 a をセットしています。
この文字型変数 name を printf 関数で文字として出力してみましょう。
printf(
("%
%c", name)
);
このように、printf 関数を書くと、a と出力されます。
2.2 記憶クラス
変数をメモリのどの位置に割り当てるかを決めるのが記憶クラスです。C 言語には、記憶クラスとして自動変数
(auto)、静的変数(static)、レジスタ変数(register)、外部変数(extern)の 4 つが用意されていますが、こ
こでは自動変数と静的変数についてのみ説明します。
2.2.1 自動変数(auto 変数)
自動変数は、自動変数が宣言されている関数やブロックの処理が始まるとメモリ内にセットされ、その実行が
終わるとメモリから消えてなくなります。つまり、関数やブロック内で変数の独立性を保つことができるわけです。
自動変数はつぎのように指定します。
auto int data ;
これは整数型の変数 data を自動変数として宣言することを意味しています。なお、auto を省略すると自動
11
計算機通論 IIa
2000 年度版
変数となります。この自動変数は、変数の値を蓄えておくための共用の記憶領域(スタック領域)に格納され
ます。つまり、自動変数を宣言するとメモリの有効利用がはかれることになります。スタックの原理を図 2.1 に示
します。
いま、変数 a と変数 b が自動変数として変数 a、変数 b の順に宣言されると、変数 a、変数 b がスタッ
ク領域に登録されます。このスタック領域に登録された変数は、変数 b、変数 a の順(LIFO、ラーストインフ
ァーストアウト)に参照することになります。
Figure 2-1 スタックの原理
それでは、自動変数の宣言から消滅までの様子を Figure 2-2 に示すプログラムを使いながら説明すること
にします。
Figure 2-2 自動変数の範囲
このプログラムは、4 つのブロック(実行単位)からできています。そして、それぞれのブロックが始まるところで変数
を宣言するようにしています。つまり、ブロック 1 では変数 a、ブロック 2 では変数 b、ブロック 3 では変数 c、ブ
ロック 4 では変数 d を宣言しています。プログラムは各ブロック内で使用できる変数のみを出力しています。こ
のプログラムを実行するとつぎのようになります。
a=
=100 b=
=200 c=
=300 d=
=400
a=
=100 b=
=200 c=
=300
a=
=100 b=
=200
a=
=100
12
計算機通論 IIa
2000 年度版
この結果をもとに、変数 a、b、c、d の宣言と参照の関連を図 2.3 に示します。この図は、ブロック開始から終
了までの過程で、宣言した変数と参照できる変数の関係を示しています。図中の a、b、c、d は宣言した変数、
a、b、c、d となっているのは参照可能な変数を示しています。ブロックの開始のところでは、ブロックが 1 から 4 と
順に形成されていく過程で使用できる変数も、a、b、c、d と増えていくことがわかります。つぎにブロックの終了
を見てみましょう。ブロック 4 が終了するとブロック 4 で宣言していた変数 d は消滅してしまいますから、ブロック 3
では変数 d は参照することができません。同じように、ブロック 3 が終了すると変数 c が消滅してしまいますから、
ブロック 2 では変数 c を参照することができません。
Figure 2-3 数式の宣言と参照関数
処理の流れ
ブロックの開始
ブロック
1
2
3
4
ブロックの修了
4
3
2
1
(aa、 b、
b、 c、
c、 d は宣言、
宣言変数と参照可能変数
a
a b
a b c
a b c d
a b c d
a b c
a b
a
a、 b、 c、 d は参照可能)
2.2.2 静的変数(static 変数)
静的変数は自動変数と異なり、プログラム実行中常にメモリ内に存在する変数です。つまり、静的変数の内
容はプログラム実行中保証されることになります。静的変数はつぎのように指定します。
static int
data;
;
これは、整数型の変数 data を静的変数として宣言することを意味しています。また、つぎのように静的変数
の内容を初期化することができます。
static int
data = 初期値 ;
なお静的変数の初期化は、コンパイル時に行なわれます。
静的変数と自動変数の違いを Figure 2-4、2-5 に示します。静的変数の場合のプログラム(プログラム 2.2)と
自動変数の場合のプログラム(プログラム 2.3)の内容は同じです。ただし、test 関数の変数 data の記憶クラ
スが異なります。
プログラムは、main と test の 2 つの関数でできています。main 関数から test 関数を 2 回呼び出していま
す。test 関数では、変数 data に 10 を加えて出力します。
静的変数の場合、test 関数の変数 data はコンパイル時に初期化されますので、test 関数が呼び出されるた
びに変数 data の内容は初期化されません。つまり、test 関数が 2 回目に呼び出された場合、1 回目の結果
が残っているので、変数 data の内容にさらに 10 を加えることになります。
13
計算機通論 IIa
2000 年度版
#include <stdio.h>
main()
{
int data = 1;
printf("(main) %d¥n", data) ;
test( );
printf("(main) %d¥n", data) ;
test( ) ;
printf ("(main) %d¥n", data) ;
}
test( )
{
static int data = 10;
data += 10 ;
printf("(sub ) %d¥n", data);
}
図 2.4 プログラム 2.2 静的変数の場合
#include <stdio.h>
main()
{
int data = 1;
printf("(main) %d¥n", data) ;
test( ) ;
printf("(main) %d¥n", data) ;
test( ) ;
printf ("(main) %d¥n", data) ;
}
test( )
{
int data = 10;
data += 10 ;
printf("(sub ) %d¥n", data);
}
図 2.5 プログラム 2.3 自動変数の場合
一方、自動変数の場合には、test 関数が呼び出されるたびに test 関数の変数 data は 10 に初期化されま
すので、何回呼び出されても変数 data の内容は 20 となります。また、関数 main の変数 data と関数 test
の変数 data とは、関数ブロックが異なるため互いに異なるものとして扱われていることに注意してください。以
上の結果をまとめると、つぎのようになります。
(main)
(sub)
(main)
(sub)
(main)
静的変数
1
20
1
30
1
(main)
(sub)
(main)
(sub)
(main)
自動変数
1
20
1
20
1
14
計算機通論 IIa
2000 年度版
2.3 変数の有効範囲
ここでは、宣言した変数がプログラムのどの範囲まで有効となるかについて説明します。BASIC や FORTRAN
などでは、宣言した変数はプログラムのどこからでも自由に参照することができますが、C 言語では変数が宣言
された位置によって、参照できる範囲が変わってきます。有効範囲の基本的な単位となるのが関数やブロック
です。ブロックとは、{と}に囲まれた部分をいいます(実行単位と呼ぶこともある)。
それでは、変数がどの範囲まで参照できるかをプログラムを使って説明しましょう。自動変数の説明で使用し
たプログラム 2.3 をもう一度見てみましょう。
#include <stdio.h>
main()
{
int data = 1 ;
printf ( “(main) %d
test( ) ;
printf ( “(main) %d
test( ) ;
printf ( “(main) %d
}
Test( )
{
int data = 10 ;
data += 10 ;
printf ( “(sub) %d
}
¥n” , data );
関数 main で宣言された変
数 data の有効範囲
¥n” , data );
¥n” , data );
関数 test で宣言された変
数 data の有効範囲
¥n” , data );
このプログラムは、main と test の 2 つの関数でできています。変数 data の有効範囲について考えてみましょう。
このプログラムを実行するとつぎのようになります。
(main)
(sub)
(main)
(sub)
(main)
1
20
1
20
1
この結果から、関数 main と test で宣言した変数 data は、その宣言された関数内だけに有効であることがわ
かります。つまり、関数内で宣言した変数は関数内のみで有効となります。このような変数をローカル変数と呼
ぶこともあります。
それでは、プログラムを構成するすべての関数で、共通に使用できるように変数を宣言できないのでしょうか。
プログラム 2.3 で使用した変数 data を main と test 関数で共通に使用できるように修正したプログラムを以下
に示します。
15
計算機通論 IIa
2000 年度版
#include <stdio.h>
int data = 1 ;
main()
{
printf( “(main) %d ¥n” , data );
test( ) ;
printf( “(main) %d ¥n” , data );
test( ) ;
printf( “(main) %d ¥n” , data );
}
Test( )
{
data += 10 ;
printf( “(sub) %d ¥n” , data );
}
------
変数 data の有効範囲
-----プログラム 2.3 との違いは、変数 data を関数 main と test の外で宣言していることです。そして、それぞれの関
数では変数 data の宣言をしていません。このように、関数の外で変数を宣言すると、それ以降のすべての関
数から自由に参照することができます。このような変数をグローバル変数と呼ぶこともあります。このプログラムの
実行結果をつぎに示します。
(main)
(sub)
(main)
(sub)
(main)
1
11
11
21
21
この結果から、変数 data は共通に使用されていることがわかります。
2.4 定数
いままで説明してきた変数は、プログラムの実行過程でその内容を変えることができますが、これから説明する
定数とは、プログラムの実行中その値が変わることのないものです。C 言語の定数には、整数定数、浮動小
数点数定数、文字定数、文字列定数の 4 つが用意されています。
2.4.1 整数定数
整数定数には、10 進数、16 進数、8 進数の 3 種類の表現方法があります。
(a) 10 進表現
10 進表現は 0 以外の数字で始まる数字列です。負の数を表現するときはマイナス(-)を数字の前に書くこと
になります。正の数を表現する場合には、通常プラス(+)をつけないことになっています。
128
-128
16
計算機通論 IIa
2000 年度版
(b) 16 進表現
16 進表現とは、数値を 16 進数で表現するためのもので、使用できる数字と文字は 0~9 と A~F までとな
ります。そして、16 進表現であることを示すために数字列の前に 0X(ゼロエックス)をつけることになっています。
0X16
(16 数の 16 を表現しています)
この 0X16 をビット列で表わすとつぎのようになります。
(c) 8 進表現
8 進表現とは、数値を 8 進数で表現するためのもので、使用できる数字は 0~7 となります。そして、8 進数表
現であることを示すために数字列の前にゼロ(0)をつけることになります。
016
(8 進数の 16 を表現しています)
この 016 をビット列で表わすとつぎのようになります。
016 とは 10 進の 14 を表わしていることになります。
2.4.2 浮動小数点数表現
浮動小数点数表現は、小数点のついた数値、2.34E18 といった大きな数値、2.34E-8 といった小さな数値を
表現したい場合に使用する、ことになります。
1.23
0.0123
また、2.34E18 のような大きな数値は、つぎに示すように指数部と仮数部に分けて表現します。
2.34E18
仮数部 2.34
指数部 18
つまり、仮数部の数値と指数部の数値を E でつなぐことになります。なお、仮数部と指数部の数値は 10 進数
表現で指定します。
17
計算機通論 IIa
2000 年度版
2.4.3 文字表現
文字表現とは、1 文字を表現するもので、表現したい 1 文字をつぎのようにシングルクォーテーションで囲んで
書きます。
’a’
(小文字の a を表現している)
ここで問題となることがあります。 ASCII コード表を見ましょう。この表の中で 16 進の 20~7E まではそれぞれ
対応するキャラクターコードが用意されています。しかし、16 進の 00~1F までには対応するキャラクターコード
がありません。つまり、シングルクォーテーションで囲む文字がないのです。このような場合、つぎに示すように表
現したいキャラクターコードを 8 進表現し、その前にバックスラッシュ(¥)をつけ、それらをシングルクォーテーション
で囲むことになります。
' ¥8 進表現 '
通常、16 進の 00~1F は画面や通信の制御文字としてよく使用されています。そこで、C 言語ではこれらの制
御文字を簡易的につぎのように書くことができます。
文字表現
’¥000’
’¥010’
’¥011’
‘¥012’
’¥014’
‘¥015’
簡易表現
’¥0’
’¥b’
’¥t’
’¥n’
’¥f’
’¥r’
説明
ヌルコード(NULL)
バックスペース(BS)
水平タプ(HT)
ニューライン(NL)
フォームフィード(FF)
キャリジリターン(CR)
つぎに、バックスラッシュ(¥、シングルクォーテーション(’)、ダブルクォーテーション(”)そのものを表わすにはどの
ように表現すればよいのでしょうか。
(a) バックスラッシュ
' ¥ '
これではバックスラッシュを表現したことになりません。
C 言語では文字表現する場合、シングルクォーテーションの後に¥がくると、その後に 8 進表現か簡易表現な
どがくることになります。つまり、これでは文字表現したい文字がないのです。そこで、バックスラッシュを表現する
場合には、つぎのようにバックスラッシュを 2 個続けて書きます。
' ¥¥ '
(b) シングルクォーテーション
単純に考えて、つぎのように書きますか?
'
' '
18
計算機通論 IIa
2000 年度版
これでは、どこからどこまでが文字表現なのか判断することができません。そこでつぎのように書きます。
' ¥ ' '
(1)の場合と同じように表現したいシングルクォーテーションにバックスラッシュをつけて、それらをシングルクォーテ
ーションで囲むことになります。
(c) ダブルクォーテーション
ダプルクォーテーションはつぎのように表現します。
‘
" ’
2.4.4 文字列表現
文字列表現はつぎに示すように、文字列をダブルクォーテーションで囲んで表現します。
"C language"
これは、C language という文字列を表現しています。さらに、文字列の終わりを示すためのヌルコード(¥0)が
文字列の最後に自動的に付加されます。
文字列表現の応用として、printf 関数を使って abc"xyz という文字列を出力する場合について考えてみ
ましょう。いままで学習してきた知識では次のように書くことになると思います。
Printf(
("abc"xyz")
);
これでは、abc"xyz と出力することができません。printf 関数で出力する内容はダブルクォーテ一ションで囲むこ
とになっています。つまり、普通の書き方ではこのダブルクォーテーションを出力することができません。printf 関
数でダブルクォーテーションを出力するには次のように書きます。
Printf(
("abc¥
¥"xyz")
);
そうです。バックスラッシュ(¥)を使えばよいのです。つまり、出力したいダブルクォーテーションの前に¥をつける
ことになります。
19
計算機通論 IIa
2000 年度版
3. C 言語の演算子
C 言語には、他のプログラミング言語の演算子よりかなり多くの種類の演算子が用意されています。このことが
C 言語の特長のひとつとなっています。ここでは、C 言語の演算子の基本的な機能と演算子の優先順位につ
いて説明します。
3.1 演算子の種類
C 言語には、表 3.1 に示す(1)から(15)のような演算子が用意されています。ここでは(1)から(9)についてのみ
説明します。
表 3.1 演算子の種類
(1)
(2)
(3)
(4)
(5)
(6)
(7)
(8)
(9)
(10)
(11)
(12)
(13)
(14)
(15)
演算子の種類
説明
算術演算子
関係演算子
論理演算子
インクリメント演算子
デクリメント演算子
代入演算子
キャスト演算子
順次演算子
条件演算子
アドレス演算子
sizeof 演算子
間接演算子
ビット演算子
シフト演算子
補数演算子
加算、減算、乗算、除算、乗余算。
大小比較。
論理積、論理和、否定。
1の加算。
1の減算。
変数の内容を他の変数に代入します。
データの型を変換します。
式と式をつなぎます。
条件により2つの値のどちらかを採用します。
変数のアドレスを求めます。
変数が確保した領域の大きさを求めます。
間接的にデータを参照します。
ビットごとの論理積、論理和などを行ないます。
ビットを左右にシフトします。
1の補数を求めます。
3.2 算術演算子
算術演算とは、我々が日常使っているたし算したり、ひき算したりする四則演算とほとんど同じものです。算術
演算子には表 3.2 に示すようなものがあります。
演算の種類
加算
減算
乗算
除算
剰余算
演算子
+
*
/
%
使用例
c = a+b ;
c = a-b ;
c = a*b ;
c = a/b ;
c = a%b ;
表 3.2 算術演算子の種類
意味
a と b の内容を加え、その結果を c にセットします。
a の内容から b の内容を引いて、その緒果を c にセットします。
a の内容と b の内容を乗算し、その結果を c にセットします。
a の内容を b の内容で除算し、その緒果を c にセットします。
a の内容をbの内容で除算し、その余りを c にセットします
これら、5つの演算子を使ったプログラムを以下に示します。このプログラムは表 3.2 で説明した内容をプログラミ
ングしたものです。
20
計算機通論 IIa
2000 年度版
#include <stdio.h>
main()
{
int a,b,c;
a = lO;
b = 3;
c = a + b;
printf("<a+b>
c = a – b;
printf("<a-b>
c = a * b;
printf("<a*b>
c = a / b;
printf("<a/b>
c= a % b ;
printf("<a%b>
}
%d¥n",c);
%d¥n",c);
%d¥n",c);
%d¥n",c);
%d¥n",c);
このプログラムでは、変数 a に 10、b に 3 を初期値としてセットし、次に算術演算子を使って c を求めて出力し
ています。このプログラムを実行すると次のような結果が出力されます。
<a
a+b
b>
<a
a-b
b>
<a
a*b
b>
<a
a/b
b>
<a
a%b
b>
13
7
30
3
1
この算術演算では、データの型の異なる変数どうしを演算する場合に問題が発生します。たとえば、整数と浮
動小数点の演算をしたような場合です。このように異なるデータ型の演算を混合演算といいます。
コンパイラは式の中に混合演算を見つけると、その式の中で使用されている変数のうち最も大きな(バイト数)
データ型に、それぞれの変数の型を自動的に変換して演算を行ないます。これを自助変換といいます。その自
動変換の規則を次の表 3.3 に示します。
変換順位
データ型
バイト数
1
char
1
表 3.3 自動変換の規則
2
3
4
short
int
long
2
4
4
5
float
4
6
double
8
表 3.3 では、変換順位の数字が大きいほど変換順位が高いことを示しています。つまり、int 型と long 型の演
算では、int 型を long 型に変換します。また、int 型と float 型の演算では int 型を float 型に変換します。こ
のように混合演算を行なうと自動的にデータの型が変換されます。このように自動変換が行なわれる場合、左
辺のデータ型が右辺のデータ型より大きい場合には問題ありませんが、この逆の場合、つまり左辺のデータ型
が右辺のデータ型より小さい場合に新たな問題が発生します。たとえば、次に示すような場合です。
int a ;
double b ;
a = b ;
21
計算機通論 IIa
2000 年度版
これは、変数 a を整数型、変数 b を倍精度浮動小数点数型として宣言しています。そして、変数 b の内容
を変数 a に代入しています。このような場合、変数 b の内容が自動的にまるめられて(切捨て)変数 a に代入
されます。このようなコンパイラの自動変換機能は便利なこともありますが、ときとしてエラーの原因となることも
ありますので十分に注意してください。
3.3 関係演算子
関係演算子とは、if 文などで判断条件を指定するのに使われる演算子で比較演算子とも呼ばれます。
演算子
<
使用例
c = (a < b);
>
c = (a > b);
<=
c = (a <= b);
>=
c = (a >= b);
==
c = (a == b);
!=
c = (a != b);
表 3.4 関係演算子の種類
意味
a の内容が b の内容より小さければ c に 1(真)、そうでなけれ
ば 0(偽)をセットします。
a の内容が b の内容より大きければ c に 1(真)、そうでなけれ
ば 0(偽)をセットします。
a の内容が b の内容より小さいか等しければ c に 1(真)、そう
でなければ 0(偽)をセットします。
a の内容が b の内容より大きいか等しければ c に 1(真)、そう
でなければ 0(偽)をセットします。
a の内容が b の内容に等しければ c に 1(真)、そうでなけれ
ば 0(偽)をセットします。
a の内容が b の内容に等しくなければ c に 1(真)、そうでなけ
れば 0(偽)をセットします。
関係演算子は、2つの数値や文字を比較し、条件を満足すれば真を、満足しない場合には偽を演算結果
とします。通常、真は 0 以外の数値で、偽は 0 で表します。真の状態を表わす数値に 1 を採用しているコンパ
イラが多いようです。関係演算子は表 3.4 に示すように6種類あります(説明上、演算結果が真であれば 1、
偽であれば 0 とします)。C 言語では、2つの変数が等しいかどうかを調べる場合は = = (イコール記号を2つ
並べる)を、代入を表わすのに = (イコール記号)を使用しますので注意してください。次に関係演算の演算
結果を調べるプログラムを以下に示します。
#include <stdio.h>
main()
{
int a, b, c ;
a = 10 ;
b = 3 ;
c = (a < b) ;
printf ( “( a < b)
c = (a > b) ;
printf ( “( a > b)
c = (a <= b):
printf ( “( a <= b)
c = (a >= b);
printf ( “( a >= b)
c = (a == b) ;
printf ( “( a == b)
%d ¥n”, c) ;
%d ¥n”, c) ;
%d ¥n”, c) ;
%d ¥n”, c) ;
%d ¥n”, c) ;
22
計算機通論 IIa
2000 年度版
c = (a != b);
printf ( “( a != b) %d ¥n”, c) ;
}
このプログラムを実行すると次のような結果が出力されます。
(a
(a
(a
(a
(a
(a
< b)
> b)
<= b)
>= b)
= = b)
!= b)
0
1
0
1
0
1
(偽)
(真)
(偽)
(真)
(偽)
(真)
3.4 論理演算子
論理演算子とは、論理積、論理和、否定などを求めるための演算子です。論理積、論理和、否定をわかり
やすく言いなおすと次のようになります。
論理積(AND)
論理和(OR)
否定(NOT)
かつ(ともに)
あるいは(どちらか)
でなければ
論理演算子には、表 3.5 に示すような3種類があります。
論理演算
演算子
論理積
&&
論理和
||
否定
!
表 3.5 論理演算子の種類
使用例
意味
a の内容と b の内容がともに真ならば c に 1(真)、そ
c = a && b ;
うでなけれぱ 0(偽)をセットします。
a の内容あるいは b の内容が真ならば c に 1(真)、
c = a || b ;
そうでなければ 0(偽)をセットします。
a の内容が真ならば c に 0(偽)、a の内容が偽なら
c=!a;
ば c に 1(真)をセットします。
それでは次のような場合、どのように表現すればよいのでしょうか。
a が 50 より大きく、かつ a が 100 より小さい
これは次に示すように、論理演算子と関係演算子を組み合せると表現することができます。
a が 50 よりおおき かつ
a>50
a が 100 より小さい
&&
a<100
これを C 言語では、次のように表現します。
a>50 &&
a<100
それでは、論理演算子を使ったプログラムを以下に示します。
23
計算機通論 IIa
2000 年度版
#include <stdio.h>
main()
{
int a,b,c;
a = 10;
b = 200;
c = a && b ;
printf("(a&&b) %d¥n",c);
c = a || b;
printf("(a||b) %d¥n",c) ;
c = !a;
c= printf("(!a) %d¥n",c) ;
c = a > 50 && a < 100;
printf("(a>50 && a<100) %d¥n",c) ;
c = a < 50 || a > 100;
printf("(a<50 || a>100) %d¥n",c) ;
c = !(a < 50);
printf("!(a <50) %d¥n",c) ;
}
このプログラムを実行すると次のような結果が出力されます。
(a && b)
(a || b)
(! a)
(a > 50 && a < 100)
(a < 50 || a > 100)
(!(a < 50))
1
1
0
0
1
0
(真)
(真)
(偽)
(偽)
(真)
(偽)
3.5 インクリメント演算子とデクリメント演算子
インクリメント演算子とデクリメント演算子は、他のプログラミング言語にはない演算子でC言語の特長のひとつ
でもあります。インクリメント演算子は1を加算し、デクリメント演算子は1を減算します。つまり、これらの演算
子はカウンタによく使用されます。インクリメント演算子は次のように書きます。
++式
式++
(前置型)
(後置型)
このようにインクリメント演算子は ++(プラスを2つ)で表わします。その書き方としては、式の前に ++ を書く方
法(前置型)と式の後に ++ を書く方法(後置型)があります。まず、インクリメント演算子を変数 b の前に書
いたプログラムを以下に示します。
#include <stdio.h>
main()
{
int a, b ;
b = 1 ;
a = ++b ;
24
計算機通論 IIa
2000 年度版
printf (“a= %d b= %d ¥n” , a, b);
}
このプログラムを実行すると次のようになります。
a = 2
b = 2
つまり、インクリメント演算子(++)が変数 b の前にある場合は、まず変数 b に1を加え、その結果を変数 a に
セットします。次に、インクリメント演算子を変数 b の後に書いたプログラムを以下に示します。
#include <stdio.h>
main()
{
int a, b ;
b = 1 ;
a = b++ ;
printf (“a= %d b= %d ¥n” , a, b);
}
このプログラムを実行すると次のようになります。
a = 1
b = 2
つまり、インクリメント演算子(++)が変数 b の後にある場合は、まず変数 b の内容を変数 a にセットし、その後
で変数 b に1を加えます。
次に、デクリメント演算子について説明します。デクリメント演算子は次のように書きます。
--式
式--
(前置型)
(後置型)
このようにデクリメント演算子は --(マイナスを2つ)で表わします。その書き方は、式の前に --を書く方法(前
置型)と式の後に -- を書く方法(後置型)があります。まず、前置型のプログラムを以下に示します。
#include <stdio.h>
main()
{
int a, b ;
b = 1 ;
a = --b ;
printf (“a= %d b= %d ¥n” , a, b);
}
このプログラムを実行すると次のようになります。
a = 0
b = 0
つまり、デクリメント演算子(--)が変数 b の前にある場合、まず変数 b から1を引き、その結果を変数 a にセッ
トします。次に、後置型のプログラムを以下に示します。
25
計算機通論 IIa
2000 年度版
#include <stdio.h>
main()
{
int a, b ;
b = 1 ;
a = b-- ;
printf (“a= %d b= %d ¥n” , a, b);
}
このプログラムを実行すると次のようになります。
a = 1
b = 0
つまり、デクリメント演算子(--)が変数 b にある場合は、まず変数 b の内容を変数 a にセットし、その後変数
b から1を引きます。
3.6 代入演算子
代入演算子とは、算術演算子の拡張したもので算術演算子よりも簡略化して表現することができます。代
入演算子には、四則演算のほかにビットを操作するための演算子も用意されていますが、ここでは四則演算
についてのみ説明します。代入演算子には、表 3.6 に示すようなものがあります。
演算子
=
+=
-=
*=
/=
%=
使用例
c=a;
c += a ;
c -= a ;
c *= a ;
c /= a ;
c %= a ;
表 3.6 代入演算子の種類
意味
c に a の内容を代入します。
c に a の内容を加えて、その結果を c に代入します。
c の内容からaの内容を引いて、その結果を c に代入します。
c に a の内容を掛けて、その結果を c に代入します。
c の内容を a の内容で割って、その結果を c に代入します。
c を a の内容で割って、その余りを c に代入します。
C 言語では、変数に任意の数値を代入するといった操作は表 3.6 に示すように演算子(=)で行ないます。この
ことから、C 言語では演算子で代入を行なうので次のようなことも可能となります。
a = b = c = d = 100;
これは d に 100 を代入し、次に c に d を、b に c を、a に b の内容を順に代入していきます。つまり、a、b、c、
d とも 100 となります。それでは、代入演算子を使ったプログラムを以下に紹介します。
#include <stdio.h>
main()
{
int a , b ;
a = 10 ;
b = 3 ;
a += b ;
printf (“<a += b> = %d ¥n” , a) ;
}
26
計算機通論 IIa
2000 年度版
このプログラムは代入演算子として+=を使用しています。この代入演算子を算術演算式を使って書きなおす
と次のようになります。
a += b ;
a = a + b ;
(代入演算子)
(算術演算子)
上記のプログラムを実行すると次のようになります。
<a += b> =
13
3.7 キャスト演算子
データの型が異なる変数どうしの演算をする場合には、コンパイラが自動的に型の変換を行なって演算します。
このようにコンパイラがデータの型を自動的に変換してくれたからといって、データの型をいいかげんに宣言するこ
とはあまり関心しません。やはり、データの型は正確に宣言するようにしましょう。
プログラムでデータの型を意識して変換するのに用いる演算子がキャスト演算子です。たとえば、整数型で宣
言した変数を、プログラムのあるところで一時的に浮動小数点数として扱いたいといった場合に使用します。こ
の演算子は、本来宣言したデータの型を変えるものではありません。この演算子を使ったときのみ、一時的に
指定されたデータの型に変わることになります。キャスト演算子は次のように書きます。
(希望するデータの型) 変数名
キャスト演算子を使ったプログラムを以下に示します。
#include <stdio.h>
main()
{
int a , b ;
float c , d
a = 100 ;
c = 1.23 ;
d = (float)
b = (int) c
printf (“a=
}
;
a ;
;
%d d=%6.2f c=%6.2f b=%d ¥n” , a,d,c,b) ;
このプログラムを実行すると、次のようになります。
a=100
d=100.00
c=1.23
b=1
このプログラムでは、変数 a(整数)の内容を浮動小数点数型に変換して変数 d にセットし、変数 b に変数 c
(浮動小数点数)の内容を整数型に変換してセットしています。
27
計算機通論 IIa
2000 年度版
3.8 順次演算子
順次演算子は、次に示すように式と式をつないで書くための演算子です。この演算子にカンマ( , )を用いる
のでカンマ演算子と呼ぶこともあります。
文1,
文1, 文2,
文2, ・・・,
・・・, 文n
これは、カンマでくぎられた順に左から実行されます。順次演算式の結果は最も右の式の結果がその値となり
ます。
c = (a=100, b=10, d=a+b) ;
これを実行すると d=a+b の結果が c の値となります。つまり、c は 110 となります。
3.9 条件演算子
条件演算子は、与えられた条件によって2つの式のいずれかの一方の式を選択し、実行するための演算子で
す。条件演算子はクウェスチョン(?)で表わし、次のように書きます。
条件式?
式1: 式2
これは、条件式が真(条件を満足している状態)のとき式1を、偽(条件を満足していない状態)のとき式2を
実行することを意味します。条件演算子を使ったプログラムを以下に示します。
#include <stdio.h>
main()
{
int inp_data, data1, data2, inp_cnt ;
data1 = data2 = 0 ;
for (inp_cnt=1 ; inp_cnt < 11 ; inp_cnt++ )
{
scanf ("%d" , &inp_data) ;
(inp_data < 6) ? data1++ : data2++;
}
printf ( "data1=%d data2=%d ¥n" , data1, data2 )
}
このプログラムは 1~10 までの任意の数値を 10 個読み込み、1 から 5 までの数値の個数と 6 から 10 までの
数値の個数を求めるためのプログラムです。このプログラムでは条件演算子を次のよう書いています。
(inp_data < 6) ? data1++ : data2++ ;
これは、読み込んだデータ(変数 inp_data にセット)が6より小さい場合には data1++ を実行し、6以上の場
合は data2++ を実行することを意味します。この演算子は、便利な演算子ですから利用するようにしましょう。
このプログラムの実行結果は、みなさんそれぞれ確認してください。
28
計算機通論 IIa
2000 年度版
3.10 演算子の優先順位
いくつかの演算子を組み合せて使う場合、どのような順序で処理されていくのかがよく問題となります。もし、演
算の順序を自分で明確に決定したい場合には、()で囲むことを進めます。しかし()を多く使用すると演算式
が複雑になるのであまりすすめられません。C 言語では、表 3.7 に示すように演算子の優先順位が決められて
います。
表 3.7 演算子の優先順位
優先レベル
15
14
13
12
11
10
9
8
7
6
5
4
3
2
1
演算子
() []
-> ・
Sizeof(型) &
-+
*
/
%
+
<<
>>
<
<=
>
>=
==
!=
&
^
|
&&
||
?
:
>>= <<= ^= &=
,
結合規則
--
++
~
!
左→右
%=
/=
*=
-=
+=
=
この表は縦軸が優先順位のレベルを示します(数字が大きくなるほど優先順位が高いことを示しています)。
結合規則とは、優先順位のレベルが同じ場合の演算の順序を示します。それでは、演算子の優先順位を具
体的な例を使って説明しましょう。
a/b-c*d
ここでは、3つの演算子 /、-、* を使っています。この3つの演算子の優先順位は、(*)→(/)→(-)の順と
なりますので、次の順序で演算されます。
(1)
(2)
(3)
a / b
c * b
(1) - (2)
次の場合はどのような順序で演算されるのでしょうか。
(a / b - c) * d
ここでは、4つの演算子 ( )、/、-、* を使っています。これらの中では()の中が優先されています。さらに、( )
の中には2つの演算子 /、- が使用されていますが / の方が優先順位が高いので次の順序で演算されま
す。
29
計算機通論 IIa
(1)
(2)
(3)
2000 年度版
a / b
(1) - c
(2) * d
次はどのような順序で演算されるでしょうか。
a -= b *= c
ここでは2つの演算子 -=、*= を使っています。この2つの演算子の優先順位レベルは同じですが、結合法
則が左→右ですので次の順序で演算されます。
(1)
(2)
b *= c
a -= (1)
次はどのような順序で演算されるでしょうか。
a * b ++
ここでは2つの演算子 *、++ を使っています。この2つの演算子は ++ 演算子の方が優先順位が高くなって
いますので、次のように演算されるのでしょうか。
(1)
(2)
b++
a * (1)
もう少し考えてみてください。ここのインクリメント演算子は、演算後1を加えるようになっていますから次のように
演算されます。
(1)
(2)
a * b
(1)++
つまり、(a*b)++ と同じになります。
30
計算機通論 IIa
2000 年度版
4. 処理の流れの制御
今回は、プログラムを書く上で重要な要素の1つである処理の流れの制御について説明します。通常、どのよ
うなプログラムでも処理の流れを分岐したり、同じ処理を何回か繰り返し実行したりすることがあります。C 言
語には、処理の流れを効率よく制御するためのいろいろな文が用意されています。
4.1 制御のための文
C言語には、次に示すように処理の流れを複数方向に分岐するもの、処理を繰り返すもの(ループ)、無条件
に分岐するものなどの処理の流れを制御するための文があります。さらに、これらを補助するための文も用意さ
れています。
制御の種類
条件分岐
繰り返し
無条件分岐
補助文
表 4.1 処理の制御コマンド
文
If 文、switch 文
while 文、for 文、do~while 文
goto 文
break 文、continue 文
4.2 if 文
if 文は、処理の流れを2つの方向に分岐するためのものです。if 文には次に示すような2つの書き方がありま
す。
(1)
) if (条件式)
文 1;
;
(2)
) if (条件式)
else
文 1;
;
文 2;
;
または、
は、“条件式”を実行(評価)し、その条件式を満足している場合(評価結果が真)は“文1”を実行します。
もし条件式を満足していない場合(評価結果が偽)は、if 文の次の文へ制御が渡ります。この機能をフローチ
ャートで表わすと Figure 4-1 のようになります。
ここでは、(1)を使ったプログラムを以下に示します。
#include <stdio.h>
main()
{
int a;
scanf (“%d”, &a);
if ( a > 20 ) printf ("data is
}
%d¥n", a);
このプログラムは、数値のデータを変数 a に読み込み、その内容が 20 より大の場合、その内容を出力するため
のものです。
31
計算機通論 IIa
2000 年度版
Figure 4-1
if 文
(2)は、(1)と同様にまず条件式を評価します。その条件式を満足した場合(真の状態)には、文1を実行し
ます。もし、条件式を満足しなかった場合(偽の状態)には、文2を実行します。この機能をフローチャートで表
わすと Figure 4-2 のようになります。
Figure 4-2
if-else 文
次に(2)を使ったプログラムを以下に示します。
#include <stdio.h>
main()
{
int a;
scanf (“%d”, &a);
if (a>20) printf ("bad data is %d¥n", a);
else
printf ("good data is %d¥n", a);
}
このプログラムは、数値のデータを変数 a に読み込み、その内容が 20 より大きかったらエラーデータとして出力し、
20 より小さかったら正しいデータとして出力するためのものです。
32
計算機通論 IIa
2000 年度版
ここまで説明してきたプログラムはもっとも基本的なものです。実際のプログラムでは条件式を評価後、複数の
文を実行することがよくあります。このような場合には、C 言語では次のように書きます。
(3)
) if(条件式)
(条件式)
{
文 1;
;
文 2;
;
…
文 n;
;
}
(3)は、条件式を満足した場合(真の状態)には、文1~文nまでの文を連続に実行します。もし条件を満
足しなかった場合(偽の状態)には、if 文の次の文を実行します。次に、この機能を使ったプログラムを以下に
示します。
#include <stdio.h>
main()
{
int a ;
int b = 1 ;
scanf (“%d”, &a);
if ( a > 20 )
{
++b ;
printf ("Number of bad data is %d¥n", b);
}
}
このプログラムは、エラーになった個数を求めています。
(4)
)
if(条件式)
(条件式)
{
文 11;
;
文 12;
;
…
文 1n;
;
}
else
{
文 21;
;
文 22;
;
…
文 2m;
;
}
(4)はまず条件式を評価し、その結果が真の場合(条件を満足した)、文 11~文 1n までの文を連続に実行
します。もし、評価結果が偽の場合(条件を満足しなかった)、文 21~文 2m までの文を連続に実行します。
次に、この機能を使ったプログラムを以下に示します。
33
計算機通論 IIa
2000 年度版
#include <stdio.h>
main()
{
int a , b , c;
a = b = c = 0;
scanf (“%d”, &a);
if ( a > 20 )
{
++b;
printf ("%d bad data is %d¥n", b , a);
}
else
{
++c;
printf ("%d good data is %d¥n", c , a);
}
}
このプログラムは、if 文で分岐されたそれぞれの回数を求めています。
次に条件式の評価後、分岐して実行する文の中に、さらに if 文を書くプログラムを以下に示します。このプログ
ラムは数値のデータを読み込み、そのデータが 10 より小さい、20 より大きい、10 と 20 の間の場合の3つに分
類するものです。
#include <stdio.h>
main()
{
int a ;
scanf (“%d”, &a);
if ( a >= 10 )
if ( a < 20 ) printf ("10 < a < 20 %d¥n" , a);
else
printf ("a >= 20
%d¥n" , a);
else printf ("a <= 10
%d¥n" , a);
}
このように if 文の評価後、実行する文の中にさらに if 文を書くと、else がどの if 文のものなのかわかりにくくなり
ます。そこで、この例のように if 文と else の関係が誰にでも一目でわかるようにプログラムを書くようにしましょう。
上記のプログラムの if 文の使い方を一般的に書くと次のようになります。
if(条件式
(条件式 1)
)
if(条件式
(条件式 2)
)
else
else
文 1;
;
文 2;
;
文 3;
;
これをフローチャートに書くと Figure 4-3 のようになります。なお、文1、文2、文3には複数の文を書くこともで
きます。そのとき、それらの文を{ }で囲むことを忘れないようにしてください。
if 文と else の関係についてもう少し考えてみましょう。
34
計算機通論 IIa
2000 年度版
if ( a > 0 )
if ( b>3 )
else
x = 10 ;
x = 20 ;
Figure 4-3 if 文と else の関係
この else は直前の if 文(2番目の if 文)に連結します。aとbの値を変化させたときのxの値の様子を次に示し
ます。
a
b
x
-2
-1
0
1
1
*
*
*
20
*:if は次の文へ実行が移る
1
2
20
1
3
20
1
4
10
1
5
10
1
6
10
1
7
10
a が 0 より小さい間は、if 文の次の文へ実行が移ります。そして、a が 0 より大きくかつ b が 3 より小さい問はx
が 20 となります。また、b が 3 より大きくなると x は 10 となります。
次に、{ }を用いて if と else の連結関係を変えてみましょう。
if ( a > 0 )
{
if ( b>3 )
}
else
x = 10 ;
x = 20 ;
この else は最初の if 文に連結します。前の例と同様に a と b の値を変化させたときの x の値の様子を次に示
します。
a
b
x
-2
-1
0
20
20
20
*:if は次の文へ実行が移る
1
1
*
1
2
*
1
3
*
35
1
4
10
1
5
10
1
6
10
1
7
10
計算機通論 IIa
2000 年度版
a が 0 より小さい間は x が 20 となります。a が 0 より大きくかつ b が 3 より小さい間は if 文の次の文を実行しま
す。また、b が 3 より大きくなると x は 10 となります。このようにまったく同じ if 文でも{ }で囲むと処理内容が違
ってきますので注意してください。
4.3 switch 文
switch 文は、処理の流れを複数方向に分岐するためのものです。前節で説明した if 文は、処理の流れを2
方向に分岐するためのものですが、if~else を使うことによって複数方向に分岐することもできます。しかし、if
~else を使って複数方向に分岐させるようにするとプログラムが非常に複雑になってしまいます。特に、if と
else の関係がわかりにくくなってしまいます。このようなことから処理の流れを複数方向に分岐するときは、
switch 文を使うようにしましょう。switch 文は次のように書きます。
switch(条件式)
(条件式)
{
case
定数式1:
case
定数式2:
...
case
定数式n:
default:
:
}
文1;
文2;
文n;
文d;
まず、switch 文に指定されている条件式と case の定数式が一致したとき、一致した case の文に実行が移り、
それ以降の case の文がすべて順々に実行されます。つまり、条件式と一致した case が見つかると、それ以降
に指定されている case の定数式を無視して、一致した case の文から実行が始まることになります。もし、条
件式と case の定数式が一致するものがなかった場合には、default で指定されている文を実行することになり
ます。この default には定数式は指定しません。もし、default の指定がなかった場合には何も実行しないこと
になります。
C 言語では、case の定数式はラベルとして扱われますので、case の定数式の後にコロン(:)を書いて、次に実
行文を書きます。なお、case は複数指定することができます。なお、1つの case に指定できる実行文は、複
数の文で構成されていてもかまいません。次に、switch 文を使ったプログラムを以下に示します。
#include <stdio.h>
main()
{
char data ;
scanf ( “%c” , &data ) ;
switch (data)
{
case ‘a’ :
printf
case ‘b’ :
printf
case ‘c’ :
printf
default
:
printf
}
}
(“(1)Æ
Æ”);
(“(2)Æ
Æ”);
(“(3)Æ
Æ”);
(“(default)”);
このプログラムは、文字のデータを変数 data に読み込み、その内容を a、b、c とそれ以外の文字に分類するた
36
計算機通論 IIa
2000 年度版
めのものです。いま、データとして a を入力すると次のような結果が出力されます。
(1)-->(2)-->(3)-->(default)
データとして c を入力すると次のような結果が出力さ机ます。
(3)-->(default)
つまり、条件式と case の定数式が一致した以降の文が順に実行されることになります。
次に、条件式と case の定数式について説明します。switch 文で指定する条件式や case の定数式は、式
式か
定数でなければなりません。この式と定数のデータ型は、整数型
整数型か文字型
定数
整数型 文字型のみです。つまり、次に示すような
文字型
文字列を case の定数式に指定することはできません。
switch (data)
{
case “ranran” :
case “nene” :
}
r_data++ ;
n_data++ ;
通常、switch 文はここまで説明してきた使い方よりも、条件式を満足した case の文のみを実行するといった
使い方が多いと思います。それでは、そのようにするにはどうすればよいのでしょうか。条件式を満足した case の
みの文を実行するには次のように書きます。
switch(条件式)
(条件式)
{
case
定数式 1:
:
case
定数式 2:
:
...
case
定数式 n:
:
default:
:
}
文 1;
;
break ;
文 2;
;
break ;
文 n;
;
break ;
文 d;
;
break ;
以上のことをフローチャートに書くと Figure 4-4 のようになります。それでは、このフローチャートをもとに書いたプ
ログラムを以下に示します。
#include <stdio.h>
main()
{
char data ;
scanf(“%c” , &data);
switch (data)
{
case ‘a’
: printf (“(1)Æ
Æ”);
break ;
case ‘b’
: printf (“(2)Æ
Æ”);
37
計算機通論 IIa
2000 年度版
case ‘c’
default
}
}
break ;
: printf (“(3)Æ
Æ”);
break ;
: printf (“(default)”);
break ;
Figure 4-4 switch 文
このプログラムを実行しデータとして a を入力すると次のように出力されます。
(1)Æ
Æ
次に、データとして c を入力すると次のように出力されます。
(3)Æ
Æ
このように、break 文(補助文)を使用すると条件式と一致した case の文のみを実行することになります。つま
り、break 文は switch 文の実行を中断し、強制的に終了させるためのものです。
4.4 while 文
ここまで説明してきた if 文や switch 文は、処理の流れを2つの方向または複数の方向に分岐するためのもの
です。ここで説明する while 文は探り返しを制御するためのものです。while 文は次のように書きます。
38
計算機通論 IIa
2000 年度版
while(条
(条件式)
(条件式)
{
文 1;
;
文 2;
;
...
文 n;
;
}
while 文は while 文で指定した条件式を満足している間(評価評価が真
評価評価が真)、文1~文nまでの文を繰り返し実
評価評価が真
行します。そして、条件式を満足しなくなったら(評価結果が偽
評価結果が偽)while
文の実行を終了し、while 文の次の文
評価結果が偽
へ実行が渡されます。この while 文の機能を確認するためのプログラムを以下に示します。
#include <stdio.h>
main()
{
int cnt = 1 ;
while ( cnt < 5 )
{
printf (“cnt = %d¥n”, cnt) ;
++cnt ;
}
}
このプログラムは、変数 cnt が5より小さい間、変数 cnt に1を加えてその内容を出力する処理を繰り返し実行
します。そして変数 cnt が5になったら、この while 文の実行を終了します。このプログラムを実行すると次のよう
な結果が出力されます。
cnt
cnt
cnt
cnt
=
=
=
=
1
2
3
4
次に、while 文のいろいろな書き方について説明します。
while 文の条件式を次のように1と指定すると条件式の評価結果が常に真となりますので、文1から文nまで
の文を無限操り返し実行
無限操り返し実行することになります。
無限操り返し実行
While( 1 )
{
文 1;
;
...
文 n;
;
}
while 文は、条件式の評価結果が偽にならないかぎり while 文を終了することはできません。そこで、C 言語に
は while 文の条件式の結果が真の間でも while 文を強制的に終了させたり、操り返しの途中からふたたび繰
り返しの先頭の文へ戻ったりするための文(補助文)が用意されています。強制的に終了するためには break
文を、繰り返しの先頭へ戻るためには continue 文を使用します。次に break と continue 文の機能を Figure
4-5 に示します。
39
計算機通論 IIa
2000 年度版
Figure 4-5 break と continue 文
それでは、break と continue 文を使ったプログラムを以下に示します。プログラムの内容は、任意の数値を 20
個読み込み、その合計を求めるためのものです。ただし、読み込んだ数値がマイナスだったら読み込みを終了
して、それまで読み込んだデータの合計を出力します(break 文の利用)。なお、10 を越えるデータを読み込ん
だ場合には、そのデータを合計に加算しないことにします(continue 文の利用)。
#include <stdio.h>
main()
{
int inp_data, cnt, total;
while(cnt < 20)
{
scanf("%d" &inp_data) ;
if (inp_data > 10) continue;
if (inp_data < O) break;
cnt++ ;
total += inp_data;
}
printf ("cnt=%d total=%d¥n" , cnt , total ) ;
}
4.5 for 文
for 文は、while 文と同様に繰り返しを制御するためのものです。for 文は次のように書きます。
For ( 式 1 ; 式 2 ; 式 3 )
{
文 1;
;
文 2;
;
...
式 1:
: 初期値設定式
式2: ループ実行判断式
式3: ループ付加実行式
40
計算機通論 IIa
2000 年度版
文 n;
;
}
for 文は、繰り返し(ループ)に入る前に式1を実行します。次に式2を実行(評価)して、その結果が真であれ
ばループに入ります。つまり、文1から文nまでの文を連続して実行することになります。1回ループすると式3を
実行します。for 文で注意しなければならないことは、式1、式2、式3をセミコロン(;)で区切って指定すること
です。for 文の式1を初期値設定式
初期値設定式、式2をループ実行判断式
ループ実行判断式、3をループ付加実行式
ループ付加実行式と呼びます。次に
for
初期値設定式
ループ実行判断式
ループ付加実行式
文を使ったプログラムを以下に示します。
#include <stdio.h>
main()
{
int j ;
for ( j=1 ; j<5 ; j++ )
{
printf (“j = %d¥n”, j);
}
}
このプログラムの for 文の式1、式2、式3は次のように指定しています。
式1
式2
式3
j=1
j<5
j++
(初期値設定式)
(ループ実行判断式)
(ループ付加実行式)
このプログラムはまず変数jに1をセット(初期化)しループに入ります。このループはjが5より小さい間繰り返され
ます。ループが1回終了するごとにjに1を加えています。jの値が5になると for 文の実行は終了します。以下は
このプログラムの実行結果です。
j
j
j
j
=
=
=
=
1
2
3
4
このように for 文は、式2の内容が真の場合、{と}(プログラムブロック)に囲まれている文を実行することになり
ます。C 言語では、ループの処理を中断したりするための補助文として、break と continue 文が用意されてい
ます。break 文を用いると、Figure 4-6 に示すように繰り返しの途中で強制的に for 文を終了させることがで
きます。また、continue 文を用いると、以下に示すように繰り返し処理を途中で終了し、あたかも正常に終了
したかのようにして式3の実行に移ります。
それでは、break と continue 文を使ったプログラムを以下に示します。プログラムの内容は while 文で使用した
プログラムと同じです。
#include <stdio.h>
main()
{
int inp_data, cnt, total ;
for (cnt=1 ; cnt<=20 ; cnt++)
{
scanf("%d" &inp_data) ;
41
計算機通論 IIa
2000 年度版
if (inp_data > 10)
{
cnt-- ;
continue ;
}
if (inp_data < O) break ;
total += inp_data ;
}
printf ("cnt=%d total=%d¥n" , cnt , total ) ;
}
Figure 4-6 for 文の break と continue
次に、for 文のいろいろな書き方について説明します。
for(
( ; ; ){ .. .. }
これは、for 文の式1、式2、式3が省略されています。式2が省略されているので、ループ実行判断式の評価
結果が常に真
常に真となります。つまり、無限ループすることになります。
常に真
for(
( a=0 , b=10 ; a<=10 ; a++ , b-- ){ 文1..
.. 文n }
文1
これは、for 文の式1と式3に順序演算子を使っています。ループに入る前に変数aに 0、変数bに 10 をセット
します。aの内容が 10 より小さい間、文1から文nまでの文を繰り返し実行します。1回のループが終わるとaに
1を加え、さらにbから1を引きます。
42
計算機通論 IIa
2000 年度版
次に、for 文が2重(ネスト)になった場合の break 文の使い方について説明します。
For ( 式 1 ; 式 2 ; 式 3 )
{
for ( 式 4 ; 式 5 ; 式 6 )
{
文 1;
;
if ( .. )
break ;
文 n;
;
}
文 a;
;
if ( .. )
break ;
文 p;
;
}
ループ
の範囲
ループ
の範囲
このように for 文がネストになっている場合、break 文はその break 文を囲むもっとも内側のループを抜けること
になります。つまり、この例のように for 文のネストが2重になっている場合には、break 文が2つ必要になってき
ます。
4.6 do~while 文
do~while 文は、for 文や while 文と同様に繰り返しを制御するためのものです。この do~while 文は次のよう
に書きます。
do
{
文 1;
;
文 2;
;
...
文 n;
;
} while (条件式
条件式)
条件式 ;
do~while 文の技能をフローチャートに書くと Figure 4-7 のようになります。do~while 文は、まず文1から文n
を実行します。そして、while 文に指定されている条件式を満足していれば(評価結果が真)、再び文1から
文nを繰り返し実行します。もし、条件式を満足していなければ(評価結果が偽)、do~while 文の次の文に
実行が移ります。つまり、do~while 文は必ず1回は文1から文nを実行することになります。
do~while 文を使ったプログラムを以下に示します。
#include <stdio.h>
main()
{
int a = 1 ;
do
{
a++ ;
printf (“a = %d¥n”, a) ;
} while (a < 5) ;
}
43
計算機通論 IIa
2000 年度版
Figure 4-7 do~
do~while 文
このプログラムは、まず変数aに1を加え、その変数aを出力します。そして、変数aの内容が5より小さい間、こ
の処理を繰り返し実行します。もし、変数aが5より大きくなると do~while 文の実行は終了します。このプログ
ラムを実行すると次のような結果が出力されます。
a
a
a
a
a
=
=
=
=
=
1
2
3
4
5
次に、do~while 文における break と continue 文の機能について説明します。Figure 4-8 に示すように、
break 文は while 文や for 文の場合と同じように繰り返しの途中で強制終了し、do~while 文の実行を終了
します。一方、continue 文は繰り返しを中断し、最初から再び実行します。
Figure 4-8 do~
do~while 文の break と continue
44
計算機通論 IIa
2000 年度版
4.7 goto 文
goto 文は、無条件に指定されたラベルの文へ分岐するためのものです。繰り返し処理から抜けだすのに使用
されます。しかし、あまり goto 文の多いプログラムは関心できません。goto 文は、次のように書きます。
label a:
文 1;
;
文 2;
;
goto
...
文 j;
;
文 n;
;
label a ;
goto 文は無条件に goto 文に指定されたラベルの文に制御を移します。C 言語ではラベル(名札)は次のよう
に書きます。
ラベル:文
ラベルに使用できる文字の種類や名前のつけ方は、変数の名前をつける場合と同じです。繰り返しから抜け
だす方法についてまとめると以下のようになります。
break 文
continue 文
goto 文
C 言語の補助文
言語の補助文
while 文
for 文
強制的に終了
強制的に終了
繰り返しの先頭に戻る
繰り返しを途中で終了
し、式3を実行する
無条件分岐
無条件分岐
45
do~while 文
強制的に終了
繰り返しを中断し、条
件式の評価から再び実
行する
無条件分岐
計算機通論 IIa
2000 年度版
5. 基本的な入出力
C 言語では、キーボードからデータを入力したりディスプレイ装置へ出力したりする操作は、すべて標準入出力
関数で行ないます。今回では、この標準入出力関数について説明します。なお、本書ではファイルの入出力
関数については説明しません。
5.1 標準入出力関数
C 言語には、標準入出力関数として、C コンパイラによっても異なりますが約 40 種類の関数が用意されてい
ます。それらの中から、ここでは標準入力装置からの入力と標準出力への出力のための関数について説明し
ます。標準入力装置からのデータの入力には、次の 2 つの関数があります。
scanf 関数
getchar 関数
書式付の入力
1 文字の入力
一方、標準出力装置へのデータの出力には、次の 2 つの関数があります。
printf 関数
putchar 関数
書式付の出力
1 文字の出力
これらの関数を使用する場合には、プログラムの先頭に #include 命令が必要となります。#include 命令のパ
ラメータには、ファイル名 stdio.h を指定しなければなりません。この #include <stdio.h> を指定することにより、
次に示すように実際の標準入出力装置が決定されます。
標準入力装置
標準出力装置
キーボード
ディスプレイ装置
5.2 scanf 関数
この関数は、標準入力装置(キーボード)からデータを書式付で入力するためのものです。 scanf 関数は、次
のように書きます。
scanf(変換書式,入力並び)
(変換書式,入力並び);
(変換書式,入力並び)
変換書式は文字列で指定します
入力並びはアドレスで指定します
具体的には次のように書きます。
Scanf ("%d %d", &data1, &data2) ;
変換書式
入力並び
この例の変換書式は、"%d %d"です。このように変換書式は文字列で指定しますので変換指示の組み合
せをダブルクォーテーションで囲みます。入力並びは、&data1、&data2 です。変換指示は必ず%で始まり、その
後に変換の種類を指定します。この例では、変換書式を次のように書いています。
46
計算機通論 IIa
2000 年度版
"%d [SPACE] %d"
これは、入力データの先頭からスペース記号([SPACE])までのデータを 10 進の数字列として入力します。さら
に、スペース記号の次のデータから最後室での数字列を 10 進の数字として入力することを意味しています。
変換書式内のスペース記号を入力区切り文字といいます。つまり、入力区切り文字とは、変換指示と変換
指示の間の文字をいいます。次に、入力区切り文字の例を示します。
%d [SPACE] %d
%d , %d
%d * %d
%2d%4d
スペース([SPACE])が入力区切り文字
スペース(
)が入力区切り文字
カンマ(,)が入力区切り文字
カンマ( )が入力区切り文字
アスタリスク(*)が入力区切り文字
アスタリスク( )が入力区切り文字
入力区切り文字なし
次に、変換指示について説明します。変換指示は一般に次のように書きます。
% (*) (幅)変換文字
()は省略可能
(幅)入力する桁数を指定します。省略した場合は区切り文字までとなります
(*)この変換位置に対応する数字列(文字列)を読み飛ばします
変換文字には、次に示す 6 種類があります。
d
x
o
c
s
f
入力した数字列を 10 進数とみなし、符号つき整数に変換します。
入力した英・数字列を 16 進数となみし、整数に変換します。入力できる
文字や数字は、0~9、A~F のみです。
入力した数字列を 8 進数とみなし、符号なし整数に変換します入力でき
る数字は、0~7 のみです。
1 文字を入力します。
入力区切り文字までの文字列を入力します。入力した文字列の最後に
ヌル文字(\0)を付加します。
入力した数字列を浮動小数点数に変換します。
(a) 変換指示 %d
%d、%x、
%x、%o による入力
変換指示 %d、%x、%o を使ったプログラムを以下に示します。
#include <stdio.h>
main()
{
int dec_data, hex_data, oct_data ;
/* decimal data */
printf ("convert.dec ?") ;
scanf ("%d", &dec_data) ;
printf ("dec_data= %d¥n", dec_data) ;
/* hexadecimal data */
printf ("convert.hex ?") ;
scanf ("%x" , &hex_data) ;
printf ("hex_data= %d¥n", hex_data) ;
/* octal data */
printf ("convert oct ?") ;
47
計算機通論 IIa
2000 年度版
scanf ("%o", &oct_data) ;
printf ("oct_data= %d¥n", oct_data) ;
}
このプログラムで入力データとして 123 を読み込んで処理した様子を次に示します。
入力
123
変換指示
%d
%x
%o
結果
123
291
83
書式
(10 進)
(16 進)
(8 進)
(b)入力区切り記号による入力
(1) スペース [SPACE] が入力区切り記号
123 [SPACE] 45 を 123 と 45 に分けて入力する。
scanf ("%d [SPACE] %d", &a, &b) ;
(2) スラッシュ (/) が入力区切り記号
87/12/25 を 87、12、25 の 3 つに分けて入力する。
scanf ("%d/%d/%d", &a, &b, &c) ;
(3) カンマ (,) が入力区切り記号
123,45 を 123 と 45 に分けて入力する。
scanf ("%d, %d", &a, &b) ;
(4) スペースとカンマが入力区切り記号
123 [SPACE] 45、789 を 123、45、789 の 3 つに分けて入力する。
scanf ("%d [SPACE] %d, %d", &a, &b, &c) ;
(5) 入力桁数の指定
12345 を 123(3 桁)と 45(2 桁)に分けて入力する。
scanf ("%3d %2d", &a, &b) ;
(6) 読み飛ばしの指定
123456 を 12(2 桁)と 56(2 桁)に分けて入力する(34 は読み飛ばします)。
scanf ("%2d %*2d %2d", &a, &b) ;
(c)文字と文字列の入力
ここで使用する変数は次のように宣言されているものとします。
(1) 1 文字の入力
48
計算機通論 IIa
2000 年度版
ABC を A、 B、 C の 3 つに分けて入力する。
scanf ("%c%c%c", &a, &b, &c) ;
(2) 文字列を配列に入力
ABC を文字列 ABC として入力する。
scanf ("%s”, &d[O]) ;
または
scanf ("%s", &d) ;
入力された文字列の終わりにヌル文字(\0)が自動的に付加されます。
(3) 文字列を配列に入力
ABCDE を文字列 ABC と DE に分けて入力する。
scanf ("%3s%2s", &d, &e) ;
入力された文字列の終わりにヌル文字(\0)が自動的に付加されます。
(4) 文字列の読み飛ばし
ABCDEFG を文字列 AB と EFG に分けて入力する(CD は読み飛ばします)。
scanf ("%2s %*2s %3s", &d, &e) ;
5.3 printf 関数
この関数は標準出力装置(ディスプレイ装置)ヘデータを書式付で出力するためのものです。printf 関数は、
次のように書きます。
printf(変換書式,出力並び)
(変換書式,出力並び);
(変換書式,出力並び)
変換書式は文字列で指定します
出力並びは出力する変数や配列を指定します
具体的には、次のように書きます。
printf ("%d %d¥n", a, b) ;
変換書式
出力並び
この例の変換書式は、 "%d %d\n" です。このように変換書式は文字列で指定しますので、変換指示の組
み合せをダブルクォーテーションで囲むことになります。入力並びはa、bです。
変換指示は必ず%で始まり、その後に変換の種類を指定します。この例では、変数aの内容を 10 進数に変
換して出力し、次にスペースを出力し、その後に変数bの内容を 10 進数に変換して出力します。さらに、制御
文字のニューライン(\n)を出力することになります。変換指示は、一般に次のように書きます。
49
計算機通論 IIa
2000 年度版
% (+/-) (0) (幅
幅) (. 精度)
精度 (I) 変換文字
()は省略可能
(. 精度)
浮動小数点数を出力するときの小数点以下の桁数を指定します
(幅)
出力幅を指定します。もし、出力する数値や文字列がここで指定した桁数を超
える場合には、自動的に必要な桁数まで拡張されます
(0)
出力する数値や文字列が出力幅より小さい場合には、空きの桁をゼロで出力し
ます。省略するとスペースで出力します
(+/-)
-と書くと出力幅内で左詰めで出力し、+と書くと出力幅内で右詰めで出力さ
れます。省略すると右詰めで出力されます
変換文字には、次の 9 種類があります。
d
x
o
u
c
s
e
f
g
符号つき整数を 10 進数に変換して出力します
符号なし整数を 16 進数に変換して出力します
符号なし整数を 8 進数に変換して出力します
符号なし整数を 10 進数に変換して出力します
文字として出力します
文字列として出力します
float と double の型の数値を±n.nnnE±mmm の形式で出力します。n と m
は±n.nnnx10±mmm の意味です
float と double の型の数値を±mmm.nn の形式で出力します mmm と nn の
桁数は幅・精度で指定します。つまり、幅は mmm の桁数と nn の桁数を加
えた合計にピリオドの1を加えた値となります。nn は小数点以下の桁数なの
で変換書式の精度で指定します
float と double の型の数値をまずf変換して出力します。これで表現できない
ときはe変換して出力します
出力書式には、さらに次の規則があります。
(a) %で始まらない文字や文字列はそのまま出力します
(b) 画面の出力制御をするための制御文字を書くことができます
(a) 変換指示%d、
、%o による出力
変換指示 、%x、
数値の 100 を変換指示 %d、%x、%o を用いて出力するプログラムを以下に示します。
#include <stdio.h>
main()
{
int a = 100 ;
printf ( "convert-dec = %d¥n" , a) ;
printf ( "convert-hex = %x¥n" , a) ;
printf ( "convert-oct = %o¥n" , a) ;
}
このプログラムを実行すると次のような結果が出力されます。
convert-dec = 100
convert-hex = 64
50
計算機通論 IIa
2000 年度版
convert-oct = 144
(b) 整数値の出力
ここでは、次のように宣言されている変数aの内容を右詰め、左詰めで出力する方法などについて説明しま
す。
int
a = 1234 ;
(a)
) printf ("data=%d" , a) ;
(b)
) prjntf ("%-5d" , a) ;
(c)
) printf ("%+06d" , a) ;
data=1234
l234 (左詰め
左詰め)
左詰め
OO1234 (右詰め
右詰め)
右詰め
(c) 文字と文字列の出力
ここでは、次のように宣言されている文字 A と文字列 XYZ を出力する方法について説明します。
char a ='A' ;
char b[4] = "XYZ" ;
(a)
)
(b)
)
(c)
)
(d)
)
(e)
)
printf
printf
printf
printf
printf
("data =%c", a) ;
("data =%s", &b[O] ) ;
("data %s", b) ;
("%-6s", b) ;
("%+6s", b) ;
data=A
data=XYZ
data=XYZ
XYZ□□□
□□□ (左詰め
左詰め)
左詰め
□□□XYZ
(右詰め
右詰め)
□□□
右詰め
(d) 浮動小数点数の出力
ここでは、次のように宣言されている変数aとbの内容をf変換とe変換して出力する方法について説明します。
float a = 3.1234
float b = 43.1234
1.
.
2.
.
3.
.
4.
.
5.
.
6.
.
printf
printf
printf
printf
printf
printf
("%6.4f", a) ;
("%10.6f", a) ;
("%6.2f", a) ;
("%-10.6f", a) ;
("%12.5e", b) ;
("%14.6e", b) ;
3.1234
3.123400
3.12
3.1234
4.31234E+001
4.312340E+001
51
計算機通論 IIa
2000 年度版
6. 配列について
ここでは、1つの変数名で複数のデータをまとめて格納することができる配列について、その宣言の方法、初期
化の方法、参照の方法などについて説明します。C 言語での配列の考え方は BASIC や FORTRAN と同じで
すので、他のプログラミング言語ですでに配列を使ったことのある読者にとっては難しくないと思います。
6.1 配列とは
いま 100 個のデータを読み込み、それぞれを異なる変数に格納することを考えてみましょう。みなさんは、読み
込んだデータをどのような変数名に格納しますか。単純に考えると次のように考えられます。
inp_data1, inp_data2, ..., inp_data99, inp_data100
このように、100 個の異なる変数名を考えることは意外と大変なことです。そこで、これらのデータを1つの変数
名で管理できるようにしたのがこれから学習する配列です。上の問題は、配列を使うと次のように書くことになり
ます。
inp_data[100]
これで、配列名 inp_data に 100 個のデータを格納することができます。このように、配列を使用すると、1つの
変数名のもとに複数のデータを格納することができます。
6.2 配列の宣言
C 言語では配列を次のように宣言します。
記憶クラス
データ型
配列名[要素数];
配列名[要素数]
(a) 記憶クラス
配列の記憶クラスには、自動配列、静的配列、レジスタ配列、外部配列の4つの記憶クラスがあり、それぞれ
の機能は変数の場合と同じです。何も指定しないと自動配列になります。
(b) データ型
データ型は変数のデータ型と同じです。つまり、整数型、浮動小数点数型、文字型の3種類のデータ型を指
定することができます。
(c) 配列名
配列の名前のつけ方は、変数の名前をつけるときの規則と同じです。
(d) 要素数
要素数とは配列の大きさのことです。つまり、配列に格納できるデータの個数のことです。各要素には、0
0 から
52
計算機通論 IIa
2000 年度版
要素数マイナス 1 までの要素番号がつけられます。つまり、要素数が 100 の場合、要素番号は 0 から 99 ま
でとなります。この要素番号を用いて配列の内容を参照することになります。要素数や要素番号を書くときは、
次に示すように[]内に書きます。
配列名[要素番号]
上の書き方は1次元配列の場合ですが、C 言語にも他のプログラミング言語と同じように、2次元配列、3次
元配列を宣言することができます。次に、2次元配列と3次元配列の宣言の方法を示します。
記憶クラス データ型 配列名[要素数][要素数]
記憶クラス データ型 配列名[要素数][要素数][要素数]
2次元の配列
3次元の配列
それでは、配列の宣言法について具体的に説明しましょう。
例1:
int test[5] ;
これは、次のような配列を宣言しています。
記憶クラス
自動配列(省略)
データ型
整数(int)
配列名
Test
要素数
5
要素番号
0~4
この配列を概念的に示すと次のようになります。
test[0]
test[1]
test[2] test[3] test[4]
要素番号(0~
)
要素番号( ~4)
この配列には整数以外のデータを格納することができません。
例2:
static char
name[3] ;
これは、次のような配列を宣言しています。
記憶クラス
データ型
配列名
要素数
要素番号
静的配列(static)
文字型(char)
Name
3
0~2
この配列は記憶クラスが静的配列ですから、プログラムが実行中常にメモリ内に存在することになります。
例3:
int
test[2][3] ;
これは、次のような2次元の配列を宣言しています。
53
計算機通論 IIa
2000 年度版
記憶クラス
データ型
配列名
要素数
要素番号
自動配列(省略)
整数型 (int)
Test
6 (2 x 3)
0 から 1、0 から 2
これは、2行3列の配列を整数型で宣言しています。この配列を概念的に表わすと次のようになります。
test[0][0]
test[1][0]
test[0][1]
test[1][1]
test[0][2]
test[1][2]
このように2次元の配列を宣言する場合には、配列名の次に行の要素数、つづいて列の要素数を指定する
ことになります。
例4: char
name[2][3][4] ;
これは、次のような3次元の配列を宣言しています。
記憶クラス
データ型
配列名
要素数
要素番号
自動配列(省略)
文字型 (char)
Name
24 (2 x 3 x 4)
0 から 1、0 から 2、0 から 3
これは、2行3列の配列が奥の方向に4つある3次元の配列を文字型で宣言しています。この配列を概念的
に表わすと次のようになります。
name[0][0][3]
name[1][0][3]
name[0][0][2]
name[1][0][2]
name[0][0][1]
name[1][0][1]
name[0][0][0]
name[1][0][0]
name[0][1][3]
name[1][1][3]
name[0][1][2]
name[1][1][2]
name[0][1][1]
name[1][1][1]
name[0][1][0]
name[1][1][0]
name[0][2][3]
name[1][2][3]
name[0][2][2]
name[1][2][2]
name[0][2][1]
name[1][2][1]
name[0][2][0]
name[1][2][0]
このように3次元の配列を宣言する場合には、配列名の次に行の要素数、列の要素数、つづいて奥方向の
要素数の順に指定することになります。
6.3 配列の初期化
配列を宣言した時点で、配列の各要素にデータをセットすることを初期化といいます。この初期化ができる記
値クラスは、静的配列と外部配列のみです。つまり、自動配列とレジスタ配列の配列は初期化することができ
ません。配列を初期化する場合には、次のように書きます。
記憶クラス
データ型
配列名 [要素数
要素数]
要素数 ={値1,値2,...,値n};
54
計算機通論 IIa
2000 年度版
配列に初期値をセットする場合には、配列の宣言の後にイコール記号(=)をつけて、初期値をカンマ(,)で区
切りながら要素数と同じ数だけ指定します。さらに、これらの初期値を{}で囲むことになります。
(1)
static int test[5] = {1, 2, 3, 4, 5} ;
これは配列 test が次のように初期化されます。
test[0]
1
(2)
test[1]
2
test[2]
3
test[3]
4
test[4]
5
static char name[3] = {'a', 'b', 'c'} ;
これは配列 name が次のように初期化されます。
name[0]
a
name[1]
b
name[2]
c
ここまでは1次元配列の初期化について説明してきました。次に、2次元の配列の初期化について説明しま
す。2次元配列の初期化は次のように書きます。
記憶クラス データ型 配列名[行の要素数][列の要素数]={
{値1,値2,...,値n},
,値n},
{値1,値2,
{値1,値2,...,値n}
,値n}
{値1,値2,
};
2次元配列に初期値をセットする場合には、基本的には1次元配列の場合と同じように行ないます。つまり、
1次元配列が複数個(行の要素数だけ)集まったと考えてください。それでは、具体的な例で考えてみましょ
う。
(3)
static int test[2][3] = {
{1, 2, 3},
{4, 5, 6}
} ;
これは配列 test が次のように初期化されます。
2行
(4)
1
4
3列
2
5
3
6
static char name[3][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'},
{'x', 'y', 'z'}
};
これは配列 name が次のように初期化されます。
55
計算機通論 IIa
2000 年度版
3行
(5)
a
d
x
3列
b
e
y
c
f
z
static int test[2][3] = {1, 2, 3, 4, 5, 6} ;
これは、2次元の配列を1次元の配列とみなして初期化する例です。その結果は(3)と同じように初期化され
ます。つまり、2次元の配列は連続した1次元の配列とみることもできるわけです。
6.4 配列の参照
配列の参照とは、ある値を配列にセットしたり、配列の内容を出力したりすることです。このような操作を行なう
には要素番号を用いて行なうのが基本です。しかし、この要素番号を用いた配列の参照は実際のプログラム
では意外と使われていません。それに代ってポインタ
ポインタを使って配列を参照する方法が多く用いられています。こ
ポインタ
こでは、これら2つの内要素番号を用いた参照について説明します。ポインタの考え方について次回で説明す
ることにします。
配列を参照するには、要素番号を用います。この要素番号は必ず0から始まって配列の要素数から1を引い
た番号まで存在することになります。要素番号を用いて配列を参照するには、参照したい配列名の次に参照
したい要素の番号を[ ]の中に指定します。
参照配列名[参照要素番号]
たとえば、配列 test の各要素に数値をセットするには次のように書きます。
int
test[3]
test[0]
test[1]
test[2]
;
= 1 ;
= 2 ;
= 3 ;
/* 要素番号0に1をセット */
/* 要素番号1に2をセット */
/* 要素番号2に3をセット */
これは、配列 test の要素番号0に数値の1が、要素番号1に数値の2が、要素番号2に数値の3がセットさ
れることを意味しています。これで配列 test のすべての要素に数値がセットされたことになります。次に配列の
内容を他の変数にセットする場合について考えてみましょう。
static int
int b ;
b = a[0] ;
a[2] = { 1, 2 } ;
これは、変数bに配列aの要素番号0の内容をセットすることを意味しています。この例では、配列aは初期化
されていますので、変数bには数値の1がセットされることになります。
次に、配列の各要素の内容を出力してみましょう。
static int test[5] = {1, 2, 3, 4 , 5} ;
int i ;
for (i=0 ; i <5 ; i++)
{
printf ("test [%d] ... %d¥n" , i , test [i]) ;
56
計算機通論 IIa
2000 年度版
}
これは配列名 test を整数型で宣言し、さらに初期化しています。この初期化された配列 test の各要素の内
容を printf 関数を使って出力します。1回の printf 関数で、1つの要素の内容を出力します。for 文を使って
配列 test の要素数だけ繰り返し実行します。つまり、5回ループすることになります。出力される様子を次に
示します。
ループ回数
1
2
3
4
5
i の値
0
1
2
3
4
出力内容
test [0] … 1
test [1] … 2
test [2] … 3
test [3] … 4
test [4] … 5
ここまでは1次元の配列についてのみ考えてきました。次に、2次元の配列の参照法についても考えてみましょ
う。
まず、2次元の配列の各要素に値をセットする場合について説明します。
int
test[2][3]
test[0][0]
test[0][2]
test[1][2]
;
= 1 ;
= 2 ;
= 3 ;
/* (1)回目
回目 */
/* (2)回目
回目 */
/* (3)回目
回目 */
これは、2行3列の配列を整数型で宣言し、3つの要素にそれぞれ値をセットしています。2次元の配列に値
をセットする方法は、1次元の配列に値をセットする方法と基本的に同じです。つまり、セットしたい配列名の
後に行方向の要素番号、次に列方向の要素番号を指定することになります。実際には、次のように値がセッ
トされます。
3列
(1) 回目
(2) 回目
2
1
2行
(3) 回目
3
次に、2次元の配列の内容を出力する方法について説明します。
int i, j;
static char name[3][4]= {
{'a', 'b', 'c', 'd'},
{'e', 'f', 'g', 'h'},
{'w', 'x', 'y', 'z'}
};
for (i=0 ; i <3 ; i++)
{
for (j=0 ; i <4 ; j++)
{
printf("name[%d][%d] ... %c¥n", i, j, name[i][j]) ;
}
}
57
計算機通論 IIa
2000 年度版
これは、3行4列の配列 name を文字型で宣言し、さらに初期化しています。出力は、for 文を使って配列
name の各要素の内容を出力しています。変数iは行の要素番号、変数jは列の要素番号を制御するのに使
用しています。出力される様子を次に示します。
i の値
0
0
0
0
1
1
1
1
2
2
2
2
j の値
0
1
2
3
0
1
2
3
0
1
2
3
出力内容
name [0][0] … a
name [0][1] … b
name [0][2] … c
name [0][3] … d
name [1][0] … e
name [1][1] … f
name [1][2] … g
name [1][3] … h
name [2][0] … w
name [2][1] … x
name [2][2] … y
name [2][3] … z
58
計算機通論 IIa
2000 年度版
7. ポインタについて
前回では、配列について、その宣言の方法、初期化の方法、参照の方法などについて説明しました。今回は、
ポインタを用いたアドレス演算子と配列の参照方法についても説明します。
7.1 ポインタを用いたアドレス演算子と間接演算子
アドレス演算子と間接演算子は、BASIC や FORTRAN にない演算子で、システムプログラムなどによく使用さ
れます。ここでは、2 つの演算子の基本的な考え方について説明します。まず、アドレス演算子から説明します。
アドレス演算子とは、変数のデータ領域が確保されているメモリ内の位置に関する情報(アドレス)を求めるた
めの演算子です。このアドレス演算子はアンパーサンド(&)で表わします。つまり、次に示すようにアドレスを求
めたい変数の前に&をつけることになります。
&変数名;
&変数名
もう少し具体的に説明しましょう。いま、次のように指定されたとします。
int a ;
a = 1 ;
&a ;
変数 a が整数型として宣言され、さらに 1 がセットされています。次にその変数 a のデータ領域のアドレスを求
めています。変数 a がメモリに確保される様子を以下に示します。
Figure 7-1 アドレスとデータ概念
Figure 7-1 は変数 a のデータ領域がメモリの 11 番地に確保され、そこに 1 がセットされていることを示してい
ます。変数 a は int 型で宣言されていますので、実際は 4 バイトのデータ領域が確保されるわけですが、説明
上 1 バイトとします。この状態で変数 a のアドレスを求めると 11 番地となります。つまり、&a と書くとその結果は
11 となるわけです。
次に求めた変数 a のアドレスを変数 b にセットしてみましょう。
int a ,b ;
a = 1 ;
b = &a ;
みなさんはこのように書くでしょうか?これは間違いです。つまり、変数 b は int と宣言されています。これでは変
数 b にアドレスをセットすることはできません。そこで、変数 b を次のように宣言します。
int a, *b ;
a = 1 ;
59
計算機通論 IIa
2000 年度版
b = &a ;
つまり、変数 b を宣言するところで変数 b の前にアスタリスク(*)をつけることになります。これは、変数 b が整
数型変数のデータ領域のアドレスを確保することを意味しています(ポインタ変数)。この様子を以下に示しま
す(変数 a のデータ領域は 11 番地に、変数 b のデータ領域は 2 番地に確保されたとします)。Figure 7-2 の
ように、2 番地には変数 a のデータ領域のアドレスである 11 がセットされます。
Figure 7-2 変数 a と b のアドレスとデータ
次に間接演算子について説明しましょう。この間接演算子をポインタ清算子と呼ぶこともあります。間接演算
子とは、直接的にデータを参照するのではなく、ポインタ変数にセットされている内容をアドレスとしてデータを参
照するための演算子です。この間接演算子はアスタリスク(*)で表わします。アドレス演算子の説明に使用
したプログラムを使って間接演算子について説明しましょう。
int a, *b ;
a = 1 ;
b = &a ;
このプログラムの変数 a と b の関連を次に示します(Figure 7-3)。
Figure 7-3 変数 a と b の関連
次に、変数 a と b の内容を出力する printf 関数を考えてみましょう。
printf ("a=%d b=%d¥n", a, b) ;
この printf 関数を実行すると次のようになります。
a=1
b=11
これはデータを直接的に参照することを意味しています。変数 b がポイントしている内容を出力するには、
printf 関数を次のように書きます。
printf ("a=%d b=%d¥n", a, *b) ;
これを実行すると次のようになります。
60
計算機通論 IIa
a=1
2000 年度版
b=1
つまり、変数 b の前にアスタリスクをつければ変数 b にセットされている内容を直接出力するのではなく、その内
容をアドレスとして、そのアドレスが示すデータ領域の内容を出力することになります。
7.2 ポインタを用いた配列の参照
ポインタの基本的な考え方とその使い方については、上記で説明しました。そこでポインタを使ったプログラムに
ついて再び説明しましょう。
int a, *b ;
a = 1 ;
b = &a ;
この例では変数aに数値の1を、変数bに変数aのアドレスをセットしています。変数aは 10 番地、変数bは 20
番地として、この様子を以下に示します(int は4バイトのデータ領域を確保するものとします)。
Figure 7-4
a と b 変数の領域
Figure 7-4 からもわかるように、変数aはメモリの10番地から4バイトの領域が確保され、そこに数値の1がセ
ットされています。変数bも同じように20番地から4バイトの領域が確保され、そこに変数aのアドレスである1
0番地がセットされています。次に示すように変数bに1を加えてみます。
int a, *b ;
a = 1 ;
b = &a ;
b++ ;
変数bの内容はどうなるでしょうか。変数bには変数aのアドレスである 10 番地がセットされていますから、10 番
地+1 で 11 番地になるでしょうか。結果は以下のようになります。
Figure 7-5 アドレスの加算
変数bの内容は、Figure 7-5 に示すように 14 番地となります。つまり、b++のインクリメント
演算は、変数bが整数型のデータ領域のアドレスを格納するように宣言されていますから、変数bの内容に整
数型データの大きさ(バイト数)である4を加えることになります。C 言語では、このように論理的なアドレスのこと
61
計算機通論 IIa
2000 年度版
をポインタと呼びます。そして、アドレスとはコンピュータのハードウェアで決めたメモリにデータを格納する最小単
位のことです。ポインタとアドレスの違いを以下に示します(整数型で説明します)。
このように、プログラムを書くとき、論理的なアドレスであるポインタのみを考えればよいことになります。このことを
確認するためのプログラムを以下に示します。
#include<stdio.h>
main()
{
int a, *b ;
a = 1 ;
b = &a ;
printf(“b = %d¥n”, b) ;
b++ ;
printf(“b = %d¥n”, b) ;
}
このプログラムを実行すると、次のような結果が出力されます。ただし、32 ビット系のコンピュータを使用し、変
数aが 100 番地に格納されているものとします。
b = 100
b = 104
(1)
(2)
この結果は、使用するコンピュータによって異なりますが、(1)と(2)の値との差が4(4バイト)になります。16 ビット
系のコンピュータを使用している場合、int 型データは、2バイトのデータ領域を確保しますので、(1)と(2)の差は
2(2バイト)となります。
それでは、本題であるポインタの説明に入ることにしましょう。配列の先頭のアドレスを求めるには次のように書
きます。
&配列名
配列名[0]
;
配列名
または
&配列名;
配列名;
配列の先頭である要素番号0の前にアドレス演算子であるアンパーサンド(&)をつけて書きます。または、単
に配列名の前にアドレス演算子を書くこともできます。
それでは、要素番号n番のアドレスはどのようにして求めればよいのでしょうか。次のように書くことになります。
&配列名
配列名[n];
;
配列名
また、2次元の配列の先頭のアドレスを求めるには、次のように書きます。
&配列名
配列名[0][0]
;
配列名
&配列名
配列名 ;
または
このように、1次元の配列の場合と同じように書くことになります。
次に、1次元の配列の各要素のアドレスを求めるプログラムを以下に示します。
#include <stdio.h>
main()
{
62
計算機通論 IIa
2000 年度版
int *add ;
int data[3] ;
add = &data[0] ;
printf ("address (data[0]) ---%d¥n", add) ;
add = &data[1] ;
printf ("address (data[1]) ---%d¥n", add) ;
add = &data[2] ;
printf ("address (data[2]) ---%d¥n", add) ;
}
このプログラムの実行結果は、みなさんが使用しているコンピュータによって異なりますので、各自実行してくだ
さい。出力結果が4(4バイト)ずつ数字が増えていくことを確認してください(ただし、16ビット系のコンピュータ
を使用する場合には2(2バイト)ずつ増えていきます)。
次に、ポインタを使って配列の各要素の内容を出力する方法について説明します。それには、間接演算子で
あるアスタリスク(*)を用いることになります。つまり、次に示すようにアドレスを格納している変数の前に間接演
算子を表わすアスタリスク(*)をつけることになります。
int *a , data[3] ;
a = &data[0] ;
これは、変数aに配列 data の先頭のアドレスをセットすることを表わしています。次に、この変数aがポイントし
ている内容を参照するには次のように書きます。
b = *a ;
変数aの前にアスタリスクをつけることによって、配列 data の要素番号0の内容を示すことになります。つまり
*a は data[0] と同じことになります。この例では、配列 data[0] の内容を変数 b にセットします(変数 b は
文字型とします)。
それでは、上記で示したプログラムを変数 add がポイントしている内容を出力するように修正してみましょう。修
正したものを以下に示します。
#include<stdio.h>
main()
{
int *add ;
static int data[3]={1,2,3}
add = &data[0] ;
printf ("address (data[0])
add = &data[1] ;
printf ("address (data[1])
add = &data[2] ;
printf ("address (data[2])
}
;
---%d=%d¥n", add, *add) ;
---%d=%d¥n", add, *add) ;
---%d=%d¥n", add, *add) ;
さらに、このプログラムを実行すると次のような結果が出力されます(配列 data の先頭のアドレスは 200 番地と
します)。
address (data[0]) ---200 = 1
63
計算機通論 IIa
2000 年度版
address (data[1]) ---204 = 2
address (data[2]) ---208 = 3
それでは、配列 data のアドレスを1度だけ求め、あとはポインタを変化させながら、配列の各要素の内容を出
力するように以下で示したプログラムを修正してみましょう。
#include<stdio.h>
main()
{
int *add ;
static int data[3]={1,2,3}
add = &data[0] ;
printf ("address (data[0])
add++ ;
printf ("address (data[1])
add++ ;
printf ("address (data[2])
}
;
---%d=%d¥n", add, *add) ;
---%d=%d¥n", add, *add) ;
---%d=%d¥n", add, *add) ;
このプログラムを実行すると以下で示す結果が出力されます。
address (data[0]) ---200 = 1
address (data[1]) ---204 = 2
address (data[2]) ---208 = 3
次に、ポインタを用いて2次元の配列の各要素の内容を出力する方法について説明します。参照方法につい
て説明する前に、もう一度2次元の配列について考えてみましょう。2次元の配列は次のように宣言します。
int
data[2][3] ;
これは、次のような2行3列の配列を表わしています。
2行
data[0][0]
data[1][0]
3列
data[0][1]
data[1][1]
data[0][2]
data[1][2]
これは、配列 data[2][3]を概念的に表わしたもので、実際にコンピュータのメモリの上でこのような形で配列の
データ領域が確保されるわけではありません。実際のメモリ上には、次に示すように連続したデータ領域として
確保されます。
data[0][0]
data[0][1]
data[0][2]
data[1][0]
Data[1][1]
data[1][2]
つまり、2次元の配列でも1次元の配列のように参照することができることになります。
それでは、2次元の配列の各要素の内容をポインタを使って参照するプログラムを以下に示します。
#include <stdio.h>
main()
{
int i ;
char *add ;
64
計算機通論 IIa
2000 年度版
static char name[2][3] =
{
{'a', 'b', 'c'},
{'x', 'y', 'z'}
};
add = &name[0][0];
for (i=0 ; i<6 ; i++)
{
printf ("%d---%c¥n" , i , *add) ;
add++ ;
}
}
このプログラムは、2次元の配列 name を文字型で宣言し、さらに初期化しています。まず、2次元の配列
name の先頭、つまり name[0][0] のアドレスを変数 add にセットします。次に、printf 関数を使って各要素の
内容を出力しています。この printf 関数は、for 文を使って name の要素の数(6要素)分繰り返し実行しま
す。このプログラムを実行すると、次のような結果が出力されます。
0
1
2
3
4
5
-------------
a
b
c
x
y
z
65
計算機通論 IIa
2000 年度版
8. 関数について
C言語のプログラムは、通常いくつかの関数で構成されています。ここでは、関数の基本的な考え方と関数間
のデータの受渡し方法などについて説明します。
C言語での関数とは、数学でよく使う関数と同じ考えをもったものです。いま、数学で f ( x ) = ax + bx + c
という関数があるとします。この関数に a=1、b=2、c=3、x=2 と数値を代入すると関数の値として 11 を求めるこ
とができます。その様子を次に示します。
2
a=1
b=2
c=3
x=2
f(x) = 1x4 + 2x2 + 3 = 11
このことをプログラムに置き換えて考えてみましょう。読者のみなさんにすっかりおなじみとなった printf 関数で説
明することにします。printf 関数は、次のように書きます。
Printf(出力書式
出力書式 ,出力並び)
,出力並び ;
これを関数的に考えると、printf 関数の出力書式と出力並びにしたがって、出力並びに指定された変数の内
容がディスプレイ装置に出力されます。関数とは何らかのデータを受け取ると、そのデータをもとに処理して結
果を出すものです。つまり、切符自動販売機にお金を入れると切符がでてくるのに似ています。自動販売機
も1つの関数と考えることができます。
8.1 C言語での関数の書き方
C言語では、関数は一般的に次のように書きます。
関数名(仮引数の並び)
仮引数の宣言;
{
関数内変数や配列の宣言;
実行文;
}
(a)
) 関数名
関数名は、変数と同じ規則にしたがって自由につけることができます。ただし、プログラムには関数名が main と
いう関数が必ず1つ必要です。プログラムの実行は、この main と名前のついた関数から始まることになります。
(b)
) 仮引数の並び
仮引数とは、関数が受け取る変数のことです。この変数が複数あるときは、カンマで区切って並ぺることになり
ます。この仮引数は()で囲みますが、仮引数がない関数の場合は、単に () と書きます(省略することはできま
せん)。
(c)
) 仮引数の宣言
関数が受け取る変数のデータの型を宣言します。このデータの仮引数は、渡す側のデータの型と同じでなけれ
ばなりません。
66
計算機通論 IIa
2000 年度版
Figure 8-1 関数概念
8.2 関数間のデータの受渡し
1つのプログラムがいくつかの関数で構成される場合、その関数間のデータの受渡し方が問題となります。ここ
では、具体的な例を使ってC言語での関数間のデータの受渡し方法について説明します。関数には Figure
8-2 に示すように“呼びだす側”
“呼びだす側”と“呼ばれる側”
“呼びだす側” “呼ばれる側”とがあります。C言語では、関数間のデータを渡す方向は、次
“呼ばれる側”
の2つ力言あります。
Figure 8-2 関数間のデータの受渡し
(a) 呼びだす側から呼ばれる側へ値を渡す。
(b) 呼ばれる側から呼びだす側へ値を渡す。
関数間のデータの受渡し方法には、いくつかの方法がありますがここでは、以下の(1)、(2)について説明しま
す。
(1)
(2)
関数の引数として変数の値を直接渡す。
関数の引数として変数のアドレス(ポインタ)を渡す。
8.2.1 関数の引数として変数の値を直接渡す方法
67
計算機通論 IIa
2000 年度版
この方法は、関数を呼びだす側から呼ばれる側へのみ値を渡すことができます。
(a) 数値データ
#include <stdio.h>
main()
{
int data1,data2;
data1 = 12 ;
data2 = 3 ;
func (data1 , data2) ;
}
func (a,b)
int a , b ;
{
int y ;
y = a*b ;
printf("(a*b) =%d¥n",y) ;
}
上に示したプログラムは main 関数と func 関数の2つの関数でできています。その2つの関数間のデータ受渡し
様子を次の Figure 8-3 に示します。
Figure 8-3 関数の引数として変数の値を直接渡す方法
上記示したように main 関数から func 関数へ変数 data1 と data2 の内容が直接渡されます。 func 関数で
は、その内容を変数aとbで受け取り、変数aとbの掛算をし、その結果を出力します。このように、関数間で変
数の値を直接渡す場合は、呼びだす側の変数の内容は呼びだされる側の処理によって変わることはありませ
ん。上記のプログラムを実行すると次のような結果が出力されます。
68
計算機通論 IIa
2000 年度版
(a * b) = 36
(b)配列データ
関数間では配列のデータを直接渡すことはできません。このことから、文字列のデータを直接渡すことができな
いことになります。
8.2.2 関数の引数として変数のアドレス(pointer)を渡す方法
この方法では、関数を呼びだす側と呼ばれる側の両方向へ値を渡すことができます。
Figure 8-4 関数の引数として変数のアドレス(pointer)を渡す方法
)を渡す方法
数の引数として変数のアドレス(
(a) 数値データ
以下に示したプログラムも、main 関数と func 関数の2つの関数でできています。この2つの関数間のデータ受
渡し様子は以下のとおりです。
#include <stdio.h>
main()
{
int data1, data2 ;
data1 = 12 ;
data2 = 3 ;
func(&data1, &data2) ;
69
計算機通論 IIa
2000 年度版
}
func (a,b)
int *a , *b ;
{
int y ;
y = *a - *b ;
printf("(a-b) =%d¥n",y) ;
}
上記からもわかるように、main 関数から func 関数へは変数 data1 と data2 が格納されているデータ領域のア
ドレスが渡されます。func 関数では、そのアドレスをもとにして変数 data1 から data2 の内容をひき算し、その
結果を出力しています。このように、関数間で変数のアドレスを渡す場合には、main 関数と func 関数で変数
data1 と data2 の格納されているデータ領域を共通に使用することになりますので注意してください。このプログ
ラムを実行すると次のような結果が出力されます。
(a–b) =9
(b) 配列のデータ
#include <stdio.h>
main()
{
int data[3] ;
data[0] = 1 ;
data[1] = 2 ;
data[2] = 3 ;
func(&data[0]) ;
}
func (a)
int a[];
{
int x ;
x = a[0] + a[1] + a[2] ;
printf ( "---%d¥n" , x) ;
}
上に示したプログラムは、main 関数で宣言した配列 data のアドレスを func 関数に渡しています。func 関数
では、受け取った配列のアドレスをもとにして、配列の各要素の内容を加算してその結果を出力しています。
main 関数では、func 関数に配列のアドレスを渡すには次のように書きます。
func(&data[0]) ;
又は
func(data) ;
つまり、配列名を直接指定する方法です。配列名のみを書きますと、配列の先頭のアドレスを示すことになる
からです。通常は、この書き方が多いようです。このプログラムを実行すると次のような結果が出力されます。
--- 6
70