動かして学ぶC言語「配列」について、動かして目で見て理解しよう

C言語:配列についてLEDで見ながら学ぼうC言語

「配列」とは「変数」と同様にプログラム実行中に使用するデータを格納しておくためのものです。

「変数」はデータを入れておく箱のようなものと紹介しましたが、「配列」も同じようにデータを入れておく箱のようなものです。
「変数」との違いは「変数」が「1つのデータを入れておく箱」なのに対して「配列」は複数のデータを入れておく「仕切りの付いた箱」です。

「配列」は複数のデータを連続的に扱う時に便利です。今回は「配列」の使い方について紹介します。

「変数」については以下のリンクで詳しく紹介しています。

動かして学ぶC言語「変数」について、正しく学ぶならC言語が一番
「変数」とはプログラム内のデータを保存しておくために使用します。 データを入れておく箱のようなもので、一時的にデータを入れておき、必要な時に取り出して使用したり、取り出して加工(演算等)してから元に戻して、また必要な時に使用したりします。
スポンサーリンク

1.「配列」の宣言と初期化

「配列」も「変数」と同様に、使用する前にあらかじめ宣言しておく必要があります。
「初期化」は「変数」と同様に宣言時に行うことができます。

「配列」の宣言と初期化は以下のように行ないます。

型名  配列名[要素数(サイズ)] = { 初期値0 , 初期値1 , 初期値2 , ・・・};

型名:扱うデータの「型」を指定します。主に使用される「型」の種類は以下のようになります。
 ・文字型:char
 ・整数型:int
 ・単精度浮動小数点型:float
 ・倍精度浮動小数点型:double

配列名:「配列名」は自由に決めることができますが、「変数」と同様に以下の①~⑤のような制限があります。
 ①下線( _ )、英大文字小文字、数字で構成する。
 ②先頭の1文字目は数字以外。
 ③大文字小文字は区別されますが、一般的に配列名には小文字が使われます。
 ④「プログラム言語」の文法で使用されているコマンド(if や for等)は使用できません。
 ⑤文字数に制限はありません。

要素数(サイズ):「配列」に格納するデータの数です。

初期値n:「配列」に格納するデータです「初期値」は{ }で囲って「 , 」で区切って「要素数」分記入します。

・基本的な初期化方法

int型」で配列名を「data」として「要素数」が[3]の場合の初期化方法は以下のようになります。

int data[3]  = { 1 , 2 , 3 };

「型名」と「配列名」は「変数」と同じように設定します。
「要素数」は[  ]で囲って「初期値」の数だけを記入します。
今回は「初期値」が3個なので{ }内に「 , 」で区切って「1,2,3」3個の値を「初期値」として設定しています。

以下のように「初期値」を設定せずに宣言することもできます。

int data[3] ;

この場合は「要素数」が[3]の配列が準備され、この時点では「初期値」は全て「0」になります。

「要素数」を超える「初期値」の設定はエラーになります。
「要素数」以下の数の「初期値」を設定した場合、残りは「0」で初期化されます。

・要素数を省略した初期化方法

「要素数」は宣言時には以下のように省略することができます。

型名  配列名[]   = { 初期値0 , 初期値1 , 初期値2 , ・・・};
「要素数」を指定せずに宣言した場合、「配列」としては不完全な状態です。
「初期値」を指定することで「配列」として成立します。
指定した「初期値」の数が「要素数」となります。

・要素数を確認する方法

「要素数」を指定せずに宣言した配列等の「要素数(サイズ)」は以下のようなプログラムで知ることができます。

「C言語」では要素数を直接確認できるコマンドがありませんので以下のようなプログラムで確認する必要があります

「要素数」を省略して宣言した「data」という配列を例に紹介します。

int data[]  = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10};
int datasize = sizeof(data) / sizeof(int);

datasize:「要素数」を格納するために「変数」を使用します。
sizeof:データのバイト数を確認するコマンドです。

