Part.6
配列変数
内容
- (1次元)配列の宣言と利用
- 演習課題たくさん
- 2次元配列
- 配列の動的確保(発展)
目標
配列変数とは何か?配列変数を使うメリットは?配列変数の使い方をマスターする.
理解度確認用問題
1.1から1000までの偶数の和、奇数の和をそれぞれ求め画面に表示するプログラムを作成せよ.また、求めた偶数の和と奇数の和を加えたものと、1から1000までの整数の和が等しいことをも示せ.
2.整数n (n>2) を入力すると、(1+2+..+n)+(2+...+n)+...+(n) を求め結果を画面に表示するプログラムを作成せよ.
配列変数の宣言と利用
C言語に限らず、ほとんどのプログラミング言語には配列変数と呼ばれるものがあります.配列変数は例えば次のように宣言することで用意することができます.
int i[5];
これにより、次のように整数型の変数が5個用意されることになります.
i[0], i[1], i[2], i[3], i[4]
かぎ括弧 [ ] で囲まれた数字は添字と呼ばれます. このように添字は 0 から始まるので注意が必要です.次のサンプルプログラムをPart6-1.c として打ち込んで実行してみてください.このプログラムは 1,2,3いずれかの数字を打ち込み、それぞれ何回入力されたかを数えるプログラムです.0 を入力することで入力を終えることができるようになっています.(途中 if 文の入れ子があります.入れ子構造を分かりやすくするために字下げが行われていることに注意してください.字下げによって、構造がよくわかるはずです.)
#include <stdio.h>
int main()
{
/* 整数型変数の宣言 */
int i, n, c[3];
/* 整数型配列の初期化を行う繰り返し文 */
for (i = 0; i < 3; i++)
{
c[i] = 0;
}
/* n の初期化(while 文の条件式をはじめから満たさないようにするため) */
n = -1;
/* 0 が入力されるまで入力を受けつづける繰り返し文 */
while (n != 0)
{
printf("1, 2, 3 いずれかを入力してください(0 で入力終わり): ");
scanf("%d", &n);
/* 入力された n の妥当性をチェック */
if ((n <=0) || (n > 3))
{
if(n == 0)
{
/* n が 0 だった場合 */
printf("0 が入力されました.入力を終わります.\n");
}
else
{
/* n の範囲が不当だった場合 */
printf("1, 2, 3 以外の数値が入力されました.無視します.\n");
}
}
else
{
/* n が 1,2,3 であった場合、c[n-1] の値を一つ増やす */
/* n-1 となっているのは、添え字が 0 から始まることによる */
c[n-1]++;
}
}
/* 結果を表示する繰り返し文 */
for (i = 0; i < 3; i++)
{
printf("%d が %d 回入力されました.\n", i+1, c[i]);
}
}
このように、配列変数の添字には整数型の変数が使えます.
(注意)配列の中身(値)は必ず初期化するべきです。言語によっては0があらかじめ入っている場合もありますが、明示的に入っておくべき値を入れるべきです。これは、通常の変数についても同様です。
整数型の配列以外にも実数型の配列も作ることができます.
double a[6];
と宣言することにより、
a[0], a[1], a[2], a[3], a[4], a[5]
と、6つの実数型変数を作ることができます.
2つの変数の数値データの入れ替え
場合によっては、2つの変数に記憶されている数値データを入れ替えたい場合があります.その場合には、入れ替えを補助する余分な変数を一つ用意する必要があります.次のプログラムは、実数型変数の内容を入れ替えるプログラムです.Part6-2.c として打ち込み実行してください.
#include <stdio.h>
int main()
{
double a, b, tmp;
a = 1.2;
b = 5.3;
/* 入れ替え前の状態を表示 */
printf("a = %lf, b = %lf\n", a, b);
/* 補助変数 tmp に a の内容を退避 */
tmp = a;
/* a に b の内容を代入 */
a = b;
/* b に退避しておいた a の内容を代入 */
b = tmp;
/* 入れ替え後の状態を表示 */
printf("a = %lf, b = %lf\n", a, b);
}
課題0
5つの整数を入力すると、まずはそれらを入力順に表示し、その後小さい順に並べ替え、小さい順に表示するプログラムを作成したい.次のプログラムは未完成であるが、太字のコメント部分に、dat[0]~dat[4] の内容を小さい順に並べ替える部分を追加し、プログラムを完成せよ.(変数 j, tmp は未完成のプログラム内では宣言はしてるが使っていない.並び替え時に必要であればそれらをつかっても良い.また、さらに変数が必要であれば、用意してもかまわない.)
#include <stdio.h>
main()
{
int i, j, dat[5], tmp;
for (i = 0; i < 5; i++)
{
printf("整数を5つ入力してください.(%d つ目) : ", i+1);
scanf("%d", &dat[i]);
}
printf("入力された5つの数字は : ");
for (i = 0; i < 5; i++)
{
printf("%d ", dat[i]);
}
printf("\n ");
/* dat[0]~dat[4] の内容を小さい順に並べ替える部分 */
printf("小さい順に並べ替えると : ");
for (i = 0; i < 5; i++)
{
printf("%d ", dat[i]);
}
printf("\n ");
}
課題1
実数を10個入力すると、そのうちの最大値と最小値を表示するプログラムを作成せよ.
課題2
整数 n を入力すると 1~n までの和を計算し表示するプログラムを作成せよ.
課題3
整数 n を入力すると 1~n までの積(階乗)を計算し表示するプログラムを作成せよ.
課題4
色々な多重ループ(3重ループや for文と while 文を組み合わせたものなど)のプログラムを作成せよ.
課題5
正の整数を1つ入力すると、その数が素数かどうかを判定して表示するプログラムを作成せよ.
課題6
正の整数 n を入力すると n 以下の素数を全て表示するプログラムを作成せよ.
課題7
フィボナッチ数列とは
F1 = 1, F2 = 1
からはじめて、漸化式
Fn+2 = Fn+1 + Fn, n = 1,2,3,.....
で作られる数列
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, ....
のことである.自然数 m を入力すると Fm+2 までのフィボナッチ数列を表示するプログラムを作成せよ.ただし、配列は使わないこと.また,配列を使ったアルゴリズムも考えてみよう.
課題8(多少難しい)
例えば、正の整数 3562 と入力すると
3, 5, 6, 2
3 + 5 + 6 + 2 = 16
と表示するプログラムを作成せよ.
また、
3, 5, 6, 2
3 + 5 + 6 + 2 = 16
と表示する必要は無く、3 + 5 + 6 + 2 の計算結果のみを表示する場合も考えよ(表示をする必要がないことからアルゴリズムはより簡単になるだろう).双方とも配列は使わないこと.また,配列を使ったアルゴリズムも考えてみよう.
課題9(難しい)
課題7のフィボナッチ数列のカンマを取り去った次のような数字の羅列を考える.
11235813213455......
さらに、この羅列を次のように数字一つ一つに分割した数列を考える.
1,1,2,3,5,8,1,3,2,1,3,4,5,5,......
この数列には 0 ~ 9 の数字が現れるが、それらの出現回数を正の整数 m を入力した場合にもとまるフィボナッチ数列に関して求めるプログラムを作成せよ.はたして0~9の数字は等確率で出現するのだろうか?私は答えを知らないのでわかったら教えてください.
課題10
3×3行列を考える.それぞれの要素は1,2,3,4,5,6,7,8,9から1つずつ選び、重複はないものとする.このとき、すべての組み合わせの行列を求め、それを画面に表示するプログラムを作成せよ.
2次元配列
double 型の2次元配列は次のように宣言します。
double A[100][100];
これで、100x100の配列を作成できます。配列の要素は A[1][4] といったふうに参照できます。これで行列Aの1行4列目の要素を参照できます。(もちろん大きさには制限があります。あまりに巨大なものは扱えません。)
double B[101][101];
と宣言すると、B[0][0], B[0][1], ...., B[0][99],.....,B[100][100] と101x101の配列となります。
上記Aの内容を全て 1.1 にするには、次の様な for 文を使います(コードは一部を示す)。
int i, j;
for(i = 0; i < 100; i++) {
for(j = 0; j < 100; j++) {
A[i][j] = 1.1;
}
}
配列の動的確保(発展)
int a[10];
という書き方は、配列の静的確保と呼ばれます。例えば、プログラムにおいて最初から必要な配列の個数がわかっていればそれで良いですが、実行してみてからはじめて必要な要素数が決まる場合もあるでしょう。また、近年の傾向として、静的なメモリ確保はあまりやらないようです。ほぼおなじ結果となる静的な確保の書き方と、動的な確保の書き方を並べて書いておきます。動的確保を用いる場合には 、プログラムの先頭あたりに次の文を追加して下さい。
#include <stdlib.h>
静的確保
int a[100];
動的確保
int *a;
a = (int *)malloc(sizeof(int)*100);
(注意)上記どちらの宣言であったとしても、 a[4] のような使い方ができる。
静的確保
double b[10][10];
動的確保
int i;
double **b;
b = (double **)malloc(sizeof(double *)*10);
for(i = 0; i < 10; i++) {
b[i] = (double *)malloc(sizeof(double)*10);
}
(注意)上記どちらの宣言であったとしても、 b[5][6] のような使い方ができる。
(参考)1次元配列を使って多次元配列
一般的な方法では無いと思うが、私はC言語における多次元配列の扱いがあまり好きではないので、次の様に1次元配列を多次元配列のごとく使う事がよくある。参考にして欲しい。
例えば、N*M の二次元配列が欲しいとき(double b[N][M] のようなものが欲しいとき)、次の様に #define を用いたマクロをつくって、二次元配列的に1次元配列を使える。(#define は定数の定義として Part.8 にでてくる)
#define N (100)
#define M (50)
#define A(i, j) a[i*(N)+j]
int main() {
double *a;
a = (double *)malloc(sizeof(double)*N*M);
このように宣言しておいて、例えば、
A(5, 4) = 4.3;
のように使える。
Part6で学んだ事
- 配列変数を使えるようになりました