第 10 回 配列

本日の内容


このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。

10-1. 配列

宣言文での初期化

C 言語では宣言文で変数の値を代入(初期化)できます。


int i=0;
float a=2.3;
char d='F';

配列

C 言語で数列を取り扱うために配列という概念が用意されています。 添字は角括弧「[ ]」で括ります。 つまり、a0, a1, a2 はそれぞれ a[0], a[1], a[2] と表します。 aiは a[i] と表します。 ただし、添字に使用できる変数は整数型(int)だけです。 さらに添字には a[i+1] や b[j*2] など、整数を値に持つ式を書くことができます。

配列を使う時、宣言は次のようにします。


int a[5];
char b[3];
float c[4];

するとそれぞれ、次の変数が使えるようになります。


a[0], a[1], a[2], a[3], a[4]
b[0], b[1], b[2]
c[0], c[1], c[2], c[3]

C++ や 1999 年に改訂された C99 という規格の C 言語では宣言文はプログラ ム中のどこにでも置けましたが、 本講義で取り上げている C 言語(ANSI C) では宣言文はプログラムの冒頭にし か置けません。 したがって、特定の計算の結果求まった値や入力値の数だけ配列変数を用意す ることはできません。 但し、現在流通している C コンパイラで C99 を準拠しているものは少ないで すが、C++ に対応しているものがほとんどです。 したがって、冒頭以外で宣言をしても文法のチェックに引っかからずそのまま コンパイルできてしまうコンパイラがほとんどです。 実際に使う場合は一般のコンパイラと本来のルールが乖離していて、どこでも 正常にコンパイルが出来る保証がないことを意識して下さい。

プログラムでは、配列はそれぞれ普通の変数として使用することができます。 但し、実行中に配列の値をまとめて代入することはできません。 但し、初期化する時だけ、次のようにまとめて値をセットできます。


int a[5]={ 3, 4, 6, 0, -1 };

このようにすると 次のように値が代入されます。


a[0]= 3,
a[1]= 4,
a[2]= 6,
a[3]= 0,
a[4]=-1

さらに値をまとめてセットする場合、要素数の指定を省略することができます。


float b[]={ 1.3, -3e-2, -5.0 };

このようにすると次のように値が代入されます。


b[0]= 1.3,
b[1]=-3e-2,
b[2]=-5.0

なお、配列の初期化の値が足りない場合、残りの要素は 0 に初期化されます。 したがって、配列全てを 0 に初期化したい場合は次のようにします。


int b[100]={0};

例10-1

配列変数の内容を表示するプログラムは次のように作ります。 まず、整数型のループ変数を用意し(ここではループ変数の名前を i とします)、 値を 0 にセットします。 そして、 while 文の中に入ります。 ループ変数 i が配列の要素数以下の時に繰り返します。 繰返し行う処理は printf 文で配列の i 番目の要素を出力することで、 出力が終ったらループ変数 i を一つ増やします。

このようにして作成したのが以下のプログラムです。 但し、#define X Y はプログラム中で Y を X という 名前で使うことを意味します。

#include <stdio.h>
#define N 6
main(){
  float a[N]={2, 1, 5, 6, 9, 2};
  int i;
  i=0;
  while(i<N){
    printf("a[%d] の値は %f\n",i,a[i]);
    i++;
  }
}

例10-2

配列変数の値の合計を得るには次のようにします。 まず、ループ変数と集計用の変数を用意します。 集計用の変数を 0 にし、ループ変数も 0 にします。 そして while 文を使い、ループ変数が配列変数の最大添字を越えない限り繰 り返します。 ここで繰返し行う処理は数列の和を求めるための漸化式 Sn = Sn-1 + an を利用して配列の各要素の値から合計値を計算します。 つまり、 s=s+a[n] を行うことで s を a[n] だけ増やします。 但し、 C 言語は代入演算子 += を使用できますので、これを用いて集計用の 変数を配列変数の要素分だけ増やします。 while 文を抜けたら、集計用の変数に合計が求まっていますので、それを出力 します。 このようにして作成したのが次のプログラムです。


#include <stdio.h>
#define N 5
main(){
  int i;
  float a[N]={2.3, 4.4, 3.9, -1.2, 0.3};
  float s;
  i=0;
  s=0;
  while(i<N){
    printf("a[%1d]=%f\n",i,a[i]);
    s+=a[i];
    i++;
  }
  printf("合計: %f\n",s);
}

なお、結果が予想と異なる場合、 float の代わりに double を使用して結果 を比較しなさい。


10-2. 番兵

while や for の繰返しをコントロールするため、ループ変数という専用の変 数を用意し、何回繰り返したかを数えてコントロールする手法を学びました。

