| title | 配列 |
|---|---|
| slug | http://localhost:4321/textbook/c-lang/beginner/array |
import { Aside } from '@astrojs/starlight/components'; import { Tabs, TabItem } from '@astrojs/starlight/components'; import { Card } from '@astrojs/starlight/components';
同じ型の変数の集まりは、ひとまとめにして表現することができます。 そのために利用する配列の基礎について学んでいきましょう。
まずは、3人の学生の点数について、その合計値と平均値を算出するプログラムlist16_01.cを作成してみましょう。
#include <stdio.h>
int main(void) {
int score1 = 70; // 1 人目の点数
int score2 = 100; // 2 人目の点数
int score3 = 85; // 3 人目の点数
printf("1人目の点数: %3d\n", score1);
printf("2人目の点数: %3d\n", score2);
printf("3人目の点数: %3d\n", score3);
int sum = 0; // 合計点(初期値:0)
sum += score1;
sum += score2;
sum += score3;
printf("合計点: %d\n", sum);
printf("平均点: %.1f\n", (double)sum / 3);
return 0;
}実行結果
1人目の点数: 70
2人目の点数: 100
3人目の点数: 85
合計点: 255
平均点: 85.0
このプログラムlist16_01.cでは、3人の点数がそれぞれint型の3つの変数(score1・score2・score3)として表されています。
さて、今後プログラムを組んでいくうえで、学生の人数が100人に増えた場合を考えましょう。 このプログラムのようなやり方では、点数を格納するための変数を100つも用意することになるでしょう。 100もの変数を管理する必要がありますし、なにより変数の定義が大変に思われるかもしれません。
そこで利用するのが配列(array)になります。 配列は、同じ型の値の集合を1つの変数として表現することができます。 また、配列に格納されるひとつひとつの値を要素(element)と呼びます。
**配列**とは、同一型の変数(**要素**)が線型かつ連続的に並んだものである。まずは配列を宣言していきましょう。 配列の宣言では、次のように、要素型(element type)、配列名(変数名)、要素数を与えることで実現します。
要素型 配列名[要素数];例として、要素型がint型で要素数が5の配列を宣言してみます。
int a[5];これによって、int型の5つの値を格納することができる1つの変数を、配列名aとして宣言することができました。
配列が持つ個々の要素へのアクセスは、添字演算子(subscript operator)を用いることで実現します。
扱う配列の配列名がaである場合、添字演算子はa[b]として表されます。
これは、配列aの先頭からb個後ろの要素を意味します。
また、演算子[]内のオペランドは、添字(subscript)と呼ばれます。
これは、“先頭要素から何個後ろの要素なのか”を表す整数値になります。
そのため、要素数がnである配列の添字の範囲が、0からn - 1までの整数値となることに注意しましょう。
実際に、冒頭のプログラムlist16_01.cを、配列を用いて表現してみましょう。
#include <stdio.h>
int main(void) {
// 配列の宣言
int scores[3]; // int型の 3 つの値を格納する配列
// 要素へのアクセス
scores[0] = 70; // 1 人目の点数
scores[1] = 100; // 2 人目の点数
scores[2] = 85; // 3 人目の点数
printf("1人目の点数: %3d\n", scores[0]);
printf("2人目の点数: %3d\n", scores[1]);
printf("3人目の点数: %3d\n", scores[2]);
int sum = 0; // 合計点(初期値:0)
sum += scores[0];
sum += scores[1];
sum += scores[2];
printf("合計点: %d\n", sum);
printf("平均点: %.1f\n", (double)sum / 3);
return 0;
}実行結果
1人目の点数: 70
2人目の点数: 100
3人目の点数: 85
合計点: 255
平均点: 85.0
配列の各要素の値の設定を、代入ではなく初期化によって実現してみましょう。
配列の初期化子は、各要素に対する初期化子をコンマ(,)で区切って順に並べたものを{}で囲んだ形式で与えます。
まずは、プログラムlist16_02.cを、配列の初期化を用いて表現してみましょう。
#include <stdio.h>
int main(void) {
int scores[3] = {70, 100, 85}; // 配列の初期化
printf("1人目の点数: %3d\n", scores[0]);
printf("2人目の点数: %3d\n", scores[1]);
printf("3人目の点数: %3d\n", scores[2]);
int sum = 0; // 合計点(初期値:0)
sum += scores[0];
sum += scores[1];
sum += scores[2];
printf("合計点: %d\n", sum);
printf("平均点: %.1f\n", (double)sum / 3);
return 0;
}実行結果
1人目の点数: 70
2人目の点数: 100
3人目の点数: 85
合計点: 255
平均点: 85.0
配列の宣言と各要素への代入を1行で表現することができました。
また、配列に与える初期化子の規則を確認していきましょう。
配列に与える初期化子内の、最後の初期化子の後ろのコンマ(,)は省略可能です。
最後の初期化子の後ろにコンマを記述する形式には、初期化の追加や削除にともなって、コンマを記述したり削除したりしなくてよくなるというメリットがあります。
int a[3] = {
1,
2,
3, // 最後の初期化子の後ろのコンマ(,)は省略可能
};
/**
* a[0]: 1
* a[1]: 2
* a[2]: 3
*/次の配列bのような、要素数を指定しない場合は、{}内の初期化子の個数に基づいて、配列の要素数が自動的に決定されます。
int b[] = {1, 2, 3}; // 要素数は自動的に 3 になる
/**
* b[0]: 1
* b[1]: 2
* b[2]: 3
*/次の配列cを確認すると、{}内に初期化子が与えられていない要素は、0で初期化されることがわかります。
int c[3] = {1, 2}; // int c[] = {1, 2, 0}; と同じ
/**
* c[0]: 1
* c[1]: 2
* c[2]: 0
*/次の配列dの場合では、d[0]が0で初期化され、つづくd[1]、d[2]は初期化子が与えられないため0で初期化されています。
int d[3] = {0}; // 全要素を 0 で初期化
/**
* d[0]: 0
* d[1]: 0
* d[2]: 0
*/走査(traverse)とは、配列の要素をひとつずつ順番になぞっていくことを意味します。
for文を用いた配列の走査について確認しましょう。
配列を先頭から順に走査するには、for文のカウンタ用変数を、配列の添字として利用することで実現できます。
プログラムlist16_03.cを、配列とfor文を用いて表現してみましょう。
#include <stdio.h>
int main(void) {
int scores[3] = {70, 100, 85}; // 配列の初期化
int sum = 0; // 合計点(初期値:0)
// 配列の走査
for (int i = 0; i < 3; i++) {
printf("%d人目の点数: %3d\n", i + 1, scores[i]);
sum += scores[i];
}
printf("合計点: %d\n", sum);
printf("平均点: %.1f\n", (double)sum / 3);
return 0;
}実行結果
1人目の点数: 70
2人目の点数: 100
3人目の点数: 85
合計点: 255
平均点: 85.0
for文を用いることで、添字のみが異なるような命令を、一括で記述することができました。
命令や繰返し処理を工夫することで、さまざまなプログラムに応用することができます。
さて、実際に練習問題を解きながら、配列を学んでいきましょう。
### 練習問題1キーボードからの入力により、5人の身長(double型)を配列heightsに格納してください。
また、入力された身長の平均値を算出して出力してください.
実行結果
heights[0]? 170.0
heights[1]? 168.2
heights[2]? 186.9
heights[3]? 152.0
heights[4]? 170.5
average: 169.520000
ファイル名: 16_issue1.c
ヒント(キーボードによる入力受付けについて)
`double`型の値のキーボード入力受付けは、関数`scanf`を用いることで、次のようにして実現されました。 ```c double tmp; scanf("%lf", &tmp); ``` 任意の配列の要素に対しても同様にして行うことができます。 次のコードは、配列`a`の2番目の要素に対してキーボード入力を受け付けるプログラムになります。 ```c scanf("%lf", &a[1]); ``` `for`文のカウンタ用変数を、配列の添字に割り当てることで、すべての配列の要素に対してキーボード入力を受け付けてみましょう。ヒント(配列の要素の平均値について)
配列の要素の平均値を求めるには、要素の合計値と要素数(今回は`5`)が必要になります。 要素の合計値の算出については、プログラム`list16_04.c`を参考にしてみましょう。模範解答
```c title="16_issue1.c" #includeint main(void) { double heights[5];
// 身長の入力
for (int i = 0; i < 5; i++) {
printf("heights[%d]? ", i);
scanf("%lf", &heights[i]);
}
double sum = 0; // 身長の合計値(初期値:0)
// 配列の走査による合計値の算出
for (int i = 0; i < 5; i++) {
sum += heights[i];
}
// 平均値の出力
printf("average: %f\n", sum / 5);
return 0;
}
</details>
</Card>
<Card>
### 練習問題2
以下の配列`arr`が与えられているとき、この配列の要素の最小値と最大値をそれぞれ求めなさい。
```c
int arr[] = {170, 168, 186, 152, 171};
実行結果
min: 152
max: 186
ファイル名: 16_issue2.c
ヒント
最小値を`min`として、最初に適当な要素の値を格納しておきましょう。 ```c int min = arr[0]; ``` 続いて、配列の走査を行います。 走査により`min`の値よりも小さな要素を見つけた場合、その要素を`min`に格納することで最小値を更新します。 ```c if (min > arr[i]) { min = arr[i]; } ```同様の考え方で、最大値を見つけることができます。
模範解答
```c title="16_issue2.c" #includeint main(void) { int arr[] = {170, 168, 186, 152, 171};
// 最小値と最大値(初期値として適当な要素を格納しておく)
int min = arr[0];
int max = arr[0];
for (int i = 0; i < 5; i++) {
if (min > arr[i]) {
// min よりも小さな値を見つけた場合、min を更新
min = arr[i];
}
if (max < arr[i]) {
// max よりも大きな値を見つけた場合、max を更新
max = arr[i];
}
}
printf("min: %d\n", min);
printf("max: %d\n", max);
return 0;
}
</details>
</Card>