2003年度後期 IT教育基礎論演習B

第3回: 手続き型プログラミング(1) C言語基礎

概要と目標

■ 概要

手続き型プログラミングパラダイムに基づく、 コンパイル型プログラミング言語の1つとしてC言語を例に取り、 プログラミング言語によるプログラムの記述と実行の概念を理解し、 プログラムの記述、実行方法を取得する。

■ 目標

目次


演習内容

■ はじめに〜C言語とは

C言語は、手続き型プログラミングパラダイムに基づく コンパイル型のプログラミング言語の1つであり、 1972年に、当時ベル研究所に勤めていたKen ThomsonとDennis Richieらにより UNIXオペレーティングシステムを開発するために開発されたB言語を改良するかたちで 開発された。 その後、1989年に、米国標準規格協会(ANSI)による標準化が行われ、 ANSI Cとして現在に至っている。

プログラムを記述する上で、個々の機能を実現する手続きを 関数と呼ばれるモジュールとして個別に定義、利用でき、 またこれらのモジュールをライブラリとして登録し、必要に応じて利用できるなど、 高級言語としての記述性の高さに加え、 主要な開発環境のほとんどで同様に記述されたプログラムを利用できるという 移植性の高さから、現在でも強い支持を受けている。 近年、システム開発当に広く利用されている オブジェクト指向プログラミング言語であるC++やJAVAなども、 C言語を基礎としている。

多くのC言語開発環境では、 オペレーティングシステムで提供される各種機能が ライブラリの形で用意されており、 そのオペレーティングシステム上で稼動する 各種アプリケーションプログラムの開発を行えるだけでなく、 シンプルな言語体系とコンパイル型の実行形式により、 記述されたプログラムの高速な実行が可能であり、 シミュレーションや統計処理などの数値計算にも FORTRANについで多く利用されている。

なお、C言語に関する書籍は 参考書籍、Webで紹介するもの以外にも 多数出版されており、 詳しい文法や言語仕様が解説されているので、 本演習では、基本的な使用方法について扱うこととし、 詳細については、各自でそれぞれ学習すること。

■ プログラムの記述と実行

C言語によるプログラムの記述と実行は概ね以下のような流れとなる。

図1: Cプログラムの記述と実行の流れ
プログラムの記述
プログラムのコンパイル
実行

□ C言語によるプログラムの記述

C言語によるプログラムを記述し、これを実行するためには、 先ず、Emacs等のエディタを利用して、 コンピュータで実行する処理手続き等をプログラムとして定義したファイルを 作成する(このプログラムをソースプログラム、 またソースプログラムが記述されているファイルをソースファイルと呼ぶ)。 ソースファイルの内容は、例えば以下のようになる。

簡単なCプログラムの例(1): hello.c
ファイルの内容 意味
/* hello.c */ コメント行
  
#include <stdio.h> 標準入出力関数の定義の読み込み
  
int main(int argc, char **argv)   メイン関数の定義開始
{
    puts("Hello World"); 文字列"Hello World"の標準出力への出力
} メイン関数の定義終了

このとき、C言語によるプログラムを記述したソースファイルは、 その内容がC言語により記述されていることが判るよう、拡張子として".c"をつける。

□ ソースプログラムのコンパイル

C言語により記述されたプログラムを実行するためには、 ソースファイルから実行可能ファイルを生成する必要がある。 C言語はコンパイル型言語であるため、 C言語により記述されたプログラムは、一般にはそのままでは コンピュータ上で実行することはできない。 そのため、プログラムが記述されたソースファイルから、 コンパイラと呼ばれるコマンドを利用して コンピュータが直接実行可能なマシン語と呼ばれるコードにより記述された 実行可能ファイルを生成する。 この作業をコンパイルと呼ぶ。

ソースプログラムをコンパイルするには、 コマンドラインから以下のようにCコンパイラのコマンドである gccを実行する。

$ gcc <source-file>

例えば、ソースファイルの名前が"hello.c"であれば、 コマンドラインに以下のように入力する。

$ gcc hello.c

これにより、"a.out"のファイル名で実行可能ファイルが生成される。

ただし、この場合、ソースファイルにどのようなファイル名が 付けられているかによらず、 実行可能ファイルのファイル名は全て"a.out"となってしまい、 不便である。 実行可能ファイルのファイル名を指定してコンパイルするには、 以下のようにコンパイラを実行する。

$ gcc -o <executable-file> <source-file>