ここでは数を数える代わりに、新たなテクニック(アルゴリズム)として、デー タの列の中に列の終りを意味する値を付加して、プログラムの流れを変えるこ とを学びます。 そのようなデータの終りを示すデータのことを番兵と呼びます。 ループ変数ではあらかじめ全体のデータの数がわからないと繰り返しを止める ことが出来ませんでしたが、番兵を用いるとデータの最後をデータ自身が教え てくれるため、データの数をあらかじめ知る必要もなく、またプログラムが簡 潔になります。

例10-3

配列の要素数を数えるプログラムは次のようにします。 まず、数を数える変数(カウンタ)を 0 にし、配列をアクセスする変数を 0 に セットします。 そして、while 文を使って配列の要素が番兵になるまで繰り返します。 各配列要素に対してアクセスするたびにカウンタを一ずつ増やします。 すると、全配列要素にアクセスし終るとカウンタが要素数を示すことになりま す。 最後にカウンタの値を出力します。 このようにして作成したプログラムを次に示します。


#include <stdio.h>
main(){
  int a[]={5,3,10,2,0};
  int i=0;
  int counter=0;
  while(a[i]!=0){
    counter++;
    i++;
  }
  printf("配列の要素数は %d\n",counter);
}

例10-4

配列の要素に対してある条件を満たすものだけ処理を適応するには次のように します。まず、前例同様、配列をアクセスする変数を 0 にセットします。 そして、while 文を使って配列の要素が番兵になるまで繰り返します。 各配列要素に対してif 文を使ってアクセスし、条件を満たす時だけ処理を行 います。 すると、各配列要素に対して条件を調べ、適応した物だけ処理ができます。 例えば、偶数だけ表示し、最後に偶数の個数を表示するプログラムを次 に示します。


#include <stdio.h>
main(){
  int a[]={5,3,10,2,0};
  int i=0;
  int count=0;
  while(a[i]!=0){
    if(a[i]%2==0){
      printf("%d ",a[i]);
      count++;
    }
    i++;
  }
  printf("偶数は %d 個\n",count);
}

10-3. 付録

練習問題

演習10-1

以下の方程式 a0 + a1x = 0 ( a1≠0 ) の解を求めるプログラムを完成させなさい。 そして、以下の方程式をこのプログラムで解きなさい。

  1. 2 + x = 0
  2. 2 - 4 x = 0
  3. 0 - 3 x = 0

#include <stdio.h>
main(){
  float a[]={2,1};
  float x;
  /* x の計算 */
  /* printf("方程式 %f + %f x = 0 の解は %f\n", ... );*/
}
参考

なお、% と f の間に + 記号を入れると値の正負に関わらず符号を出力する。 したがって、 printf("方程式 %f%+fx = 0 の解は %f\n", ... ) と書くと値 が負の時に +-0.3 のような表示にならずに済む。


演習10-2

以下の入力された配列変数を表示するプログラムを完成させなさい。 但し、繰返しは while ではなく for を使用しなさい。


#include <stdio.h>
#define N 6
main(){
  float a[N]={2, 1, 5, 6, 9, 2};
  int i;
  /* for( ; ; ){ */
  /* printf("a[%d] の値は %f\n",...); */
  /* } */
}

演習10-3

演習10-2 のプログラムを雛型にして、 配列の平均値を出力するプログラムを作りなさい。但し、繰返しは while を 使わず、 for を使用しなさい。


演習10-4

次のプログラムを完成させ、正の値が出力されるようにしなさい。


#include <stdio.h>
main(){
  int a[]={5,3,10,2,0};
  int i=0;
  /* a[i] の値が 0 でないとき以下を繰り返す */
  {
    /* i 番目の配列の要素を表示する */
    /* i を 1 増やす */
  }
}

演習10-5

次のプログラムを完成させ、配列の中の正の整数の中に偶数がいくつあるか表 示しなさい。配列の最後は 0 で終るものとします。 なお、特定の条件を満たすものを数えるには繰返し文の中に if 文を入れ、条 件が成立した要素だけカウンタを増やすようにします。


#include <stdio.h>
main(){
  int a[]={5,3,10,2,0};
  int n=0;
  int i;
  /* i を 0 にする */
  /* a[i] の値が 0 でないとき以下を繰り返す */
  {
    if(/* a[i] が偶数の時*/){
      /* 数を数える */
    }
    /* i を 1 増やす */
  }
  printf("与えられた入力中に偶数は %d 個\n",n);
}

演習10-6

番兵を使って次の正の値の列の合計が出力されるプログラムを書きなさい。

{2, 7, 1, 3, 9, 0}

演習10-7

番兵を使って次の正の値の列の平均を出力するプログラムを書きなさい。

{2.1, 7.5, 1.2, 3.4, 9.9, 0}
ヒント

列の長さはあらかじめ決められないので、番兵が出てくるまで要素をひとつひ とつ数えて配列のサイズを求める。


坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科