🖥️

【C言語超入門】 第25回 型定義

2024/12/25に公開

https://youtu.be/TitZhkXSs6g

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

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

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

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

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

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

\footnotesize \textcolor{pink}{四国めたん:} C言語ではintcharfloat等の予め決められた型が定義されていますわ

\footnotesize \textcolor{lime}{ずんだもん:} 結構、多くの型があるのだ

\footnotesize \textcolor{pink}{四国めたん:} ただ、決められた型のみでは色々と不便なことも多くありますわ

\footnotesize \textcolor{lime}{ずんだもん:} 確かに自分たちで型を定義できれば便利なのだ

\footnotesize \textcolor{pink}{四国めたん:} ですので、C言語では独自の型を定義する、もしくはエイリアスをつけるための方法が提供されていますわ

\footnotesize \textcolor{lime}{ずんだもん:} エイリアス?

\footnotesize \textcolor{pink}{四国めたん:} はい、別名のことですわ

\footnotesize \textcolor{lime}{ずんだもん:} どのように定義するのだ?

\footnotesize \textcolor{pink}{四国めたん:} typedefキーワードを使って、型を定義するための宣言を行いますわ

既存の型にエイリアスを付けましょう

\footnotesize \textcolor{pink}{四国めたん:} まず最初に、 エイリアス についてお話ししますわ

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

\footnotesize \textcolor{pink}{四国めたん:} 基本的には エイリアス は既存の型につけますわね

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

\footnotesize \textcolor{pink}{四国めたん:} 形式としてはtypedef 元の型 エイリアス名;と云う形になりますわ

\footnotesize \textcolor{lime}{ずんだもん:} typedefの後に、変数を宣言するような形式でエイリアス名を定義しているのだ

\footnotesize \textcolor{pink}{四国めたん:} まあ、そんな感じですわね

\footnotesize \textcolor{lime}{ずんだもん:} ところで、どうして エイリアス を定義するのだ?

\footnotesize \textcolor{pink}{四国めたん:} たとえばcharは1バイトの型ですが型名からは判りづらいと思いませんか?

\footnotesize \textcolor{lime}{ずんだもん:} たしかに...

\footnotesize \textcolor{pink}{四国めたん:} そんな時に char型 のエイリアスとしてbyteと云う名前の型を定義すると判り易くなりますわ

\footnotesize \textcolor{lime}{ずんだもん:} 具体的に教えてほしいのだ

\footnotesize \textcolor{pink}{四国めたん:} わかりましたわ

#include <stdio.h>

void main() {
  typedef char byte;
  byte c[] = {97, 98, 99};
  int length = sizeof(c) / sizeof(byte);

  for (size_t i = 0; i < length; i++) {
    printf("%c ", c[i]);
  }
}

エイリアス

\footnotesize \textcolor{lime}{ずんだもん:} 「a b c」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、しっかりとbytecharの代わりになっていますわね

\footnotesize \textcolor{lime}{ずんだもん:} ところで、もう少し便利さがわかる エイリアス はないのか

\footnotesize \textcolor{pink}{四国めたん:} そうですわね、unsigned intなどは型名としても長くて煩わしいのでtypedef unsigned int uint;などとして短い型名に置き換えるような使い方はどうですか?

#include <stdio.h>

void main() {
  typedef unsigned int uint;
  uint c[] = {97, 98, 99};
  int length = sizeof(c) / sizeof(uint);

  for (size_t i = 0; i < length; i++) {
    printf("%d ", c[i]);
  }
}

エイリアス2

\footnotesize \textcolor{lime}{ずんだもん:} お~、確かに短くなって便利そうなのだ

\footnotesize \textcolor{pink}{四国めたん:} なお、 エイリアス の定義をメイン関数内ではなく、メイン関数の外で行うことも可能ですわ

\footnotesize \textcolor{lime}{ずんだもん:} つまり、定義以降の関数の全てで エイリアス を使うことができるということか

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

\footnotesize \textcolor{pink}{四国めたん:} エイリアス は、関数の外で定義する方が一般的かもしれませんわね

ポインタ型をエイリアスに

\footnotesize \textcolor{pink}{四国めたん:} 次に、ポインタ型に対してもエイリアスを定義することも可能ですわ

\footnotesize \textcolor{lime}{ずんだもん:} どのようにして定義するのだ?

\footnotesize \textcolor{pink}{四国めたん:} 形式としてはtypedef 元の型* エイリアス名;と云う形になりますわ

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZE (20)

typedef char* string;

void main() {
  string s = (string)malloc(SIZE);

  if (s != NULL) {
    strcpy_s(s, SIZE, "This is a test.");
    printf("%s\n", s);
  }

  free(s);
  s = NULL;
}

ポインタのエイリアス

\footnotesize \textcolor{lime}{ずんだもん:} 「This is a test.」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、typedef char* string部分がポインタ型に対するエイリアスの定義部分ですわ

\footnotesize \textcolor{lime}{ずんだもん:} メイン関数の外で定義しているのだ

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

