配列の応用

配列の応用

前回に引き続き配列について学んでいきます。配列については学ぶことが多いので記事を分割して情報を詰め込みすぎないようにしてみました。

前回の復習

前回の記事では配列について見ていきました。配列の宣言(作り方)や代入(使い方)を始め配列の実体がメモリ上の連続した領域であることも勉強しましたね。
その他にもsizeof演算子というものも学習しました!

前回の部分で配列のほとんどの部分は勉強し終わっています。しかし、配列の実装をするにあたりひとつ重要な学習事項が残っているので、それを勉強してもらいます。

前回の練習問題の解答例

A問題

岩ちょこ君のクラスには6人の生徒がいます。岩ちょこ君の調査によるとA君からF君の成績は順番に、5, 2, 6, 2, 8, 1であることが判明しました。今日はC君の三者面談です。C君の成績を親御さんに見せてあげるプログラムを作成してください。(配列を作ってそこから値を取り出すようなプログラムを書いてみてくださいね)

解答例

#include <stdio.h>

int main() {
  int score[6] = {5, 2, 6, 2, 8, 1};
  printf("%d\n", score[2]);
  return 0;
}

配列の要素は0から数えることに注意してくださいね。このソースコードがかければとりあえず前回までの内容は理解できていますね。解けなかった人はソースコードをコピペして実験したり、前の記事で勉強しなおしてB問題に挑戦してみてくださいね。(同じような問題です)

また、初期化を用いている場合は要素数を省略して書くことができます。4行目の『score[6]』のところにある要素数6を省略して、『score[]』と書くことができます

B問題

岩ちょこ君のクラスでは生徒が5人います。そして今日は年1回の身長を測定する日です。その結果、A君からE君の身長は順番に、150.3, 142.6, 160.4, 146.7, 152.2でした。A君の身長を出力してください。

解答例

#include <stdio.h>

int main() {
  float tall[5] = {150.3, 142.6, 160.4, 146.7, 152.2};
  printf("%f\n", tall[0]);
  return 0;
}

A問題との違いはfloat型の配列を扱っている部分ですね。それ以外は同じ処理をしています。

C問題

岩ちょこ君のクラスには中二病四天王がいます。彼らによるとA君は四天王の中でも最弱らしいです。A君の戦闘力をスカウターで測定すると3でした。残りの四天王もついでにスカウターで測定したところ、戦闘力が5, 7, 9であることがわかりました。四天王の戦闘力の平均値をsizeof演算子を用いて計算してください。

解答例

#include <stdio.h>

int main() {
  int power[4] = {3, 5, 7, 9};
  int sum = power[0] + power[1] + power[2] + power[3];
  int n = sizeof(power) / sizeof(int);
  int average = sum / n;
  printf("%d\n", average);
  return 0;
}

sizeof演算子を用いることで変数や配列などデータを保持するのに用いられているByte数を取得することができます。ちなみに1Byteは8bitで、1bitというのは2進数1桁を保持できる情報量になります。

文字列

前回の問題で配列の復習ができたと思うので、今回の内容の方に移っていきます。

前回の記事で配列を勉強したとき実は整数型(int)と小数型(float, double)型しか扱わず、あえてchar型の配列は扱っていませんでした。

どうして扱わなかったのかというと文字列の扱いがやや特殊なためです。

以降では実験しながら文字列を勉強していきましょう。まずは前回学習した方法で配列を作ってみます。

#include <stdio.h>

int main() {
  char name[8] = {'i', 'w', 'a', 'c', 'h', 'o', 'c', 'o'};
  printf("%s\n", name);
  return 0;
}

実行結果

iwachoco?

printf関数を用いた文字列の出力では、『%s』のフォーマット指定子を用います
実行結果を見てもらいましょう。8文字からなる文字列をchar型の配列に入れることで文字列を作って出力させたつもりですが、何やら出力が変ですね。これはどうして起こるのでしょうか。

実は文字列を配列に入れるときには特別なルールがあります。それは、配列の要素を1つ余分に確保することです。つまり8文字の文字列を扱いたいときはchar型配列の要素を9以上にしなければならないというわけです。どうしてなのかは後で見ていくことにして早速さっきのコードを修正してみましょう。

#include <stdio.h>

int main() {
  char name[9] = {'i', 'w', 'a', 'c', 'h', 'o', 'c', 'o'};
  printf("%s\n", name);
  return 0;
}

実行結果

iwachoco

配列の要素をひとつ増やしたことで出力が正常になりましたね。

それでは、char型配列の内側でどのような処理をしているのかを理解して、どうして要素をひとつ多く確保しなければならないのかを理屈で納得してもらいます。

文字型配列というのは、文字が一列のロッカーに順番に収納されているものになります。これを使うことで文字列というものを表現しています。しかしプログラマー視点に立つと文字列はchar型文字の配列といちいち考えて扱うと分かりづらいので配列に少し細工をして文字列を扱いやすくしています。

どんな細工が施されているのかというと、char型の配列に文字列を代入したときに、文字列の最後(一番後ろ)に目印を置いて文字列の終わりを検出しています。この目印のことは『番兵』と呼ばれることが多いです。

文字列というのは長さが前もって決まらないので、どのくらい要素を使うのかが事前にわかることが少なく扱いづらいものでしたが、この番兵を用いた処理で文字列の終わりが検出できるようになり、文字列を文字(char型)の配列であると意識しなくても簡単に扱えるようになっているのです。