sizeof(data):「配列」全体のバイト数を表します。
 ここでは「int型」のデータが10個あるので「4バイト × 10個 = 40バイト」
sizeof(int):「int型」のバイト数(4バイト)を表します。

まず、「配列」全体のバイト数(40バイト)を確認し、使用している「型」(ここでは「int型」4バイト)のバイト数で割ることで要素数を知ることができます。

datasize」の要素数は 40/4 = 10 となります。

「配列」のデータを「for文」で連続的に処理する時、繰り返し回数は「要素数」とすることが多いです。
この「配列」の「要素数」を指定せずに宣言しておいて、「sizeof」で「要素数」を変数に格納し、この「変数」を「for文」の繰り返し回数に指定することで、後から「要素数」を変更した時にプログラム内の繰り返し回数を変更する手間が無く、自動で変更されるため変更忘れや間違いを無くすことができます。

・「2次元配列」の宣言と初期化

「2次元配列」とは「配列」の中に「配列」を持たせたものです。
「配列」内のデータを関連する項目ごとにまとめて表のように扱えるので複雑なデータを処理する時にに便利です。

int型」で配列名を「data」として「1次要素数」と「2次要素数」が[3]の場合の初期化方法は以下のようになります。

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

「1次要素数」と「2次要素数」はそれぞれ[  ]で囲って「初期値」の数だけ記入します。

「2次元配列」のデータは下表のように格納されます。

2次元配列表

2.「配列」の使い方

以下の「配列」を例に使い方を紹介します。

intdata[3]  = { 1 , 2 , 3 };

・データを取り出す方法

「配列」の値を取り出して使用する時は以下のように指定します。

1を取り出す時 data[0]
2を取り出す時 data[1]
3を取り出す時 data[2]
「配列」内の1番目の値を示す要素番号は[1]ではなく[0]からになるため注意しましょう。

・データを変更する方法

配列の値を変更する時は以下のように行います。

data[0] = 4;

と指定すると配列内の値は{1, 2, 3} が {4, 2, 3} となります。

・データを連続的に処理する方法

データを連続的に処理する時は以下のように「for文」を使用します。

int data[3]  = { 1 , 2 , 3 };
int
sum = 0;

for (int i = 0; i < 3; i++) {
  sum = sum + data[i];
}

「配列data」の3つの要素を合計して「変数sum」に格納しています。
for文」の繰り返し回数を「要素数」に設定して連続的に「配列data」の値を取り出して処理しています。

「for文」については → こちら

3.「LEDテープライト」で「配列」の使い方を確認してみよう(ATOM LITE)

「ATOM LITE」と「LEDテープライト」を使用して「配列」の使用方法を確認してみましょう。

「LEDテープライト」は「フルカラーLED」がたくさん並んでつながったものです。
使用したのは60個のLEDが連なった1mのものですが、このうち10個を配列に見立ててLEDの点灯色を指定します。

LEDの点灯色は「赤、緑、青」の明るさを変えることで指定できます。このLED一つ一つを「配列」と考えると理解がしやすいと思います。

「ATOM LITE」と「LEDテープライト」については以下のリンクで詳しく紹介しています。

ATOM LITE プログラミング初心者におすすめ超小型で高機能!
マイコンボードはRaspberry Pi、Arduino、M5Stack等がありますが、一通りやってみてそれぞれの良さはあるものの「最初に何を?」と聞かれたらATOM LITEが一番お手軽♪プログラミング初心者におすすめ
LEDテープライトでクリスマスイルミネーションを作ろう♪ATOM LITE
LEDテープライト(Neopixel)で流れるLEDイルミネーションを作りましょう♪ 使用するLEDはマイコン内蔵のSK6812(WS2812B互換)で点灯色やパターンはArduino等があれば専用ライブラリを使用して簡単に制御できます。
「開発環境の準備」がまだの方は → こちら

