🖥️

【C言語超入門】 第13回 定数と列挙体

2024/12/25に公開

https://youtu.be/NhBGFwskf0I

四国めたん
\textcolor{pink}{四国めたん: }教師役ですわ

ずんだもん
\textcolor{lime}{ずんだもん: }生徒役なのだ

\footnotesize \textcolor{pink}{四国めたん:} 皆さん、こんにちは。四国めたんです

\footnotesize \textcolor{lime}{ずんだもん:} ずんだもんなのだ。こんにちはなのだ

\footnotesize \textcolor{pink}{四国めたん:} 今回もC言語のお勉強をしていきましょう

\footnotesize \textcolor{lime}{ずんだもん:} レッツゴーなのだ

\footnotesize \textcolor{pink}{四国めたん:} 前々回で forループ についてお話しした際、配列のサイズやループの回数等については数値を直接指定していたのを覚えていますか?

\footnotesize \textcolor{lime}{ずんだもん:} 全て"100"という数値を使っていたのだ

\footnotesize \textcolor{pink}{四国めたん:} これらの直接指定する数値等は一般的に リテラル と呼ばれていますわ

\footnotesize \textcolor{lime}{ずんだもん:} リテラル

\footnotesize \textcolor{pink}{四国めたん:} はい、ただ リテラル は、できるだけ使わない方が良いとされていますわ

\footnotesize \textcolor{lime}{ずんだもん:} なぜなのだ?

\footnotesize \textcolor{pink}{四国めたん:} リテラル を使用してもプログラムは動作しますので問題はありませんわ

\footnotesize \textcolor{pink}{四国めたん:} でも、例えば後で配列のサイズを"100"から"50"に変更する必要があった場合を考えてみましょう

\footnotesize \textcolor{lime}{ずんだもん:} 普通に変えれば良いのではないのか?

\footnotesize \textcolor{pink}{四国めたん:} その通りなのですが、配列のサイズだけではなく、ループの回数等を全て変更する必要がありますわ

\footnotesize \textcolor{lime}{ずんだもん:} 意外にめんどうなのか?

\footnotesize \textcolor{pink}{四国めたん:} 短いプログラムであれば簡単に変更が可能ですわ

\footnotesize \textcolor{pink}{四国めたん:} でも、長くて複雑なプログラムの場合には変更が大変ですわね

\footnotesize \textcolor{lime}{ずんだもん:} たしかに、そのとおりなのだ

\footnotesize \textcolor{pink}{四国めたん:} 更に、1つでも変更を忘れると問題が発生しますわ

\footnotesize \textcolor{lime}{ずんだもん:} それはこまるのだ

\footnotesize \textcolor{pink}{四国めたん:} ところで、数値を直接指定した場合、その数値が何を表しているのか、すぐにわかりますか?

\footnotesize \textcolor{lime}{ずんだもん:} 無理なのだ

\footnotesize \textcolor{pink}{四国めたん:} このような数値は マジックナンバー と呼ばれ、あまり歓迎されませんわ

\footnotesize \textcolor{lime}{ずんだもん:} マジックナンバー

\footnotesize \textcolor{pink}{四国めたん:} はい、そこで今回は 定数の定義 によって マジックナンバー を避ける方法をお話ししますわ

\footnotesize \textcolor{lime}{ずんだもん:} おねがいなのだ

\footnotesize \textcolor{pink}{四国めたん:} さらに、配列のサイズやループの回数等を 定義された定数 で指定する方法もお話ししますわね

\footnotesize \textcolor{lime}{ずんだもん:} はやく教えるのだ

定数を定義しましょう

\footnotesize \textcolor{pink}{四国めたん:} まず、C言語の 定数の定義 の書き方は、一般的に3通り存在しますわ

\footnotesize \textcolor{lime}{ずんだもん:} 3通りもあるのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、1つはdefineディレクティブを使用する方法ですわ

\footnotesize \textcolor{lime}{ずんだもん:} ふむふむ

\footnotesize \textcolor{pink}{四国めたん:} もう1つはconstキーワードを使用する方法、最後に 列挙子 を使用する方法ですわね

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

defineディレクティブで定数を

\footnotesize \textcolor{lime}{ずんだもん:} ところで ディレクティブ とは、何なのだ?