例えば、"hello.c"をコンパイルして、 "hello"という実行可能ファイルを生成したい場合、 コマンドラインに以下のように入力する。

$ gcc -o hello hello.c

□ 実行可能ファイルの実行

コンパイルにより生成された実行可能ファイルによりプログラムを実行するには、 通常のコマンドの実行と同様に、 その実行可能ファイルのファイル名をコマンドラインから入力する。

ただし、通常、環境変数"PATH"で指定される コマンドラインサーチパスにカレントディレクトリは含まれていないため、 その場合、明示的にパスを指定する必要がある。 例えば、実行可能ファイルのファイル名が"hello"で、 カレントディレクトリに置かれている場合、 カレントディレクトを示す"."を利用して、 "./hello"と入力する。 "hello.c"をコンパイルした"hello"の実行結果を以下に示す。

$ ./hello
Hello World
演習

以下のプログラムを入力せよ。

簡単なCプログラムの例(2): hello-to.c
/* hello-to.c */

#include <stdio.h>

int main(int argc, char **argv)
{
  int i;

  if ( argc < 2 ) {
    puts("Hello to whom?");
  } else {
    printf("Hello, %s", argv[1]);

    if ( argc < 3 ) {
      puts("!");
    } else {
      for( i = 2; i < argc - 1; i++ ) {
	printf(", %s", argv[i]);
      }
      printf(" and %s!\n", argv[i]);
    }
  }
}

次に、このプログラムを"hello-to"としてコンパイルし、 以下のような実行結果が得られることを確認せよ。

hello-toの実行結果(1)
$ ./hello-to
Hello to whom?

hello-toの実行結果(2)
$ ./hello-to Michael Steven Robert
Hello, Michael, Steven and Robert!

■ 変数

C言語では、プログラムの実行中に何らかの値(データ)を保存し、 これを後から参照するために、 変数を利用することができる。

□ 変数の宣言と利用

変数の利用方法は、以下の例で示すように、 先ず、使用する変数の名前をプログラム中で宣言する。 変数名として使用できる文字は英数字、および "_"(アンダースコア)のみであり、 また、数字から始まる変数名を使用することはできない。

またこのとき、変数の宣言は、 "{"と"}"で囲まれたブロックの中の先頭、 もしくはブロックの外で、その変数が使用される前の部分で行う必要がある。 宣言された変数は、基本的に、ブロックの中で宣言された変数はそのブロック内で、 またブロックの外で宣言された変数はそのファイル全体で、 値の代入、参照を自由に行うことができる。

変数の宣言と利用例: variable-sample.c
/* variable-sample.c */

#include <stdio.h>

int c;						/* 使用する変数の宣言 */

int main(int argc, char **argv)
{
  int a, b, d;					/* 使用する変数の宣言 */

  a = 1;					/* 変数への値の代入 */
  b = 2;					/* 変数への値の代入 */
  c = 4;					/* 変数への値の代入 */

  d = (a + b) * c;				/* 変数の値の参照と代入 */

  printf("(%d + %d) * %d = %d\n", a, b, c, d);	/* 変数の値の参照 */
}

このプログラムの実行例は以下のようになる。

variable-sampleの実行結果
$ ./variable-sample
(1 + 2) * 4 = 12

□ 変数と型

C言語では、整数や実数、文字などの各種データ型を使用することができ、 その値を代入するための変数を用意することができる。 逆に、使用するデータ型に応じて変数を宣言しなければならず、 基本的には、異なる型のデータを そのままでは別の型の変数に代入することはできない。

C言語で利用できる基本データ型は以下の通りである。 ただし、データサイズは使用するコンピュータシステムのアーキテクチャや オペレーティングシステムの種類に依存するため、 あくまで目安としての値であり、この限りではない。

基本データ型の種類
型名データサイズ
文字char 8bit(1byte)  
整数短整数 short int 16bit(2byte)  
整数 int 32bit(4byte)  
長整数 long int 32bit(4byte)  
実数短精度浮動小数点数 float 32bit(4byte)  
倍精度浮動小数点数 double 64bit(8byte)  
拡張倍精度浮動小数点数 long double 128bit(16byte)

各データ型の変数を宣言するには、以下のように、 変数名の前に型名を記述する。

<type> <variable>;

例えば、短精度浮動小数点型の変数fを宣言する場合、 以下のように記述する。

float f;

□ 変数の初期化