下のコードを「コピペ」して書き込んで動作確認してみましょう。
※コピーは下コード(黒枠)内の右上角にある小さなアイコンのクリックでもできます

・フルカラーLEDの赤、緑、青の値を「配列」で指定

フルカラーLEDの色は「赤、緑、青」の色の3原色の組み合わせ(明るさの値を0~255で指定)で点灯色を指定します。
以下のプログラムは3つの色の組み合わせを、「要素数」3個の配列(10~13行目)として指定することで10個のLEDを1秒ごとに赤、緑、青に変えるプログラムです。

LEDテープライト赤、緑、青
各LEDの明るさの指定値は最大20を目安に設定しましょう。明るくしすぎると電流が大きくなってマイコンボードが発熱し最悪壊れます。
プログラム書込み直後はマイコンボードの発熱をしばらく確認し、熱くなるようならすぐに電源を切ってください。
「ATOM LITE」で「LEDテープライト(LED60個)」の組み合わせの場合、全て30の設定でも発熱は無く使用できました。
#include <M5Atom.h>
#include <Adafruit_NeoPixel.h>  //NeoPixel SK6812制御用ライブラリ

#define PIN        26  // LEDテープ信号端子
#define NUMPIXELS  10  // LEDの数

// NeoPixel初期設定
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

// 配列宣言 LED色(明るさ)指定 0~255 (赤, 緑, 青)
int red[3] = {10, 0, 0};   //LED色赤
int green[3] = {0, 10, 0}; //LED色緑
int blue[3] = {0, 0, 10};  //LED色青

// 初期設定 **********************************************************************
void setup() {
  M5.begin(true, false, false);  //Serial,POWER,LED
  pixels.begin();     //LED色初期化
  pixels.clear();     //LED色指定クリア
}
// メイン ***********************************************************************
void loop() {
  for(int i = 0; i < NUMPIXELS; i++) {    //LED番号0~NUMPIXELまで繰り返し
    pixels.setPixelColor(i, pixels.Color(red[0], red[1], red[2]));  //全LED赤
  }
  pixels.show();  //LED色出力
  delay(1000);    //遅延時間(1秒)

  for(int i = 0; i < NUMPIXELS; i++) {    //LED番号0~NUMPIXELまで繰り返し
    pixels.setPixelColor(i, pixels.Color(green[0], green[1], green[2])); //全LED緑
  }
  pixels.show();  //LED色出力
  delay(1000);    //遅延時間(1秒)

  for(int i = 0; i < NUMPIXELS; i++) {    //LED番号0~NUMPIXELまで繰り返し
    pixels.setPixelColor(i, pixels.Color(blue[0], blue[1], blue[2])); //全LED青
  }
  pixels.show();  //LED色出力
  delay(1000);    //遅延時間(1秒)
}

・フルカラーLEDの赤、緑、青の値を「2次元配列」で指定

上のコードと同じ動作をするプログラムを「2次元配列」を使うともっとシンプルに指定することができますし、複数の色を扱う時にも便利です。

以下のプログラムの11行目のように「2次元配列」を指定します。
「1次要素」を色番号として、「2次要素」にその色の「赤、緑、青」の数値を指定しておきます。

#include <M5Atom.h>
#include <Adafruit_NeoPixel.h>  //NeoPixel SK6812制御用ライブラリ

#define PIN        26  // LEDテープ信号端子
#define NUMPIXELS  10  // LEDの数

// NeoPixel初期設定
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

// 配列宣言 LED色(明るさ)指定 0~255 (赤, 緑, 青)
int red[3][3] = {{10, 0, 0}, {0, 10, 0}, {0, 0, 10}};   //赤、緑、青を2次元配列で指定

// 変数宣言
int change = 0; //色切換カウント用

