第 11 回 まとめ

本日の内容


11-1. C 言語の構造

プログラムの全体の構造

C 言語のプログラムは最後が「.c」で終るテキストファイルに書きます。 本講義ではプログラムの構造として次の二つを取り上げました。

構造1
#include <stdio.h>
main(){
  ここに文を書く
}
構造2
#include <stdio.h>
#include <math.h>
main(){
  ここに文を書く
}

構造 2 は平方根 sqrt() や三角関数 sin() など数学的な関数を使用する時だ け使い、通常は構造 1 を使います。

文はセミコロン「;」で区切ります。

またプログラムの任意の場所に /* で始まり */ で終るコメントを書くことが できます。

プログラムの組み立て

プログラムは宣言をするところと、手順を記述するところにわけることができ ます。

構造3
#include <stdio.h>
main(){
  宣言

  手順
}

宣言

宣言とは変数の登録をすることです。 変数には型と名前があります。 型は文字型 char、整数型 int、単精度浮動小数点型 float、倍精度浮動小数 点型 double があります。 名前は英字で始まる任意の文字列を使用できます。 文字型とは文字一文字のことで、シングルクォーテーションマーク「' '」で 括ります。 改行記号は '\n' で表します。

int i,j;
float kotae1,kotae2;
char a,b,c;

また、通常の変数の他に配列変数を扱うこともできます。 これは、数学で扱う数列 a0, a1 のように、添字の番 号を指定して a[0], a[1] のように値を呼び出すことができます。 宣言する時は使用する数を与える必要があります。 但し、添字は 0 から始まりますので、5 個の配列を宣言した時、使用できる 添字は 0,1,2,3,4 になります。 手続き中の配列変数の添字には整数型の変数を使用することもできます。

int x[3]; /* 使用できる変数名は x[0], x[1], x[2] */
char word[2]; /* 使用できる変数名は word[0], word[1] */

宣言では変数の最初の値(初期値)を与えることができます。 配列変数では 0 番目の値から順に { } の中にカンマで区切って与えることが できます。{ } で値を与える場合、使用する配列の数を与えなくても良いです。

int i=0;
char a='x';
float y[2]={1.3 , 3.5}; /* y[0]=1.3, y[1]=3.5 */
int k[]={3, 2, 1}; /* k[0]=3, k[1]=2, k[2]=1 */

文字型の配列変数は文字列を使用するのに使うことができます。 その場合、文字列の最後に必ず特別な文字 '\0' を入れる必要があります。 また、文字型の配列変数の初期化には上記のような { } を使用する代わりに、 文字列を " " で括る方法もあります。この場合最後に自動的に '\0' が割り 当てられます。

char x[]="abc" /* x[0]='a', x[1]='b', x[2]='c', x[3]='\0' */

手順

本講義では手順として次のものを取り上げました。

  1. 代入文
  2. printf
  3. if
  4. while

代入文

代入文は「変数名 = 式」という形式で書き、式の値が変数に入ります。 ですから、「 i = i+1 」という代入文では i+1 の値が i に入りますから、 i の値が 1 増えます。 式には通常の四則演算の他、数学関数も使用できます。 但し、演算の優先順位を与えるカッコは丸カッコ ( ) しか使用できません。 また、変数を含む式は変数の型が考慮されます。整数型の変数 a,b にそれぞ れ 7,2 が入っていた時、 a/b の結果は整数型に直され、3 になります。

a = ((1+2)*3+4)*5;
x[2] = sqrt(y[1]);

C 言語ではあらゆるものが値を持ちます。代入文自体も代入された値を値とし て持つので、次のような記法が可能になります。

i=j=0;

C 言語独特の記法として代入演算子やインクリメント、デクリメントがありま す。代入演算子「変数 += 式」は式の値だけ左辺の変数を増やすという意味に なります。また、インクリメント、デクリメントは i++, --i などで、それぞ れ i が 1 ずつ増減します。但し、++, -- が先頭に来る場合の式の値は増え た後の値になりますが、後に来る場合は式の値は増える前の値になります。