C言語では、変数の初期値がわかっている場合、 以下のように、変数の宣言時に予めその値を代入し、初期化することができる。

<type> <variable> = <value>;

例えば、短精度浮動小数点型の変数f0で初期化して 宣言する場合、以下のように記述する。

float f=0;

□ 配列とポインタ

ポインタ変数

ポインタとは、数値や文字などのデータそのものの値ではなく、 言わば、データが格納されている入れ物のラベルであり、 具体的には、そのデータが格納されているコンピュータシステム上の メモリ領域のアドレスのことである。 ポインタ変数は、このラベル、 すなわちデータが格納されているメモリ領域のアドレスを値とする変数である。 ポインタ変数を宣言するには、以下のように、 変数名の前に"*"を記述する。

<type>  *<variable>;

例えば、文字を格納する領域へのポインタ変数"p"を宣言するには、 以下のように記述する。

char *p;

ポインタ変数に代入されているポインタを参照するには、 "*"をつけず、"p"のように単に変数名を記述する。 また、ポインタ変数に代入されているポインタが指し示す領域に 格納されている値を参照するには、"*p"のように 変数名の前に"*"をつけて記述する。 ただし、ポインタ変数を宣言しただけでは、 これはあくまでポインタを値とする変数が宣言されただけであり、 データを格納する領域そのものが確保されたわけではない。 この場合、ポインタの値はデタラメな値となっており、 そのポインタの示す先を参照しようとすると 意図しない結果となることに注意すること。

一般の変数に対し、変数名の前に"&"をつけることにより、 その変数に代入されている値が格納されている領域への ポインタを参照することもできる。 例えば、文字変数"c"に代入されている文字を格納している領域への ポインタを参照し、ポインタ変数"p"に代入するには、 以下のように記述する。

p = &c;
演習

以下のプログラムを入力し、実行せよ。

ポインタ変数の利用例: pointer-sample.c
/* pointer-sample.c */

#include <stdio.h>

int main(int argc, char **argv) {
  int a;		/* 整数型変数 a を宣言 */
  int *b;		/* 整数型へのポインタ変数 b を宣言 */

  b = &a;		/* a の値の格納領域を示すポインタを b に代入 */

  a = 1;		/* a に 1 を代入 */
  printf("a: %d\n", a); /* a の値を参照し、標準出力に出力 */

  *b = 2;		/* b のポインタが示す先に 2 を代入 */
  printf("a: %d\n", a); /* a の値を参照し、標準出力に出力 */
}

このプログラムを実行すると、以下のような結果を得る。 はじめ変数"a"には1が代入されており、 その後、変数"a"を直接、操作していないにも関わらず、 次に参照した際には"a"の値は2に置き換わっている。 この結果から、ポインタを利用してどのようなことが行われたか、考察せよ。

pointer-sampleの実行結果
$ ./pointer-sample
a: 1
a: 2
ポインタの利用

ポインタには様々な利用方法があるが、関数(手続き)を呼び出す際に、 その関数の中で変数の値を変更したい場合などに利用できる。 例えば、標準入力から入力された値を読み取る関数である scanf()を使用して、読み込んだ値を変数に格納する場合、 以下のようなプログラムを記述することができる。

関数呼び出しでのポインタの利用例: scanf-sample.c
/* scanf-sample.c */

#include <stdio.h>

int main(int argc, char **argv) {
  float r, PI=3.14159265358979323846;

  printf("Radius? ");
  scanf("%f", &r);

  printf("Size of circle is %f\n", PI * r * r);
}
scanf-sampleの実行結果
./scanf-sample
Radius? 2
Size of circle is 12.566371
配列変数

C言語では、同じ型の複数のデータをまとめて、 これを配列変数に格納して扱うことができる。

配列変数を宣言するには、以下のように、 変数の宣言時に変数名の後ろに"["と"]"で囲んで 配列の大きさを指定する。

<type> <variable>[<amount>];

例えば、256個の短精度浮動小数点型のデータを格納するための 配列変数fを宣言する場合、以下のように記述する。

float f[256];

このとき、変数"f"には、配列を格納するための領域の先頭アドレスを 示すポインタが設定される。 また、配列の各要素の初期値がわかっている場合、以下のように、 予めその値を"{"と"}"で囲んで","で区切って 指定することができる。

<type> <variable> [<amount>] = { <value-1>, <value-2>, <value-3>, ..., <value-n> };