ここでは文字列というのがchar型の配列として実装されていることを学ぶためにあえてこのような書き方をしましたが、文字列を実際に扱う際には以下のように書くこともできます。

#include <stdio.h>

int main() {
  char user[20] = "choco";
  char comment[] = "gorogorogoro";
  printf("%s\n", user);
  printf("%s\n", comment);
  return 0;
}

実行結果

choco
gorogorogoro

文字列を代入する際は『ダブルクォーテーション(“)』で囲んであげる必要があります。要素数は過剰に確保しても大丈夫ですがその分メモリを消費しますので計画的に使うようにしてくださいね。

また前回の記事でも書きましたが、配列を初期化で作る際には要素数を省略して書くことができます。

文字列とは文字が連なったものを指し、実体はchar型の配列で表現される。文字列の長さはその都度異なるため番兵を用いて文字列の終わりを調べている。
番兵に用いられている特殊な文字は『\0』である。
column
番兵に使われている文字の正体を見てみましょう!コラム扱いしたのはif文を使って確認するので、現在の知識だけでは見られないためです。それではコードを書いて確かめてみましょう。

#include <stdio.h>

int main() {
  char message[] = "abc";
  if(message[3] == '\0') {
    printf("番兵です\n");
  } else {
    printf("番兵ではありません\n");
  }
  return 0;
}

実行結果

番兵です

5行目のif文の条件部分に書いた式の意味は、message配列の3番目(前から4個目)の文字が『\0』ならif文のすぐ後ろのブロック(波括弧で囲まれた部分)を実行し、成り立っていない場合はelseのすぐ後ろのブロックを実行するコードです。番兵に使われている文字は『\0』であることを確認してみたのが上の結果です。

この文字が番兵の正体になります。覚えておくと実装の役に立つこともあります。(コンパイラを作ったりするときに、文字列の終端検出に使えて便利です)

2次元配列

後半も頑張っていきましょう!ここでみていくのは多次元配列です。実際、多次元配列は配列がわかっていればそこまで難しくないのでサクっと片付けちゃいましょう。

まずは2次元配列のイメージを1次元の配列(先ほどまで単に配列と呼んでいたもの)と比較しながら学んでいきます。まずは簡単に両者のイメージを説明しましょう。1次元の配列というのは横一列に並ぶロッカーのようなものであると説明しましたが、2次元配列は縦横両方向に広がりがあるロッカーになります。例えば3×3のような表の各マスのデータを保存したいときなんかに使われます。

なんとなくイメージを掴んでもらったところで、実装の違いをみていきましょう。配列の宣言、代入、初期化を1次元の配列の場合と2次元の配列の場合に分けて書いてみました。以下のコードをそれぞれ見比べてみてください。多分3次元の配列の作り方も推測できると思います。

#include <stdio.h>

int main() {
  /*1次元配列*/
  int score[3]; //配列の宣言
  score[0] = 85; //0番目の要素に代入
  score[1] = 33; //1番目の要素に代入
  score[2] = 50; //2番目の要素に代入
  int number[5] = {1, 4, 3, 2, 5}; //配列の初期化

  /*2次元配列*/
  int field[2][3]; //配列の宣言
  field[0][0] = 12; //0行0列目の要素に代入
  field[0][1] = 23; //0行1列目の要素に代入
  field[0][2] = 34; //0行2列目の要素に代入
  field[1][0] = 13; //1行0列目の要素に代入
  field[1][1] = 35; //1行1列目の要素に代入
  field[1][2] = 57; //1行2列目の要素に代入
  int table[2][4] = {{1, 2, 3, 4}, {2, 4, 6, 8}}; //配列の初期化
  return 0;
}

実行結果

多次元配列も基本の1次元配列が理解できればへっちゃらですね。(一見とっつきにくい感じはあるかもしれませんが……)

2次元配列とは縦方向と横方向に広がりを持ったロッカーで配列の各要素に配列が入ったものと考えることができる。

まとめ

今回の記事では配列の応用ということで文字列と2次元配列について勉強しました。文字列を扱う機会は非常に多いのでこの辺りの知識は是非覚えておくと実務で役立つでしょう。
今回の内容を簡単にまとめてみました。復習としてみていきましょう!

  • 文字列とはchar型の配列で作られた連続した文字のデータであり、文字列の終わりには番兵があるため、配列の要素は文字列+1の大きさが必要である。
  • 多次元配列は基本的に1次元配列と似たような手法により作成できる。

次回は代入を見ていきます。この代入まで勉強すれば競技プログラミングの簡単な問題くらいは解けるようになるので楽しみにしていてくださいね。

練習問題

A問題

自分の名前をchar型の配列に入れて、それを出力してみてください。(ただし日本語は1文字が数バイトになっていることもあるので、要素数は30ぐらい確保しておいた方が無難ですね。ローマ字で書いてもらっても構いません)

B問題

とある街の1月から4月までの平均気温と降水量を月毎に調べたところ以下の表のようになった。

1月 2月 3月 4月
気温[℃] 7 9 15 19
降水量[mm] 100 50 40 20

この表のデータを2×4の2次元配列を作り、それぞれの要素に代入してください。(初期化でも可)

配列【C言語講座#6】

scanf関数【C言語講座#8】


最後まで記事を見ていただきありがとうございます。また別の記事でお会いできることを祈っております。

Print Friendly, PDF & Email

C言語カテゴリの最新記事