s+=a[i];
z[i++]=y[j++];

printf

標準出力に文字を出力するのが printf です。指定した文字を出力するだけで なく、変数の内容も表示できます。 printf の最初の引数は文字列で、基本的にはその内容が表示されます。

printf("Hello World\n");

文字列の中に 「%+文字」 が含まれると、その位置に変数の値が表示されます。 文字型変数には %c, 整数型変数には %d, 浮動小数点型には(float も double も) %f を使用します。

char x='a';
int y=5;
float z=6.2;
printf("変数 x の値は %c",x);
printf("変数 y の値は %d, 変数 z の値は %f",y,z);

文字型の配列変数の内容を全て表示するには %s を使用します。 但し、配列の最後には '\0' が割り当てられている必要があります。

char x[]="abc"; /* x[0]='a', x[1]='b', x[2]='c', x[3]='\0' */
printf("配列変数 x には文字列 %s が入っている",x);

if

if 文には次の二つの構文があります。

if(条件){文1;}
if(条件){文2;}else{文3;}

初めの構文は条件が成立する時(0 でない時)だけ文1を実行し、成立しなかっ た時(0 だった時)はなにもしません。 二番目の構文は条件が成立する時(0 でない時)は文2を実行し、成立しなかっ た時(0 だった時)は文2を実行します。

条件式

二つの値を比較をする条件式には次の書き方があります。

a == b
a と b は等しい
a != b
a と b は等しくない
a < b
a は b より小さい
a > b
a は b より大きい
a <= b
a は b 以下
a >= b
a は b 以上

これらの値は条件が成立すれば 1、成立しなければ 0 になります。

if(x>max){max=x;}

if(b==0){printf("全ての値\n");}
else{printf("解なし\n");}

二つの条件式を一つにまとめるため次の書き方があります。

(A) && (B)
A と B の両方が成り立つ(両方が 0 でない)
(A)||(B)
A か B のどちらかが成り立つ(どちらかが 0 でない)
!(A)
A が成り立たない(A が 0 である)
if((a==0)&&(b!=0)){printf("解なし");}
if((a!=0)||(b!=0)){x=-b/a;}

while

while 文の構文は if と良く似ています。

while(条件){文;}

「条件を調べて、成立すれば文を実行する」というのを繰り返します。 適切な回数だけ繰り返させるためには何らかのアルゴリズムが必要になります。

なお、for 文は while 文を読みやすくするために導入された別の書き方です。 次の for と while は同じ意味になります。

for(A;B;C){
  D;
}

A;
while(B){
  D;
  C;
}

#define

#define を使用すると、定数を使用することができます(実際はマクロの定義)。 次のように使用します。

#include <stdio.h>
#define N 3
main(){
  int x[N]={7, 3, 4};
  int i;

  for(i=0;i<N;++i){
    printf("%d\n",x[i]);
  }
}

11-2. 取り上げたアルゴリズム

ここでは数学の問題の解法ではない、純粋にプログラミングのためのアルゴリ ズムを取り上げます。

while 文を制御し、適切な回数だけ繰り返すために二つのテクニックを取り上 げました。

さらに繰り返し計算の中の計算方法として次のような処理を取り上げました。

ループ変数

ループ変数とは、繰り返しの中で徐々に値が変化する変数です。 その変数の値に関する条件により、繰り返しを制御します。

for(i=0;i<5;++i){
  文;
}

i=0;
while(i<5){
  文;
  ++i;
}

この for と while はそれぞれ同じ意味になります。 どちらもループ変数 i の値を始めに 0 にしています。 そして、i が 5 より小さい間、文を実行し、i を一つずつ増やしていきます。 つまり次のように実行されます。

このようにループ変数を使うことで、 5 回の繰り返しが可能になります。 一方、文を実行する際に i の値が一つずつ増えています。 文で i を参照すると、単なる繰り返しではなく、i の値に応じた処理をする ことが可能になります。