ただし、この場合、要素の個数は省略することができる。 例えば、5個の整数型のデータ{1,2,3,4 5}を格納するための 配列変数aを宣言する場合、以下のように記述する。

int a[]={1, 2, 3, 4, 5};

配列変数の各要素に値を代入したり、また、代入されている値を参照するには、 変数名の後ろに "[" と "]" で囲んで 要素の番号を指定することで、通常の変数と同様に扱うことができる。 例えば、配列変数aの3番目要素を 別の変数bに代入するには、以下のように記述する。 ただし、要素の番号は0から始まることに注意すること。 この場合、3番目の要素を示す番号は3-1=2となる。

b = a[2];
文字配列

C言語では、文字列そのものを扱うことはできない。 その代わり、文字列の終わりを示す0を最後の要素とする文字の配列として 文字列を扱い、これを文字配列と呼ぶ。 例えば、"apple"という文字列は、 文字型の配列{"a", "p", "p", "l", "e", "\0"}として扱われ (注: "\0"は文字型の0を意味する)、 これをそのまま"apple"と記述することができる。

文字配列を格納する変数を利用するには、以下のように宣言する。 このとき、<string-length>には、 文字列を格納するのに十分な値を指定する。

char <variable>[<string-length>];

例えば、255文字分の文字列を格納する文字配列変数 "string"を宣言するためには、 文字列の最後の要素である0を加えた256文字分の文字型の配列として、 以下のように記述する。

char string[256];

予め格納する文字列が決まっている文字配列変数を宣言する場合、 例えば、"The quick brown fox jumps over the lazy dog." という文字列を格納する文字配列変数"string"を宣言するには、 以下のように記述することができる。

char string[] = "The quick brown fox jumps over the lazy dog.";

ただし、文字配列はあくまで配列なので、 以下のような文字列を他の文字配列変数に代入することはできない。 文字配列の各要素ごとに他の文字配列変数の要素に代入することや、 文字配列の格納領域を示すポインタを文字型のポインタ変数に格納することはできる。

char a[]="apple";
char b[256];
char *c;

b = a;		/* これはダメ */

b[0] = a[0];	/* これは可能 */
b[1] = a[1];
b[2] = a[2];
b[3] = a[3];
b[4] = a[4];
b[5] = a[5];

c = a;		/* これも可能 */

□ 構造体

複数の異なる種類のデータをまとめて1つのデータ群として扱えると便利である。 複数のデータをまとめて扱う手法として配列があるが、 配列は同種のデータしか扱うことができない。 これに対し、複数の異なる種類のデータをまとめて扱う場合、 C言語では構造体を利用することができる。 構造体とは、複数の要素をまとめて構造化したものである。 構造体の各要素をその構造体のメンバと呼ぶ。

構造体の宣言

構造体を利用するためには、先ず、構造体がどのよう型のメンバからなるか、 その構造を宣言する必要がある。 例えば、名前を格納する文字配列と、年齢を格納する整数の 2つのメンバからなる構造体_personを宣言する場合、 以下のように記述する。

struct _person {
  char *name;
  int age;
};

構造体の構造を宣言しただけでは、まだ構造体を利用することはできない。 具体的なデータを格納するためには、 その構造体によるデータを格納するには、そのための変数を宣言する。 これを構造体変数と呼ぶ。 例えば、先に宣言した構造体_personによる 構造体変数personを宣言する場合、 以下のように記述する。

struct _person person;

構造体の構造と構造体変数の宣言を同時に行なうこともできる。 例えば、上記の構造体_personの宣言と 構造体変数personの宣言をまとめて、 以下のように記述することもできる。

struct _person {
  char *name;
  int age;
} person;

また、よく利用する構造体は、 C言語の型定義機能であるtypedefを利用して 新たな型として定義すると便利である。 例えば、構造体_personの構造を宣言すると同時に、 これを新たな型としてPERSONとして定義するには、 以下のように記述する。

typedef struct _person {
  char *name;
  int age;
} PERSON;

ただし、この場合、宣言された構造体の名前である_personは 後から使用する必要がなくなるので、 これを省略して以下のように記述することもできる。

typedef struct {
  char *name;
  int age;
} PERSON;

これにより、この構造体により定義された新たな型による変数の宣言は、 以下のように簡単に記述できるようになる。

PERSON person;
構造体変数の参照