\footnotesize \textcolor{lime}{ずんだもん:} stringの方がchar*より文字数が多いような...

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

\footnotesize \textcolor{pink}{四国めたん:} ですが、文字列を表していることを示すにはchar*よりもstringの方が良いと思いますわ

\footnotesize \textcolor{lime}{ずんだもん:} まぁ、たしかに...

\footnotesize \textcolor{lime}{ずんだもん:} ただ、ポインタを示すアスタリスク*が隠れてしまって、ポインタ型とわからなくなりそうなのだ

\footnotesize \textcolor{pink}{四国めたん:} 確かに、 エイリアス の名前は、ひと目で判るようにしたほうがいいですわね

\footnotesize \textcolor{pink}{四国めたん:} とはいえ、ポインタに絡む間違いは致命的なエラーの元となるので、ポインタ型のエイリアスは慎重に使うようにした方がいいですわね

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

配列をエイリアスに

\footnotesize \textcolor{pink}{四国めたん:} ポインタと同様に、配列に対してもエイリアスを定義することが可能ですわ

\footnotesize \textcolor{lime}{ずんだもん:} どのようにして定義するのだ?

\footnotesize \textcolor{pink}{四国めたん:} 形式としてはtypedef 元の型 エイリアス名[要素数];となりますわ

\footnotesize \textcolor{lime}{ずんだもん:} 要素数の指定は必要なのか?

\footnotesize \textcolor{pink}{四国めたん:} 必須ではありませんが、指定した方がいいですわね

\footnotesize \textcolor{lime}{ずんだもん:} 要素数の指定がない場合は、どうなるのだ?

\footnotesize \textcolor{pink}{四国めたん:} エイリアスを使った宣言時に、初期化も行う必要がありますわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほど、配列の宣言時に要素数を指定しない場合と同じなのか

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

\footnotesize \textcolor{pink}{四国めたん:} では実際のプログラムを見てみましょう

#include <stdio.h>

#define SIZE (20)

typedef char string[SIZE];

void main() {
  string s = "This is a test.";
  printf("%s\n", s);
}

配列のエイリアス

\footnotesize \textcolor{lime}{ずんだもん:} これもしっかり「This is a test.」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、typedef char string[SIZE];の部分が配列に対するエイリアスの定義部分ですわ

\footnotesize \textcolor{pink}{四国めたん:} ちなみにtypedef char string[];と定義しても問題なく動作しますわね

\footnotesize \textcolor{lime}{ずんだもん:} こちらもstringの方がcharより文字数が多いのだ

\footnotesize \textcolor{pink}{四国めたん:} まぁ、文字列を表すにはstringの方が判り易いと思いますわ

\footnotesize \textcolor{lime}{ずんだもん:} う~ん

\footnotesize \textcolor{lime}{ずんだもん:} 配列であることを示す角括弧[]や要素数が隠れてしまって、配列とはわからなくなるのだ

\footnotesize \textcolor{pink}{四国めたん:} 確かに、 エイリアス の名前は、ひと目で判るようにしたほうがいいですわね

\footnotesize \textcolor{pink}{四国めたん:} とはいえ、間違いやすいのも確かなので、配列のエイリアスは慎重に使うようにした方がいいですわね

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

列挙体を型定義します

\footnotesize \textcolor{pink}{四国めたん:} 以前、列挙体の定義はenum タグ {列挙子(定数名)1, ...};と云う形式で行うとお話ししましたわ

\footnotesize \textcolor{lime}{ずんだもん:} おぼえているのだ

\footnotesize \textcolor{pink}{四国めたん:} この場合、列挙体の変数を宣言する際はenum タグ 変数名のようにおこなっていましたわ

\footnotesize \textcolor{lime}{ずんだもん:} そうだったのだ

\footnotesize \textcolor{pink}{四国めたん:} これはこれで特に問題はありませんわ

\footnotesize \textcolor{pink}{四国めたん:} ただ、多くの場所で列挙体の変数を宣言しなければならない場合にはenumを書くのも煩わしくなりますわね

\footnotesize \textcolor{lime}{ずんだもん:} まあ、たしかに...

\footnotesize \textcolor{pink}{四国めたん:} そのような場合にtypedefを用いて 列挙体の型定義 を行うと、プログラムがシンプルで判り易いものになりますわ

\footnotesize \textcolor{lime}{ずんだもん:} 列挙体の型定義

\footnotesize \textcolor{pink}{四国めたん:} はい、形式としてはこのようになりますわ

typedef enum タグ {
  列挙子(定数名)1,
  列挙子(定数名)2,
    :
    :
  列挙子(定数名)n,
} エイリアス名;

\footnotesize \textcolor{lime}{ずんだもん:} 今までの列挙体の定義の最初にtypedefを、最後に エイリアス名 を付けるのか

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

\footnotesize \textcolor{lime}{ずんだもん:} タグとエイリアス名は同じ名前を指定してもいいのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、タグとエイリアス名は同じにしても、エラーにならない場合が多いですわね