単なる繰り返し
#include <stdio.h>
main(){
  int i;

  for(i=0;i<5;++i){
    printf("Hello!\n");
  }
}
i の単純な参照
#include <stdio.h>
main(){
  int i;

  for(i=0;i<5;++i){
    printf("%d 回目\n",i);
  }
}
配列変数の i 番目の参照
#include <stdio.h>
main(){
  int i;
  float x[5]={5.3, 2.2, 1.5, 4.5, 7.1};

  for(i=0;i<5;++i){
    printf("配列変数 x[%d] の値は %f\n",i,x[i]);
  }
}

番兵

データの列の終りを示す特別な値を番兵といいます。 番兵をデータの列に入れておき、プログラムで検出することにより繰り返しを 終了させることができます。 配列、文字列、ファイルに対してとりあげました。 プログラムの形としては次のようになります。

while(読んだデータ != 番兵){
  読んだデータの処理
}

次のプログラムは配列変数の最後に番兵を入れる例です。この例では番兵は 0 になります。

#include <stdio.h>
main(){
  int i;
  float x[6]={5.3, 2.2, 1.5, 4.5, 7.1, 0};
  for(i=0;x[i]!=0;++i){
    printf("配列変数 x[%d] の値は %f\n",i,x[i]);
  }
}

文字列を扱う場合、番兵は '\0' という特別な値でした。

#include <stdio.h>
main(){
  int i;
  char x[]="abcdef";
  for(i=0;x[i]!='\0';++i){
    printf("文字列 x の %d 番めの文字は %c\n",i,x[i]);
  }
}

ファイルの場合、番兵は EOF になります。 getchar() により一文字ずつ読み込むので、while 文の条件文の中で文字を読 みます。 配列と違って添字がなくても次の文字を読むことができるのでループ変数は必 要ありません。

#include <stdio.h>
main(){
  char x;
  while((x=getchar())!=EOF){
    printf("読み込んだ文字は %c\n",x);
  }
}

漸化式

繰り返し構文の中では「直前の値」から「直後の値」への計算を与えると、繰 り返しにより全体の計算ができるようになります。 数学ではこのような考え方を漸化式と言います。

漸化式をたてることができると、繰り返しにより値を求めることができます。 例として、1 から n までの和 1 + 2 + 3 + 4 + ... + n を考えます。 i-1 番目までの和 1 + 2 + ... + (i-1) を s とすると i 番目までの和は (1 + 2 + ... + (i-1)) + i = s + i になります。 数学では si=si-1 + i と書きますが、 while 文の中 では「s=s+i」と書いて古い s の値から新しい s の値を計算することが できます。

#include <stdio.h>
main(){
  int i;
  int s;
  s=0;
  for(i=1;i<=5;++i){
    s=s+i;
  }
  printf("合計 %d\n",s);
}

なお C 言語ではさらにこの「s=s+i」という式を「s+=i」と書くことができま す。

配列変数 x[0], ..., x[n-1] までの和 x[0] + x[1] + ... + x[n-1] を求めるにはどうすれば良いでしょうか? これも同様に i-1 番目までの和 x[0] + x[1] + ... + x[i-1] を s とすると、 i 番目までの和は (x[0] + x[1] + ... + x[i-1]) + x[i] より、 s + x[i] になります。従ってプログラムは次のようになります。

#include <stdio.h>
main(){
  int i;
  int n=5;
  float x[5]={5.3, 2.2, 1.5, 4.5, 7.1};
  float s;
  s=0;
  for(i=0;i<5;++i){
    s+=x[i];
  }
  printf("合計 %f\n",s);
}

このようにすると i が 0 から n-1 まで増える間に s+=x[i] が実行されるの で、s+=x[0], s+=x[1], s+=x[2], s+=x[3], s+=x[4] が実行されます。 つまり、s は x[0] から x[4] までの値を一回ずつ足されることになります。

数を数える