構造体変数として宣言された変数に具体的なデータを代入したり、 また格納されているデータを参照するなど、構造体変数の参照を行なうには、 その構造体変数の後ろに、メンバ演算子"."に続けてメンバを指定する。 例えば、構造体変数personに値を代入するには、 以下のように記述する。

person.name = "Takoshi YOTSUASHI";
person.age  = 33;

同様に、構造体変数personに格納されている値を参照し、 標準出力に出力する場合、以下のように記述することができる。

printf("Name: %s\n", person.name);
printf("Age:  %d\n", person.age);

もし、構造体変数を宣言すると同時にその値の初期化を行ないたい場合には、 以下のように記述することができる。

struct _person person = {"Takoshi YOTSUASHI", 33};

また、構造体の配列を利用することもできる。 例えば、構造体の配列変数を宣言し、値を代入する場合、 以下のように記述することができる。

struct _person people[10];

people[0].name = "Masayuki TORAI";
people[0].age  = 34;
people[1].name = "Takoshi YOTSUASHI";
people[1].age  = 33;
              ...

もし、構造体の配列変数を初期化する場合には、 以下のように記述することができる。

struct _person people[] = {{"Masayuki TORAI",    34},
                           {"Takoshi YOTSUASHI", 33},
                           {"Yuji TAMEYAMA",     33},
                           {"Yasuto Izumikawa",  32},
                           {"Yuichi Ogawa",      29}};

また、構造体へのポインタも、一般のポインタと同様に利用することができる。 例えば、構造体変数person、 構造体へのポインタ変数pを宣言し、 personへのポインタをpに代入する場合、 以下のように記述することができる。

struct _person person = {"Takoshi YOTSUASHI", 33};
struct _person *p;

p = &person;

ただし、構造体へのポインタを介して構造体のメンバを参照する場合には 注意が必要である。 ポインタ変数の示す値を参照する演算子"*"よりも、 メンバ演算子"."の方が優先されるため、 以下のように記述すると正しく動作しない。

struct _person person = {"Takoshi YOTSUASHI", 33};
struct _person *p;
char name;

p = &person;
name = *p.name; /* これはダメ */

ポインタ変数の示す先の構造体のメンバを参照するには、 以下のように括弧を使用して明示的に演算順序を指定する必要がある。

struct _person person = {"Takoshi YOTSUASHI", 33};
struct _person *p;
char name;

p = &person;
name = (*p).name; /* これはよい */

しかし、構造体へのポインタを利用することは多いため、 括弧を使用して演算順序を指定する代わりに以下のように記述することができ、 一般的にはこの書式が利用される。

struct _person person = {"Takoshi YOTSUASHI", 33};
struct _person *p;
char name;

p = &person;
name = p->name; /* これもよい */

■ 制御構造

制御構造には、大きく分けて条件分岐と繰り返しの2種類がある。 条件分岐とは、変数の値などを条件として、 複数の手続きの中から1つを選んで実行するものであり、 また、繰り返しとは、ある手続きを指定回数分だけ、 もしくはある条件が成り立っている間だけ、 繰り返し実行するものである。 C言語では、条件分岐や繰り返しなどの制御構造により、 複雑な手続きの流れを定義することができる。

□ 条件分岐

真偽値に基づく条件分岐: if 〜 then 〜 else

ある条件が成立しているか否かで条件分岐を行うには、if文を使用する。 if文の書式は以下のようになる。

if文の書式
if ( <条件式> ) <手続き1> else <手続き2>

このとき、<条件式>が成立していれば<手続き1>を実行し、 成立していなければ<手続き2>を実行する。 <条件式>には、変数の値が指定した値と等しいか、異なるか、 大きいか、小さいか、などを記述することができる。 また、複数の条件式の論理演算で結合した、条件式を記述することもできる。 <手続き1>および<手続き2>には、 式、もしくは"{"と"}"で囲んだブロックを記述する。 なお、もし、<条件式>が成立しなかった場合に何もしないのであれば、 else以降の青字の部分は省略することができる。

例えば、標準入力から読み込んだ文字に応じて 標準出力に出力する内容を変えたい場合には、以下のようなプログラムとなる。

if文を使用したプログラム例
/* if-then-else-sample.c */

#include <stdio.h>

int main(int argc, char **argv)
{
  char a[255];

  printf("Is it OK? (y/n) ");
  scanf("%s", a);
  if ( *a == 'y' || *a == 'Y' )
    puts("Thank you.");
  else {
    puts("Oh, really?");
    puts("I see...");
  }
}