// 初期設定 **********************************************************************
void setup() {
  M5.begin(true, false, false);  //Serial,POWER,LED
  pixels.begin();     //LED色初期化
  pixels.clear();     //LED色指定クリア
}
// メイン ***********************************************************************
void loop() {
  for(int i = 0; i < NUMPIXELS; i++) {    //LED番号0~NUMPIXELまで繰り返し
    pixels.setPixelColor(i, pixels.Color(red[change][0], red[change][1], red[change][2]));  //全LED色指定
  }
  pixels.show();  //LED色出力
  // 色変更
  change++;           //色切換カウント+1
  if (change == 3) {  //色切換カウントが3なら
    change = 0;       //色切換カウント0リセット
  }
  delay(1000);    //遅延時間(1秒)
}

・「2次元配列」の1次要素(色番号)を演算で指定して赤、緑、青を交互に点灯

下コードの25行目のように「for文」の中で繰り返しカウント「i」を3で割った余り(0~2繰り返し)を「2次元配列」の「1次要素」の色番号に指定することで「赤、緑、青」を交互に連続的に指定することも簡単にできます。

LEDテープライト赤、緑、青交互点灯
#include <M5Atom.h>
#include <Adafruit_NeoPixel.h>  //NeoPixel SK6812制御用ライブラリ450

#define PIN        26  // LEDテープ信号端子
#define NUMPIXELS  10  // LEDの数

// NeoPixel初期設定
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

// 配列宣言 LED色(明るさ)指定 0~255 (赤, 緑, 青)
int red[3][3] = {{10, 0, 0}, {0, 10, 0}, {0, 0, 10}};   //赤、緑、青を2次元配列で指定

// 変数宣言
int change = 0; //色切換カウント用

// 初期設定 **********************************************************************
void setup() {
  M5.begin(true, false, false);  //Serial,POWER,LED
  pixels.begin();     //LED色初期化
  pixels.clear();     //LED色指定クリア
}
// メイン ***********************************************************************
void loop() {
  for(int i = 0; i < NUMPIXELS; i++) {    //LED番号0~NUMPIXELまで繰り返し
    change = i % 3; //iを3で割った余りを色番号(change)に指定。0~2を繰り返す
    pixels.setPixelColor(i, pixels.Color(red[change][0], red[change][1], red[change][2]));  //全LED色指定
  }
  pixels.show();  //LED色出力
  delay(1000);    //遅延時間(1秒)
}

・応用編:流れる光のサンプルプログラム

配列の要素を隣へ順番にずらしていくことで流れる光も表現できます。

下のプログラムを実行すると、電源ONで全LEDが点灯し白い光(2個)が流れ続けます。
全LEDの色は「ATOM LITE」本体のボタンを押すごとに4色の切換えができます。

#include <M5Atom.h>
#include <Adafruit_NeoPixel.h>  //NeoPixel SK6812制御用ライブラリ

#define PIN        26  // LEDテープ信号端子
#define NUMPIXELS  60  // LEDの数

// NeoPixel初期設定
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

// 配列宣言
int array[NUMPIXELS+2][3] = {}; //LED色指定用配列(先頭と最終要素に色保持するため+2)
// 流れる光(白)と残光の配列snow(白をだんだん暗く、残光数の分準備)
int snow[][3] = {{60, 60, 60}, {40, 40, 40}, {30, 30, 30}, {20, 20, 20}, {10, 10, 10}};
// ベース色指定用配列color
int color[][3] = {{10, 5, 1}, {12, 0, 2}, {0, 10, 1}, {2, 0, 12}};
int cnt = 0;                                      //流れる光出力タイミング用
int afterglow = 0;                                //流れる光と残光回数用
int snow_size = sizeof(snow) / (sizeof(int)*3);   //配列snowの要素数(1次要素数)
int color_size = sizeof(color) / (sizeof(int)*3); //配列colorの要素数(1次要素数)
int change = 0;                                   //ベース色切替えカウント用