文字列の中に含まれている特定の文字などを数えるなど、「数を数える」とい う問題の解き方を考えましょう。 これは人間が行うのと同じく、「一つ一つ順にデータを見て、条件が合えば指 を折る」ように処理します。 つまり、繰り返しの中でデータを一つずつ見て、条件にあった時だけ数を数え るようにします。そのため、数を数えるための変数を別に用意します。 そして、繰り返しの中で if 文により条件を判断し条件にあった時だけ変数を 1 増やします。下のプログラムは文字配列中で使用されている文字 'a' の頻 度を表示します。

#include <stdio.h>
main(){
  char x[]="This is a pen.";
  int i;
  int n;

  n=0;
  for(i=0;x[i]!='\0';++i){
    if(x[i]=='a'){
       ++n;
    }
  }
  printf("文字列 %s 中に a は %d 回出現\n",x,n);
}

ファイルの中の特定の文字の文字数を数えるには次のようにします。

#include <stdio.h>
main(){
  char c;
  int n;

  n=0;
  while((c=getchar())!=EOF){
    if(c=='a'){
       ++n;
    }
  }
  printf("\n"); /* MS-DOS プロンプトのバグ対策 */
  printf("a は %d 回出現\n",n);
}

11-3. 演習

演習11-1

画面に abcdefg を印刷するプログラムを書きなさい。

演習11-2

整数型変数iは次のように定義されているとします。

int i=23;

その時、画面にiの内容を表示するプログラムを書きなさい。 但し、出力は次のようになるのが望ましい。


The value of i is 23.

演習11-3

浮動小数点型変数xは次のように定義されているとします。

float x=0.314E1;

その時、画面にxの内容を表示するプログラムを書きなさい。 但し、出力は次のようになるのが望ましい。


変数 x の値は 3.14000.

演習11-4

文字型変数cは次のように定義されているとします。

char c='a';

その時、画面にcの内容を表示するプログラムを書きなさい。 但し、出力は次のようになるのが望ましい。


文字変数 c の値は 'a' である。

演習11-5

文字型の配列変数sは次のように定義されているとします。

char s[]="abcdefg";

その時、画面にsの内容を表示するプログラムを書きなさい。 但し、出力は次のようになるのが望ましい。


配列変数 s は文字列として abcdefg という値を持つ。

演習11-6

変数i の値が 0 より大きかったら画面に「正の値」、0 より小 さかったら画面に「負の値」、 0 だったら「零」を表示するプログラムを書 きなさい。

(但し、あらかじめiには次のように値が代入されているとする。)

int i=3;

演習11-7

あらかじめ浮動小数点の変数a, bには次のように値が与えられ ているとします。

float a=3.0;
float b=6.0;

このとき、a≠0であるとき、一次方程式

a x + b = 0

の解を求めて表示しなさい。

演習11-8

あらかじめ浮動小数点の変数a, bには次のように値が与えられ ているとします。

float a=3.0;
float b=6.0;

この時、式

a x + b = 0

の解を求めて表示しなさい。 但し、aが 0 であるときも考慮しなさい。

演習11-9

整数変数nは次のように値が代入されているとします。

int n=50;

このとき、画面に1からnまでを表示するプログラ ムを書きなさい。

演習11-10

整数変数nは次のように値が代入されているとします。

int n=50;

このとき、画面に1からnまでの和、つまり

1+2+3+...+n

の値を表示するプログラムを書きなさい。

演習11-11

整数変数の配列に次のように値が代入されているとします。

int a[10]={5,4,2,0,1,9,7,3,8,6};

この時、画面に a[0] から a[9] までの値を表示するプログラムを書きなさい。

演習11-12

整数変数の配列に次のように値が代入されているとします。

int a[10]={5,4,2,0,1,9,7,3,8,6};

この時、画面に a[0] から a[9] までの合計を表示するプログラムを書きなさ い。

演習11-13

浮動小数点変数の配列に次のように値が代入されているとします。

float x[10]={5.0,4.0,2.0,0.0,1.0,9.0,7.0,3.0,8.0,6.0};

この時、画面に x[0] から x[9] までの平均を表示するプログラムを書きなさ い。

演習11-14

文字列が次のように文字型配列変数に与えられているとします。