このプログラムの実行結果は、以下のようになる。

if文を使用したプログラムの実行例(1)
$ ./if-then-else-sample
Is it OK? (y/n) y
Thank you.

if文を使用したプログラムの実行例(2)
$ ./if-then-else-sample
Is it OK? (y/n) n
Oh, really?
I see...

変数の値に基づく条件分岐: switch 〜 case

変数の値などに応じて複数の分岐を行う場合には、 switch 〜 case文を使用する。 switch 〜 case文の書式は以下のようになる。

switch 〜 case文の書式
switch ( <式> ) {
  case <定数1>: <式1-1>; <式1-2>; ...;
  case <定数2>: <式2-1>; <式2-2>; ...;
    ...
  case <定数n>: <式n-1>; <式n-2>; ...;
  default:      <式0-1>; <式0-2>; ...;
}

これは、switchでしていされる式を評価した結果が caseで指定される定数と一致する箇所にジャンプすることを意味する。 もし、caseで指定された定数と一致しない場合には、 defaultで指定される箇所にジャンプする。

ただし、単にcaseで指定した箇所にジャンプした場合、 このままでは一致した箇所以降の全てが実行対象となり、 指定された定数と一致していない部分も実行されることとなる。 これに対して、caseで指定される定数と一致する箇所のみを 実行したい場合、 ブロック中の手続きの実行を打ち切る命令であるbreak文を使用して、 以下のように記述するとよい。

よく利用されるswitch 〜 case文の書式
switch ( <式> ) {
  case <定数1>: <式1-1>; <式1-2>; ...; break;
  case <定数2>: <式2-1>; <式2-2>; ...; break;
    ...
  case <定数n>: <式n-1>; <式n-2>; ...; break;
  default:      <式0-1>; <式0-2>; ...;
}

例えば、指定した年月日の曜日を表示するプログラムは、 switch 〜 caseを利用して以下のように記述することができる。

指定年月日からの曜日の算出
/* week.c */

#include <stdio.h>

int main(int argc, char **argv)
{
  int year, month, day, week;

  printf("year:  ");
  scanf("%d", &year);
  printf("month: ");
  scanf("%d", &month);
  printf("day:   ");
  scanf("%d", &day);

  if (month == 1 || month == 2) {
    year--;
    month += 12;
  }

  week = (year + year/4 - year/100 + year/400 + (13*month + 8)/5 + day) % 7;
  switch ( week ) {
  case 0:
    puts("Sunday");
    break;
  case 1:
    puts("Monday");
    break;
  case 2:
    puts("Tuesday");
    break;
  case 3:
    puts("Wednesday");
    break;
  case 4:
    puts("Thursday");
    break;
  case 5:
    puts("Friday");
    break;
  case 6:
    puts("Saturday");
    break;
  default:
    puts("not a day of week");
  }
}

このプログラムの実行結果は、以下のようになる。

week.c の実行例
$ ./week
year:  2002
month: 11
day:   5
Tuesday

□ 繰り返し

指定回数分の繰り返し: for

指定した回数分だけ、同じ手続きを繰り返す場合、for文を使用する。 for文の書式は以下のようになる。

for文の書式
for ( <初期条件>; <繰り返し条件>; <更新式> ) <手続き>

例えば、1からnまでの総和を求めるプログラムは、 for文を利用して以下のように記述できる。

for文を利用した総和を求めるプログラム: sum.c
/* sum.c */

#include <stdio.h>

int main(int argc, char **argv)
{
  int sum = 0, n, i;

  printf("n: ");
  scanf("%d", &n);

  for( i = 1; i <= n; i++ ) {
    sum += i;
  }

  printf("Summation from 1 to %d is %d\n", n, sum);
}

このプログラムの実行結果は、以下のようになる。

sum.c の実行例
$ ./sum
n: 10
Summation from 1 to 10 is 55

真偽条件に基づく繰り返し(1): while

ある条件が成立している間、同じ手続きを繰り返す場合、while文を使用する。 while文の書式は以下のようになる。

while文の書式
while ( <条件式> ) <手続き>

1からnまでの総和を求めるプログラムをwhile文を利用して 以下のように記述することができる。

while文を利用した総和を求めるプログラム: sum-by-while.c
/* sum-by-while.c */

#include <stdio.h>