// 初期設定 **********************************************************************
void setup() {
  M5.begin(true, false, false);  //Serial,POWER,LED
  Serial.begin(9600);
  pixels.begin();     //LED色初期化
  pixels.clear();     //LED色指定クリア

  // 配列colorの最終要素の(赤, 緑, 青)を初期値として配列arrayにコピー
  for(int i = 0; i < NUMPIXELS+1; i++) {    //LED番号0~NUMPIXELまで繰り返し
    for (int j = 0; j < 3; j++) {           //LED(赤, 緑, 青)の3個分繰り返し
      array[i][j] = color[color_size-1][j]; //ベース色をLED指定用配列にコピー
    }
  }
}
// メイン ***********************************************************************
void loop() {
  M5.update();  //本体ボタン状態更新
  // ボタンが押されるごとにベース色変更
  if(M5.Btn.wasPressed()) {                 //本体ボタンが押されていれば
    cnt = 30;                               //流れる光出力タイミング
    for(int i = 0; i < NUMPIXELS+2; i++) {  //LED番号0~NUMPIXEL+2まで繰り返し
      for (int j = 0; j < 3; j++) {         //LED(赤、緑、青)の3個分繰り返し
        array[i][j] = color[change][j];     //ベース色をLED指定用配列にコピー
      }
    }
    change++;           //ベース色切替えカウント+1
    if (change == color_size) {  //changeがcolor_sizeなら
      change = 0;       //0リセット
    }
  }
  // ベース色セット
  for (int i = 0; i < 3; i++) {           //LED(赤、緑、青)の3個分繰り返し
    array[NUMPIXELS+1][i] = array[0][i];  //LED指定用配列の0番目をベース色に指定
  }
  // 流れる光と残光設定
  if (afterglow < snow_size) { //流れる光数カウント(afterglow)がsnow配列の要素数以下なら
    for (int i = 0; i < 3; i++){  //LED(赤、緑、青)の3個分繰り返し
      //残光用LED色指定(残光とベース色を加算して1/2することで残光を表現)
      array[NUMPIXELS+1][i] = (snow[afterglow][i] + array[0][i]) / 2;
    }
    afterglow++;            //流れる光と残光回数+1
  }
  // 流れる光タイミングと残光カウントリセット
  if (cnt == NUMPIXELS/2) { //NUMPIXELS(LED数)の1/2で流れる光2個、1/3なら3個
    cnt = 0;                //流れる光出力タイミング0リセット
    afterglow = 0;          //流れる光と残光回数0リセット
  }
  cnt++;                    //流れる光出力タイミング+1

  for(int i = 1; i < NUMPIXELS+1; i++) {    //LED番号1~NUMPIXEL+1まで繰り返し
    // 全LED色指定
    pixels.setPixelColor(i-1, pixels.Color(array[i][0], array[i][1], array[i][2]));
  }

  for(int i = 1; i < NUMPIXELS+1; i++) {  //array配列の2つ目からLED数+1まで繰り返し
    for (int j = 0; j < 3; j++) {         //LED(赤、緑、青)の3個分繰り返し
      array[i][j] = array[i+1][j];        //array配列を左へシフト
    }
  }
  pixels.show();  //LED色出力

  delay(100);     //遅延時間(ms)
}

4.「配列」の「型」について

「変数」の時にも紹介しましたが、「配列」も同様に「型名」には扱えるデータサイズ(データ範囲)によってたくさんの種類があります。
今全てを覚える必要はありませんので紹介程度に一覧表にまとめました。
今後必要に応じて確認してみてください。

型の説明データサイズ扱えるデータ範囲
unsigned char文字型1byte 0~255
char文字型1byte-128~127
unsigned short符号なし単整数型2byte0~65,535
short単整数型2byte-32,768~32,677
unsigned int符号なし整数型4byte0~4,294,967,295
int整数型 4byte-2,147,483,648~
2,147,483,647
unsigned long符号なし長整数 4byte (8byte)0~4,294,967,295
long長整数 4byte (8byte) -2,147,483,648~
2,147,483,647
float単精度浮動小数点4byte最小の正の数:3.4e-38
最大値:3.4e+38
double倍精度浮動小数点8byte最小の正の数:1.7e-308
最大値:1.7e+308