int k=6;
char a[]="This is a sample string.";

このとき、文字列の k 文字目(つまり 6 文字目)を出力するプログラムを書き なさい。

演習11-15

文字列が次のように文字型配列変数に与えられているとします。

char a[]="This is a sample string.";

このとき、この文字列の長さを出力するプログラムを書きなさい。

演習11-16

文字列が次のように文字型配列変数に与えられているとします。

char a[]="This is a sample string.";

このとき、この文字列を縦書きに出力するプログラムを書きなさい。

演習11-17

文字列が次のように文字型配列変数に与えられているとします。

char c='i';
char a[]="This is a sample string.";

このとき、この文字列中に変数 c の文字(つまり「i」)がいくつ使われている かを出力するプログラムを書きなさい。

演習11-18

次のように文字型配列変数が与えられているとします。

char a[]="This is a sample string.";
char b[100];

このとき、配列変数 a[] の内容を b[] にコピーしてから、 b の内容を表示 するプログラムを書きなさい。

演習11-19

文字列が次のように文字型配列変数に与えられているとします。

char c='i';
char a[]="This is a sample string.";

このとき、この文字列中に変数 c の文字(つまり「i」)が使われている 場所を下のように指摘するプログラムを書きなさい。

This is a sample string.
  ^  ^              ^

演習11-20

文字列が次のように文字型配列変数に与えられているとします。

char c='m';
char a[]="This is a sample string.";

このとき、この文字列中で最初に変数 c の文字(つまり「m」)が使われている 文字が何番目かを出力するプログラムを書きなさい。

演習11-21

標準入力から文字を読み込み、縦書きに出力するプログラムを書きなさい(改行は考 慮しなくて良い)。

演習11-22

標準入力から文字を読み込み、文字数を出力するプログラムを書きなさい。

演習11-23

標準入力から文字を読み込み、6 文字目を出力するプログラムを書きなさい。

演習11-24

文字変数に文字が与えられているとします。

char c='i';

このとき、ファイル中にこの変数 c の文字(つまり「i」)がいくつ使われてい るかを出力するプログラムを書きなさい。

11-4. 解答例

演習11-1

#include <stdio.h>
main(){
  printf("abcdefg");
}

演習11-2

#include <stdio.h>
main(){
  int i=23;
  printf("The value of i is %d",i);
}

演習11-3

#include <stdio.h>
main(){
  float x=0.314E1;
  printf("変数 x の値は %f",x);
}

演習11-4

文字変数 c の値は 'a' である。
#include <stdio.h>
main(){
  char c='a'
  printf("文字変数 c の値は %c である。",c);
}

演習11-5

#include <stdio.h>
main(){
  char s[]="abcdefg";
  printf("配列変数 s は文字列として %s という値を持つ。",s);
}

演習11-6

#include <stdio.h>
main(){
  int i=3;
  if(i>0){
    printf("正の値");
  }else{ 
    if(i<0){
      printf("負の値");
    }else{
      if(i==0){
        printf("零");
      }
    }
  }
}

なお中カッコ { } 自体は中の文が一つの場合省略できるのでこれは次のよう にも書ける。

#include <stdio.h>
main(){
  int i=3;
  if(i>0){
    printf("正の値");
  }else if(i<0){
    printf("負の値");
  }else if(i==0){
    printf("零");
  }
}

演習11-7

#include <stdio.h>
main(){
  float a=3.0;
  float b=6.0;
  float x;
  x=-b/a;
  printf("%f",x);
}

演習11-8