int main(int argc, char **argv)
{
  int sum = 0, n, i = 1;

  printf("n: ");
  scanf("%d", &n);

  while ( i <= n ) {
    sum += i;
    i++;
  }

  printf("Summation from 1 to %d is %d\n", n, sum);
}
真偽条件に基づく繰り返し(2): do 〜 while

while文が、先ず条件式を評価し、 条件が成立していれば指定された手続きを実行するのに対し、 do 〜 while文では、先ず、指定された手続きを実行した後に、条件式の評価を行う。 これは、最低1回は手続きが実行されることを意味する。 do 〜 while文の書式は以下のようになる。

while文の書式
do <手続き> while ( <条件式> )

do 〜 while文を利用したプログラムの例を以下に示す。

do 〜 while文を利用したプログラム例: spoilt-child.c
/* spoilt-child.c */

#include <stdio.h>

int main(int argc, char **argv)
{
  char a[256];

  do {
    printf("Will you give me a candy? [y/n] ");
    scanf("%s", a);
  } while ( *a != 'y' && *a != 'Y' );

  puts("Thank you!!");
}

このプログラムの実行結果は、以下のようになる。

spoilt-child.c の実行例
$ ./spoilt-child
Will you give me a candy? [y/n] no
Will you give me a candy? [y/n] no!
Will you give me a candy? [y/n] No!
Will you give me a candy? [y/n] yes...
Thank you!!

レポート課題

(注) プログラムを作成する課題では、レポートとしてプログラムと実行結果を示せ。

■ レポート課題1

(1)

以下のように、標準入力より2つの実数を読み込み、 その四則演算結果をそれぞれ出力するプログラム four-arithmetic.cを作成せよ。 ただし、2番目の値として0が入力された場合には、 演算を実行せずにプログラムを終了するようにせよ。

four-arithmetic.c の実行例
$ ./four-arithmetic
Input first number: 1.5
Input second number: 0.3
1.500000 + 0.300000 = 1.800000
1.500000 - 0.300000 = 1.200000
1.500000 * 0.300000 = 0.450000
1.500000 / 0.300000 = 5.000000
$ ./four-arithmetic
Input first number: 2.0
Input second number: 0
Booom! Inputed number is Zero.

(2)

実部と虚部からなる複素数を示す構造体を定義し、 標準入力から入力された2つの複素数の 和と差の演算結果を求めるプログラムを作成せよ。 実行結果には、(3+2i )および(1-4i )を入力した結果を示せ。 ただし、複素数の入力は、実部と虚部をそれぞれ分けて入力すればよい。

■ レポート課題2

以下の各数列の和に対し、 標準入力より数列の個数n を入力すると その数列の和を求めるプログラムをそれぞれ作成せよ。 レポートには、n=5の結果を示せ。

(1)
n
Σ i 2
i = 1
, (2)
n
Σ i 3
i = 1
, (3)
n
Σ 3i -1
i = 1

なお、標準入力より整数を入力する場合には、 以下のようにscanf()を使用するとよい。

scanf("%d", &n);

■ レポート課題3

以下の各プログラムを作成し、 (1)100より大きい3の倍数、 (2)10,000円をユーロに変換した額、 (3)質量10kgの物質のエネルギーをそれぞれ求めよ。

  1. 入力された値より大きい3の倍数
  2. 為替レートを 1USD(アメリカドル)=110.43円、 1EUR(ユーロ)=128.55円、 1GBP(イギリスポンド)=184.78円、 1CHF(スイスフラン)=82.77円とし、 入力された円から、頭文字で指定された通貨へ変換
  3. 特殊相対性理論に基づく質量m [kg]と エネルギー量E [J]との関係式 E = m c 2 (ただし、c = 2.99792458 × 108[m/s])から、 入力された質量のエネルギー量を算出

■ レポート課題4

a=1, b=1, c=1であるとき、 以下の2つの論理式の真偽を自分の頭で計算し、求めよ。 また、プログラムを記述し、結果を比較せよ。

((a >= 0 || b == 1 ) && c > 2)

(a >= 0 || b == 1 && c > 2)

参考書籍

  1. B. W. カーニハン, D. M. リッチー 著, 石田 晴久 訳: 「プログラミング言語C 第2版 ANSI規格準拠」, 共立出版(株), ISBN4-320-02692-6, ¥2,800-
  2. 平林 雅英 著:「ANSI C/C++辞典」, ISBN4-320-02797-3, ¥6,500-

Last modified: Fri Oct 24 22:48:39 JST 2003