5.ブラウザ上で動作確認するなら

ちょっとした動作確認をしたい時は、ブラウザ上でプログラムを作成して実行できる「paiza.IO」が便利なので以下にリンクを貼っておきます。
無料で使用できて「C言語」だけでなく「python」や「JavaScript」「PHP」等たくさんの言語にも対応しています。

ブラウザでプログラミング・実行ができる「オンライン実行環境」| paiza.IO
paiza.IOはオンラインですぐにプログラミングが始められる、オンライン実行環境です。Java,Ruby,Python,PHP,Perlなど主要24言語に対応。プログラミング学習にも。

プログラミングの上達には学ぶことも大切ですが、自分で書いて実行して、どんな動きになるか、間違っててもいいのでこれをひたすら繰り返すことが一番の近道と思います。たくさん書いて実行してみましょう。

6.まとめ

「配列」は「変数」と同様にデータを格納しておくことができますが「変数」が一つのデータを格納するのに対して、「配列」は複数のデータを格納しておくことができます。

格納するデータ数は「要素数(サイズ)」と呼ばれます。
格納されている1番目のデータを示す要素番号は[1]ではなく[0]からになります。

「配列」を使用するには「変数」と同様にあらかじめ宣言しておく必要があり、宣言時に「要素数」と「要素数」分の「初期値」を指定します。

「要素数」は省略することができ、省略した場合は指定した「初期値」の数が「要素数」となります。

「要素数」は「sizeof」コマンドで確認することができ、これを「for文」の繰り返し回数に指定することで、後から「初期値」の数(要素数)を変更した時にプログラム内の繰り返し回数を変更する手間が無く、自動で変更されるため変更忘れや間違いを無くすことができます。

このように、関連するデータをまとめて格納しておける「配列」を使うことで「for文」等を使って連続的に処理を行うことができるため、効率的で理解しやすいプログラムにすることができます。


動かして学ぶ「C言語」については、他にも以下のリンクで基礎からサンプルプログラムを使って詳しく紹介しています。

C言語プログラミングの記事一覧
C言語のプログラミング記事一覧です。安価なマイコンボードを使用して動かしながら学んでみましょう♪ プログラム例も用意してあります。まずはコピペで書き込み、動作確認しながら少しづつ理解を深めましょう。

7.独学に限界を感じたら

最後に、独学に限界を感じている方へ、私のように無駄な時間を過ごさないように「C言語」や「Python」を基礎から学べるオンラインスクール(教室での受講も可)の紹介です。

私は学生の頃、独学で「C言語」を始めましたが、かなり時間をかけて結局一度挫折しました(汗)
仕事で少しプログラミングに触れる機会もあり、今となっては小規模なプログラムなら組めますが、どれだけ時間をかけたでしょう・・・

短期間で基礎から学べて質問もできる、そんな環境が当時あったらと思うと・・・
今はとても羨ましい時代になりました。


いろいろなプログラミングスクールのサイトを見て回りましたが「C言語」と「Python」の両方を扱っている所は少ないです。その中でも以下のスクールは他の言語も含めて、たくさんの講座から目的やスキルに合ったものを組合わせて、オンラインでも対面でも受講が可能です。

ほとんどの受講生が未経験で、全国に教室があるため現地で直接指導してくれますが多くの講座がオンライン対応です。
オンラインでは「個人レッスン(講師一人につき3名程)」と「集合レッスン」の選択も可能です。

オンラインレッスンの方には授業で使用するノートPC(ソフトインストール済み)も追加料金なしで貸りられて、質問掲示板を使って授業時間外でも質問できます。

無料体験もあり、説明会やカウンセリング等も行っています。
以下のリンクから内容だけでも確認してみてください。


コメント

タイトルとURLをコピーしました