\footnotesize \textcolor{pink}{四国めたん:} ディレクティブ は、和訳すれば 指示文 のことですわ

\footnotesize \textcolor{lime}{ずんだもん:} 指示をするのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、コンパイラに指示をする文のことで、先頭にシャープ"#"を付加しますわ

\footnotesize \textcolor{lime}{ずんだもん:} そういえばincludeも先頭にシャープ"#"がついていたのだ

\footnotesize \textcolor{pink}{四国めたん:} その通りで、defineも先頭にシャープ"#"を付加して宣言しますわ

\footnotesize \textcolor{lime}{ずんだもん:} どのように使うのだ?

\footnotesize \textcolor{pink}{四国めたん:} 形式としては#define a bという形になりますわね

\footnotesize \textcolor{lime}{ずんだもん:} どのような意味があるのだ?

\footnotesize \textcolor{pink}{四国めたん:} 基本的には aをbに置き換える という意味で マクロ定義 とも呼ばれますわ

\footnotesize \textcolor{lime}{ずんだもん:} ファイル中の文を対象に置き換えるのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、ディレクティブ以下の文で"a"と書かれている部分を"b"に置き換えますわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{pink}{四国めたん:} ちなみに"a"の部分を マクロ定義名 、"b"の部分を と呼びますわね

\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ

\footnotesize \textcolor{lime}{ずんだもん:} ところで文末にセミコロン";"がないのだが、問題ないのか?

\footnotesize \textcolor{pink}{四国めたん:} "include"でも同じですが、ディレクティブの文末にはセミコロン";"は付けませんわね

\footnotesize \textcolor{lime}{ずんだもん:} 必要ないのか…

\footnotesize \textcolor{pink}{四国めたん:} 必要ないと云うか、付けてはいけませんわ

\footnotesize \textcolor{lime}{ずんだもん:} わかったのだ

\footnotesize \textcolor{pink}{四国めたん:} ここで前々回で使用した forループ を使ったプログラムを見てみましょう

#include <stdio.h>

void main() {
  int apple[100] = {0};
  int total = 0;

  for (int i = 0; i < 100; i = i + 1) {
    apple[i] = 100 + i;
  }

  for (int j = 0; j < 100; j = j + 1) {
    total += apple[j];
  }

  int average = total / 100;
  printf("リンゴの重さの平均は%dグラムです\n", average);
}

\footnotesize \textcolor{pink}{四国めたん:} このプログラムをdefineディレクティブを使って書き換えて実行してみましょう

#include <stdio.h>

#define SIZE (100)
#define WEIGHT (100)

void main() {
  int apple[SIZE] = {0};
  int total = 0;

  for (int i = 0; i < SIZE; i++) {
    apple[i] = WEIGHT + i;
  }

  for (int j = 0; j < SIZE; j++) {
    total += apple[j];
  }

  int average = total / SIZE;
  printf("リンゴの重さの平均は%dグラムです\n", average);
}

マクロ定義で書き換え

\footnotesize \textcolor{lime}{ずんだもん:} 前々回と同じく「リンゴの重さの平均は149グラムです」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} その通りですわ

\footnotesize \textcolor{pink}{四国めたん:} では、 マクロ定義 について解説して行きますわ

\footnotesize \textcolor{lime}{ずんだもん:} おねがいするのだ

\footnotesize \textcolor{pink}{四国めたん:} まず、#define SIZE (100)という定義で、それ以降の"SIZE"の部分を"100"に置き換えるように指示していますわ

\footnotesize \textcolor{lime}{ずんだもん:} 4か所が置き換わるのだ

\footnotesize \textcolor{pink}{四国めたん:} また#define WEIGHT (100)という定義で、"WEIGHT"の部分を"100"に置き換えるように指示していますわ

\footnotesize \textcolor{lime}{ずんだもん:} こんどは1か所だけなのだ

\footnotesize \textcolor{pink}{四国めたん:} そうですわね

\footnotesize \textcolor{pink}{四国めたん:} ちなみに置き換えの対象となる マクロ定義名 については慣習として全て大文字の名前を使用しますわ

\footnotesize \textcolor{lime}{ずんだもん:} 慣習なら守る方がいいのだ

\footnotesize \textcolor{lime}{ずんだもん:} でも、区切りが必要な場合はどうするのだ?