#include <stdio.h>
main(){
  float a=3.0;
  float b=6.0;
  float x;

  if(a==0){
    if(b==0){
      printf("全ての値");
    }else{
      printf("解なし");
  }else{
    x=-b/a;
    printf("%f",x);
  }
}

演習11-9

#include <stdio.h>
main(){
  int n=50;
  int i;
  for(i=1;i<=n;++i){
    printf("%d ",i);
  }
}

演習11-10

#include <stdio.h>
main(){
  int n=50;
  int i,s;
  s=0;
  for(i=1;i<=n;++i){
    s+=i;
  }
  printf("%d",s);
}

演習11-11

#include <stdio.h>
main(){
  int a[10]={5,4,2,0,1,9,7,3,8,6};
  int i;
  for(i=0;i<=9;++i){
    printf("%d ",a[i]);
  }
}

演習11-12

#include <stdio.h>
main(){
  int a[10]={5,4,2,0,1,9,7,3,8,6};
  int i,s;
  s=0;
  for(i=0;i<=9;++i){
    s+=a[i];
  }
  printf("%d",s);
}

演習11-13

#include <stdio.h>
main(){
  float x[10]={5.0,4.0,2.0,0.0,1.0,9.0,7.0,3.0,8.0,6.0};
  float s;
  int i;
  s=0;
  for(i=0;i<=9;++i){
    s+=x[i];
  }
  printf("%f",s/10);
}

演習11-14

#include <stdio.h>
main(){
  int k=6;
  char a[]="This is a sample string.";
  printf("%c",a[k-1]);
}

演習11-15

#include <stdio.h>
main(){
  char a[]="This is a sample string.";
  int i;
  i=0;
  while(a[i]!='\0'){
    ++i;
  }
  printf("%d",i);
}

演習11-16

#include <stdio.h>
main(){
  char a[]="This is a sample string.";
  int i;
  for(i=0;a[i]!='\0';++i){
    printf("%c\n",a[i]);
  }
}

演習11-17

#include <stdio.h>
main(){
  char c='i';
  char a[]="This is a sample string.";
  int i,n;
  n=0;
  for(i=0;a[i]!='\0';++i){
    if(a[i]==c){
      ++n;
    }
  }
  printf("%d\n",n);
}

演習11-18

#include <stdio.h>
main(){
  char a[]="This is a sample string.";
  char b[100];
  int i;

  for(i=0;a[i]!='\0';++i){
    b[i]=a[i];
  }
  b[i]='\0';
  printf("%s\n",b);
}

演習11-19

#include <stdio.h>
main(){
  char c='i';
  char a[]="This is a sample string.";
  char b[100];
  int i;

  printf("%s\n",a);
  for(i=0;a[i]!='\0';++i){
    if(a[i]==c){
      b[i]='^';
    }else{
      b[i]=' ';
    }
  }
  b[i]='\0';
  printf("%s\n",b);
}

演習11-20

#include <stdio.h>
main(){
  char c='m';
  char a[]="This is a sample string.";
  int i;

  for(i=0;a[i]!=c;++i){
  }
  printf("%d\n",i);
}

但し、これだと指定した文字が含まれていない時動作がおかしくなるので、正 しくは次のように書く。

#include <stdio.h>
main(){
  char c='m';
  char a[]="This is a sample string.";
  int i;

  for(i=0;(a[i]!=c)&&(a[i]!='\0');++i){
  }
  if(a[i]=='\0'){
    printf("使用されてません");
  }else{
    printf("%d\n",i);
  }
}

演習11-21

#include <stdio.h>
main(){
  char c;
  while((c=getchar())!=EOF){
    printf("%c\n",c);
  }
}

演習11-22

#include <stdio.h>
main(){
  char c;
  int i;

  i=0;
  while((c=getchar())!=EOF){
    ++i;
  }
  printf("\n"); /* MS-DOS プロンプトのバグ対策 */
  printf("%d\n",i);
}

演習11-23

#include <stdio.h>
main(){
  char c;
  int i;

  i=0;
  while((c=getchar())!=EOF){
    ++i;
    if(i==6){
      printf("\n%c\n",c);
    }
  }
}

演習11-24

#include <stdio.h>
main(){
  char c='i';
  char x;
  int i;

  i=0;
  while((x=getchar())!=EOF){
    if(x==c){
      ++i;
    }
  }
  printf("\n"); /* MS-DOS プロンプトのバグ対策 */
  printf("%d\n",i);
}

坂本直志 <[email protected]>
東京電機大学工学部情報通信工学科