\footnotesize \textcolor{pink}{四国めたん:} ただ、問題になる場合もあるので、異なる名前を使用した方がいいですわね

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

\footnotesize \textcolor{pink}{四国めたん:} なお、typedefを用いて列挙体の型定義を行う場合には、タグを省略してもかまいませんわ

\footnotesize \textcolor{lime}{ずんだもん:} タグを省略できるなら、その方が楽なのだ

\footnotesize \textcolor{pink}{四国めたん:} とりあえずプログラムを実行してみましょう

#include <stdio.h>

typedef enum {
  RED,
  YELLOW,
  GREEN,
} fruit_color;

void main() {
  fruit_color apple = RED;

  if (apple == RED) {
    printf("リンゴの色は赤色です。\n");
  } else if (apple == YELLOW) {
    printf("リンゴの色は黄色です。\n");
  } else {
    printf("リンゴの色は緑色です。\n");
  }
}

列挙体の型定義

\footnotesize \textcolor{lime}{ずんだもん:} 「リンゴの色は赤色です。」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、エイリアス名fruit_colorを使って列挙体を扱うことができていますわね

\footnotesize \textcolor{lime}{ずんだもん:} ちなみに、今回は列挙体の定義を関数の外でおこなっているのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、列挙体の定義については関数の外でおこなっても問題ありませんわ

\footnotesize \textcolor{pink}{四国めたん:} むしろ、列挙体は複数の関数で使用する場合が多いので、必然的に関数の外で定義することが多くなりますわ

構造体を型定義します

\footnotesize \textcolor{pink}{四国めたん:} 構造体についても定義はstruct 構造体名 {型 メンバー名; ...};と云う形式で行うとお話ししましたわ

\footnotesize \textcolor{lime}{ずんだもん:} おぼえているのだ

\footnotesize \textcolor{pink}{四国めたん:} この場合、構造体の変数を宣言する際はstruct 構造体名 変数名;のようにおこなっていましたわ

\footnotesize \textcolor{lime}{ずんだもん:} たしかに...

\footnotesize \textcolor{pink}{四国めたん:} これはこれで特に問題はありませんわ

\footnotesize \textcolor{pink}{四国めたん:} ただ、多くの場所で構造体の変数を宣言しなければならない場合にはstructを書くのも煩わしくなりますわね

\footnotesize \textcolor{lime}{ずんだもん:} 列挙体の場合と同じなのだ

\footnotesize \textcolor{pink}{四国めたん:} そのような場合にtypedefを用いて 構造体の型定義 を行うと、プログラムがシンプルで判り易いものになりますわ

\footnotesize \textcolor{lime}{ずんだもん:} 構造体の型定義

\footnotesize \textcolor{pink}{四国めたん:} はい、形式としてはこのようになりますわ

typedef struct 構造体名 {
  型1 メンバー名1;
  型2 メンバー名2;
      :
  型n メンバー名n;
} エイリアス名;

\footnotesize \textcolor{lime}{ずんだもん:} 今までの構造体の定義の最初にtypedefを、最後に エイリアス名 を付けるのか

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

\footnotesize \textcolor{lime}{ずんだもん:} 構造体名とエイリアス名は同じ名前を指定してもいいのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、構造体名とエイリアス名は同じにしても、エラーにならない場合が多いですわね

\footnotesize \textcolor{pink}{四国めたん:} ただ、問題になる場合もあるので、異なる名前を使用した方がいいですわね

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

\footnotesize \textcolor{pink}{四国めたん:} なお、typedefを用いて構造体の型定義を行う場合には、構造体名を省略してもかまいませんわ

\footnotesize \textcolor{lime}{ずんだもん:} 構造体名を省略できるなら、その方が楽なのだ

\footnotesize \textcolor{pink}{四国めたん:} とりあえずプログラムを実行してみましょう

#include <stdio.h>

typedef enum {
  RED,
  YELLOW,
  GREEN,
} fruit_color;

typedef struct {
  int weight;
  fruit_color color;
} fruit;

void main() {
  fruit apple = {100, RED};

  printf("リンゴの重さは%dグラムです。\n", apple.weight);
  if (apple.color == RED) {
    printf("リンゴの色は赤色です。\n");
  } else if (apple.color == YELLOW) {
    printf("リンゴの色は黄色です。\n");
  } else {
    printf("リンゴの色は緑色です。\n");
  }
}

構造体の型定義

\footnotesize \textcolor{lime}{ずんだもん:} 「リンゴの重さは100グラムです。」、「リンゴの色は赤色です。」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、エイリアス名fruitを使って構造体を扱うことができていますわね

\footnotesize \textcolor{lime}{ずんだもん:} 構造体についても定義は関数の外でおこなっているのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、構造体の定義についても関数の外でおこなって、問題ありませんわ

\footnotesize \textcolor{pink}{四国めたん:} むしろ、構造体についても、複数の関数で使用する場合が多いので、必然的に関数の外で定義することが多くなりますわ

まとめ

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

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

\footnotesize \textcolor{pink}{四国めたん:} 以上で 型定義 を終了しますわ

Discussion