\footnotesize \textcolor{pink}{四国めたん:} 区切りについては、例えば"ARRAY_SIZE"のようにアンダースコア'_'を用いますわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{lime}{ずんだもん:} ところで を括弧"()"で括っているのは、何か意味があるのか?

\footnotesize \textcolor{pink}{四国めたん:} いいえ、本来は必要はありませんわ

\footnotesize \textcolor{pink}{四国めたん:} ただ、将来 "define"ディレクティブの高度な使い方 をする場合には、必要となってきますわ

\footnotesize \textcolor{lime}{ずんだもん:} まあ、いづれ必要になるなら、今から習慣付けておくのだ

マクロ定義の括弧

マクロ定義 を括弧"()"で括るのは、バグを防ぐ上では必須です

マクロ定義 は、ただ を置き換えるだけであれば問題ないのですが、式なども置き換えることができます

たとえば#define ONE_PLUS_TWO (1 + 2)とすれば、それ以降のコードで"ONE_PLUS_TWO"の部分は(1 + 2)に置き換えられます

実際に、

#include <stdio.h>
#define ONE_PLUS_TWO (1 + 2)

void main() {
  int a = ONE_PLUS_TWO * 3;
  printf("答えは%dです\n", a);
}

と云うコードは

#include <stdio.h>

void main() {
  int a = (1 + 2) * 3;
  printf("答えは%dです\n", a);
}

と同等です

ところが、括弧"()"が無く、#define ONE_PLUS_TWO 1 + 2となっている場合は、それ以降のコードで"ONE_PLUS_TWO"の部分は1 + 2に置き換えられます

つまり

#include <stdio.h>
#define ONE_PLUS_TWO 1 + 2

void main() {
  int a = ONE_PLUS_TWO * 3;
  printf("答えは%dです\n", a);
}

と云うコードは

#include <stdio.h>

void main() {
  int a = 1 + 2 * 3;
  printf("答えは%dです\n", a);
}

となります

結果として、2 * 3が優先的に計算されるために、期待した答えにならなくなります

\footnotesize \textcolor{lime}{ずんだもん:} ところで マクロ定義 を使うことで、何かいいことがあるのか?

\footnotesize \textcolor{pink}{四国めたん:} 例えば、配列のサイズを急に半分にしたくなった場合はどうなるでしょう

\footnotesize \textcolor{lime}{ずんだもん:} #define SIZE (50)と、 を半分にすれば良いのだ

\footnotesize \textcolor{pink}{四国めたん:} その通りですわ

\footnotesize \textcolor{pink}{四国めたん:} でも マクロ定義 を使っていなければ"SIZE"の部分の全てを書き換える必要がありますわ

\footnotesize \textcolor{lime}{ずんだもん:} 4か所も書き換えるのか…

\footnotesize \textcolor{pink}{四国めたん:} もっと大きなプログラムでは、数十か所、数百か所の書き換えになるかも知れませんね

\footnotesize \textcolor{lime}{ずんだもん:} なるほど、 マクロ定義 を使っていれば、1か所の変更で済むのか…

\footnotesize \textcolor{pink}{四国めたん:} その他にも、 マクロ定義名 によって が何を表しているかが判り易くなりますわね

\footnotesize \textcolor{lime}{ずんだもん:} そうなのか?

\footnotesize \textcolor{pink}{四国めたん:} 例えばapple[i] = 100 + i;の行で"100"を"SIZE"ではなく"WEIGHT"で書き換えているのは、ここでの"100"は"重さ"を示しているからですわ

\footnotesize \textcolor{lime}{ずんだもん:} たしかに判り易いのだ

constキーワードで定数を

\footnotesize \textcolor{pink}{四国めたん:} 定数を定義する別の方法としてはconstキーワードを使用する方法がありますわ

\footnotesize \textcolor{lime}{ずんだもん:} constキーワード?

\footnotesize \textcolor{pink}{四国めたん:} はい、これは変数の宣言時にconstを前に付加することで、定数を定義する方法ですわ

\footnotesize \textcolor{pink}{四国めたん:} 正確には、constは、変数の値の変更を禁止するキーワードですわね

\footnotesize \textcolor{lime}{ずんだもん:} つまりconstを指定した場合には、宣言と初期化を同時に行う必要があるということか?

\footnotesize \textcolor{pink}{四国めたん:} 鋭いですわね

\footnotesize \textcolor{lime}{ずんだもん:} まかせるのだ

\footnotesize \textcolor{pink}{四国めたん:} 実際にプログラムを書いてみましょう

#include <stdio.h>

void main() {
  const int size = 100;
  const int weight = 100;
  int apple[size] = {0};
  int total = 0;

  for (int i = 0; i < size; i++) {
    apple[i] = weight + i;
  }

  for (int j = 0; j < size; j++) {
    total += apple[j];
  }

  int average = total / size;
  printf("リンゴの重さの平均は%dグラムです\n", average);
}

\footnotesize \textcolor{lime}{ずんだもん:} あれ?"apple"の配列の宣言部分で「式には定数値が必要です」というエラーが出ているのだ

配列のエラー

\footnotesize \textcolor{pink}{四国めたん:} はい、実はC言語ではconstで定義した定数は、配列の宣言時のサイズ指定には使用できないのですね

\footnotesize \textcolor{lime}{ずんだもん:} そうなのか?

\footnotesize \textcolor{pink}{四国めたん:} ですので配列のサイズの指定は別の方法、例えば整数のリテラル等で行う必要がありますわ

\footnotesize \textcolor{lime}{ずんだもん:} う~ん、めんどうなのだ

\footnotesize \textcolor{pink}{四国めたん:} 書き直して、実行してみましょう

#include <stdio.h>

void main() {
  const int size = 100;
  const int weight = 100;
  int apple[100] = {0};
  int total = 0;

  for (int i = 0; i < size; i++) {
    apple[i] = weight + i;
  }

  for (int j = 0; j < size; j++) {
    total += apple[j];
  }

  int average = total / size;
  printf("リンゴの重さの平均は%dグラムです\n", average);
}

constキーワードで書き換え

\footnotesize \textcolor{lime}{ずんだもん:} 今度はエラーが出ずに実行できたのだ

\footnotesize \textcolor{pink}{四国めたん:} ちなみにconstを使用した定数の名前については、一般的には小文字を使用しますわ

\footnotesize \textcolor{lime}{ずんだもん:} マクロ定義名 のように、大文字ではないのか

\footnotesize \textcolor{pink}{四国めたん:} はい、これらはconstにより値の変更が制限されているだけで、実際には変数ですわ

\footnotesize \textcolor{pink}{四国めたん:} ですので、名前の付け方も変数と同じになりますわ

列挙体は定数として使えます

\footnotesize \textcolor{pink}{四国めたん:} 次は 列挙体 もしくは enumeration ですわね

\footnotesize \textcolor{lime}{ずんだもん:} 列挙体

\footnotesize \textcolor{pink}{四国めたん:} はい、 列挙体列挙子 もしくは enumerator と呼ばれる定数のセットと考えていいかと思いますわ

\footnotesize \textcolor{lime}{ずんだもん:} 列挙子

\footnotesize \textcolor{pink}{四国めたん:} いきなりでは、よく分かりませんわね

\footnotesize \textcolor{lime}{ずんだもん:} ちんぷんかんぷんなのだ

\footnotesize \textcolor{pink}{四国めたん:} まずは 列挙体 の形式を示しますわね

enum タグ {
  列挙子(定数名)1,
  列挙子(定数名)2,
    :
  列挙子(定数名)n,
};

\footnotesize \textcolor{pink}{四国めたん:} まず、enumのキーワードに続けて、 列挙体 を識別するための タグ を記述しますわ

\footnotesize \textcolor{lime}{ずんだもん:} 列挙体タグ で識別するのか

\footnotesize \textcolor{pink}{四国めたん:} はい、 タグ は小文字で指定しますわ

\footnotesize \textcolor{pink}{四国めたん:} また、 列挙体 を区別する必要がなければ、 タグ を省略してもOKですわ

\footnotesize \textcolor{lime}{ずんだもん:} なんか適当なのだ

\footnotesize \textcolor{pink}{四国めたん:} 続く波括弧"{}"内に 定数名 の代わりとなる 列挙子 をカンマ区切りで列挙しますわ

\footnotesize \textcolor{pink}{四国めたん:} ちなみに 列挙子 の名前は、慣習として大文字を用いますわ

\footnotesize \textcolor{lime}{ずんだもん:} ということは、単語の区切りが必要な場合にはアンダースコア _ を使用するのか?

\footnotesize \textcolor{pink}{四国めたん:} その通りですわ

\footnotesize \textcolor{lime}{ずんだもん:} 最後の定数名の後にもカンマが付いているが、なぜなのだ?

\footnotesize \textcolor{pink}{四国めたん:} 本来は、カンマは必要ないのですが、付加しておくと便利だったりします

最後の列挙子の後のカンマ

最後の 列挙子 にカンマを付加するのは、C99より前はコンパイラ依存でしたが、C99以降は正式に許可されました

人によって好き嫌いがあるようですので、書き方が統一されていれば良いと思います

一般的なメリットとしては、後で 列挙子 を追加することになった際に、カンマの付け忘れを防ぐことができることでしょうか?

多くのコードを書くようになると、十分なメリットに感じるのですが…

\footnotesize \textcolor{lime}{ずんだもん:} ところで 定数の定義 と言いつつ、何処にも が設定されていないのだが…

\footnotesize \textcolor{pink}{四国めたん:} はい、 列挙体 では、 を指定しない場合には、自動的に上の 列挙子 から、0, 1, 2…と順番に整数値が割り当てられますわ

\footnotesize \textcolor{lime}{ずんだもん:} 好きな を割り当てられないのか?

\footnotesize \textcolor{pink}{四国めたん:} 特定の を割り当てたい場合には、"="を使用して を代入することができますわ

\footnotesize \textcolor{pink}{四国めたん:} 実際の例を見てみましょう

enum 列挙子名 {
  列挙子(定数名)1 = 1,
  列挙子(定数名)2 = 2,
    :
  列挙子(定数名)n = n,
};

\footnotesize \textcolor{pink}{四国めたん:} つまり 列挙子 は整数の定数の代わりとして使用できるのですわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{lime}{ずんだもん:} ところで 列挙子 に浮動小数点数を割り当てることはできるのか?

\footnotesize \textcolor{pink}{四国めたん:} 残念ですが、 列挙子 には浮動小数点数等の整数値以外を割り当てることはできませんわ

\footnotesize \textcolor{lime}{ずんだもん:} 本当に残念なのだ

\footnotesize \textcolor{pink}{四国めたん:} それでは 列挙体 を使ってプログラムを書き換えて実行してみましょう

#include <stdio.h>

void main() {
  enum {
    SIZE = 100,
    WEIGHT = 100,
  };

  int apple[SIZE] = {0};
  int total = 0;

  for (int i = 0; i < SIZE; i++) {
    apple[i] = WEIGHT + i;
  }

  for (int j = 0; j < SIZE; j++) {
    total += apple[j];
  }

  int average = total / SIZE;
  printf("リンゴの重さの平均は%dグラムです\n", average);
}

列挙体で書き換え

\footnotesize \textcolor{lime}{ずんだもん:} 特に変わりはないのだ

\footnotesize \textcolor{lime}{ずんだもん:} ところで 列挙子 の"SIZE"と"WEIGHT"に、同じ (100)を割り当てているけど…

\footnotesize \textcolor{pink}{四国めたん:} はい、同じ整数値を割り当てても問題ありませんわ

\footnotesize \textcolor{pink}{四国めたん:} さらにconstを使用した定数と異なり、 列挙子 は配列のサイズとして使用することもできますわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

まとめ

\footnotesize \textcolor{pink}{四国めたん:} お疲れさまでした

\footnotesize \textcolor{lime}{ずんだもん:} おつかれさまなのだ

\footnotesize \textcolor{lime}{ずんだもん:} ところで定数として使うのは、どれが一番なのだ?

\footnotesize \textcolor{pink}{四国めたん:} どれも一長一短がありますわね

\footnotesize \textcolor{pink}{四国めたん:} 以前はdefineディレクティブを使うのが好まれましたわ

\footnotesize \textcolor{lime}{ずんだもん:} 最近は?

\footnotesize \textcolor{pink}{四国めたん:} 最近では、できるだけconstキーワードを使うのが流行りかも知れませんわ

\footnotesize \textcolor{lime}{ずんだもん:} 列挙体 は、どうなのだ?

\footnotesize \textcolor{pink}{四国めたん:} 列挙体 は、少し特殊なので、使いどころを選びますわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{pink}{四国めたん:} 以上で 定数と列挙体 を終了します

Discussion