Linux講座にようこそ。このページは「C言語プログラミング入門 - 第12章.その他の型」です。

C言語プログラミング入門

12. その他の型(1/4)

12.1 型の種類

C言語の型名の一部については「【表2-1】 型名一覧(代表的なもの)」で紹介しましたが、それらも含めて、一覧表で示します。

【表12-1】 C言語の型名一覧
区分型名、その他
符号付整数型char、signed char
short、short int、signed short、signed short int
int、signed int、signed
long、long int、signed long int、signed long
long long、long long int、signed long long int、signed long long
(領域長(バイト)はshort≦int≦long≦long longの関係です)
符号なし整数型unsigned char
unsigned short、unsigned short int
unsigned int、unsigned
unsigned long、unsigned long int
unsigned long long、unsigned long long int
浮動小数点数型float、double、long double
(領域長(バイト)はdouble≦long doubleの関係です)
配列型void型以外は配列型として宣言して使うことができます。配列の取扱いについては「7.たくさんのデータを処理するための配列」を参照してください。
ポインタ型すべての型はポインタ型として宣言して使うことができます。ポインタの取扱いについては「8.データをアドレスで操作するためのポインタ」を参照してください。
関数型ある型名の値を返す関数は関数型です。関数の取扱いについては「9.プログラムの部品化のための関数」を参照してください。
構造体型異なる型名のデータをまとめて取り扱いたい場合は構造体型として宣言して使うことができます。構造体の取扱いについては「11.異なる型名データをまとめて取り扱う構造体」を参照してください。
void型型を持たないという特殊な型名です。返り値を持たない関数の型名などに使用します。
共用体型メモリ領域を複数の型名データで供用するためのものです。以降で説明します。
列挙型列挙定数と呼ぶ識別子によって定義された整数値の集合を使用するためのものです。以降で説明します。

ここでは共用体型列挙型および、ユーザ固有の型を定義するためのtypedefについて説明します。

12.2 共用体型

共用体とは
【図12-1】共用体とは

構造体の場合の各メンバ一はメモリ上に順番に配置されます。例えば、図12-1の構造体ではint型のデータ(4バイト長)、double型のデータ(8バイト長)、char型のデータ(1バイト長)ですので、この構造体は13バイトの領域となります。(コンピュータの種類やOSにより異なります。これはあくまでも一例です)また、この構造体には3種類のデータを格納することができます。

一方、共用体の場合の各メンバ一はメモリ上の同じ領域に配置されます。例えば、図12-1の共用体ではint型のデータ(4バイト長)、double型のデータ(8バイト長)、char型のデータ(1バイト長)が同じ領域に配置されます。この共用体の大きさはメンバ一の中で一番大きなdouble型のデータの大きさの8バイトになります。

なお、共用体は同じ領域を共有することから、1種類のデータしか格納できませんので注意してください。例えば、図12-1の共用体にint型のデータを格納した後にdouble型のデータを格納するとdouble型のデータで上書きされます。

共用体は条件により、取り扱うデータの型が異なるデータを格納する領域を確保するような場合に便利です。構造体を使うよりもメモリ使用量を減らせる可能性があります。

12.2.1 形式

構造体と共用体はよく似ていますので、使い方もほとんど同じです。相違点は型指定子としてstructの代わりにunionを指定します。

※ 共用体型名の宣言
union 共用体型名 {型名 メンバー名1; 型名 メンバー名2; …};

※ 共用体変数の宣言
union 共用体型名 共用体変数名 = {初期値};

※ 共用体型名の宣言と共用体変数の宣言を同時に行う
union 共用体型名 {型名 メンバー名1; 型名 メンバー名2; …} 共用体変数名 = {初期値};

※ メンバーの参照・更新
共用体変数名.メンバー名;
共用体変数名.メンバー名 = 値;
ポインタ変数名->メンバー名;
ポインタ変数名->メンバー名 = 値;
union
共用体を宣言するための型指定子です。
共用体型名
任意の共用体型名を宣言します。共用体型名は自由につけることが出来ます。共用体型名の宣言と共用体変数の宣言を同時に行う場合は共用体型名は省略できます。
型名 メンバー名
メンバーの型名とメンバー名を指定します。配列の指定も出来ます。
共用体変数名
共用体型名を宣言しただけではメモリ上に実体は確保されていませんので、共用体変数名を宣言することにより変数としてメモリ上に実体を確保します。
.
共用体変数名を使ってメンバーを参照・更新する場合はドット演算子(.)を使用します。
->
予め、ポインタ変数に共用体変数の先頭アドレスを設定しておき、そのポインタ変数を使ってメンバーを参照・更新する場合はアロー演算子(->)を使用します。

12.2.2 例題

四角形、三角形および、円の面積を計算します。面積を計算するためのデータ(長さや高さ)は図形毎に用意した構造体に格納しますが、それらは同時に使うことはないため、共用体として再定義して使用します。

  1. #include <stdio.h>
  2. /* 四角形の縦と横の長さを格納する構造体 */
  3. struct rectangle_type
  4. {
  5.     double  width;     /* 横の長さ */
  6.     double  height;    /* 縦の長さ */
  7. };
  8. /* 三角形の底辺と高さを格納する構造体 */
  9. struct triangle_type
  10. {
  11.     double  base;      /* 底辺の長さ */
  12.     double  altitude;  /* 高さ */
  13. };
  14. /* 円の半径を格納する構造体 */
  15. struct circle_type
  16. {
  17.     double  radius;    /* 半径の長さ */
  18. };
  19. /* 面積データを格納する構造体 */
  20. struct area_type
  21. {
  22.     int                         figure_kind;           /* 図形の種類(四角形 … 1、三角形 … 2、円 … 3) */
  23.     double                      area;                  /* 面積 */
  24.     union length_type
  25.     {
  26.         struct rectangle_type   rectangle_length;      /* 四角形の縦と横の長さ */
  27.         struct triangle_type    triangle_length;       /* 三角形の底辺と高さ */
  28.         struct circle_type      circle_length;         /* 円の半径 */
  29.     }                           length;                /* 面積を求めるためのデータ */
  30. };
  31.  
  32. int main(void)
  33. {
  34.     struct area_type    area_data;
  35.     int                 return_code;
  36.     /* CalcArea関数のプロトタイプ宣言 */
  37.     int CalcArea(struct area_type *Area);
  38.  
  39.     printf("図形の面積を計算します\n");
  40.     printf("四角形 --- 1\n");
  41.     printf("三角形 --- 2\n");
  42.     printf("円 ------- 3\n");
  43.     printf("図形の種類を番号で指定してください ==> ");
  44.     scanf("%d", &area_data.figure_kind);
  45.  
  46.     if(area_data.figure_kind >= 1 && area_data.figure_kind <= 3)
  47.     {
  48.         switch(area_data.figure_kind)
  49.         {
  50.             case 1:
  51.                 printf("縦と横の長さを入力してください ==> ");
  52.                 scanf("%lf%lf", &area_data.length.rectangle_length.width,
  53.                                 &area_data.length.rectangle_length.height);
  54.                 break;
  55.             case 2:
  56.                 printf("底辺と高さを入力してください ==> ");
  57.                 scanf("%lf%lf", &area_data.length.triangle_length.base,
  58.                                 &area_data.length.triangle_length.altitude);
  59.                 break;
  60.             case 3:
  61.                 printf("半径を入力してください ==> ");
  62.                 scanf("%lf", &area_data.length.circle_length.radius);
  63.                 break;
  64.         }
  65.  
  66.         return_code = CalcArea(&area_data);
  67.         if(return_code == 0)
  68.         {
  69.             printf("面積は%.2fです\n", area_data.area);
  70.         }
  71.         else
  72.         {
  73.             printf("面積の計算が出来ませんでした\n");
  74.         }
  75.     }
  76.     else
  77.     {
  78.         printf("番号が不当です\n");
  79.         return_code = 1;
  80.     }
  81.  
  82.     return return_code;
  83. }
  84.  
  85. /* 面積の計算を行う */
  86. int CalcArea(struct area_type *pArea)
  87. {
  88.     int return_code = 0;
  89.  
  90.     switch(pArea->figure_kind)
  91.     {
  92.         case 1:
  93.             /* 四角形の面積の計算を行う */
  94.             if(pArea->length.rectangle_length.width > 0.0 &&
  95.                 pArea->length.rectangle_length.height > 0.0)
  96.             {
  97.                 pArea->area = pArea->length.rectangle_length.width *
  98.                                 pArea->length.rectangle_length.height;
  99.             }
  100.             else
  101.             {
  102.                 return_code = 1;
  103.             }
  104.             break;
  105.         case 2:
  106.             /* 三角形の面積の計算を行う */
  107.             if(pArea->length.triangle_length.base > 0.0 &&
  108.                 pArea->length.triangle_length.altitude > 0.0)
  109.             {
  110.                 pArea->area = pArea->length.triangle_length.base *
  111.                                 pArea->length.triangle_length.altitude / 2.0;
  112.             }
  113.             else
  114.             {
  115.                 return_code = 1;
  116.             }
  117.             break;
  118.         case 3:
  119.             /* 円の面積の計算を行う */
  120.             if(pArea->length.circle_length.radius > 0.0)
  121.             {
  122.                 pArea->area = pArea->length.circle_length.radius *
  123.                                 pArea->length.circle_length.radius * 3.14;
  124.             }
  125.             else
  126.             {
  127.                 return_code = 1;
  128.             }
  129.             break;
  130.     }
  131.  
  132.     return return_code;
  133. }
$ ./ex12_1.prg
図形の面積を計算します
四角形 --- 1
三角形 --- 2
円 ------- 3
図形の種類を番号で指定してください ==> 1
縦と横の長さを入力してください ==> 2.5 3.5
面積は8.75です
$
$ ./ex12_1.prg
図形の面積を計算します
四角形 --- 1
三角形 --- 2
円 ------- 3
図形の種類を番号で指定してください ==> 2
底辺と高さを入力してください ==> 2.5 3.5
面積は4.38です
$
$ ./ex12_1.prg
図形の面積を計算します
四角形 --- 1
三角形 --- 2
円 ------- 3
図形の種類を番号で指定してください ==> 3
半径を入力してください ==> 2.5
面積は19.62です
$
$ ./ex12_1.prg
図形の面積を計算します
四角形 --- 1
三角形 --- 2
円 ------- 3
図形の種類を番号で指定してください ==> 4
番号が不当です
$
3〜7行目
四角形の縦と横の長さを格納するための構造体型名(rectangle_type)を宣言します。
9〜13行目
三角形の底辺と高さを格納するための構造体型名(triangle_type)を宣言します。
15〜18行目
円の半径を格納するための構造体型名(circle_type)を宣言します。
20〜30行目
面積を計算するのに必要なデータを格納するための構造体型名(area_type)を宣言します。上記3つの構造体を共用体として組み込みます。
34行目
上記構造体型名(area_type)を使用して構造体変数(area_data)を宣言します。
37行目
面積の計算を行うCalcArea関数の関数プロトタイプ宣言です。返り値はint型で計算が出来たときは0を、出来なかったときは1を返します。引数は計算を行うためのデータが格納されている構造体(area_data)の先頭アドレスを指定します。
44行目
図形の種類を整数値でarea_dataのfigure_kindメンバ一に入力します。
52〜53行目
四角形の縦と横の長さをarea_dataのlength.rectangle_length.widthメンバ一と、area_dataのlength.rectangle_length.heightメンバ一に入力します。
57〜58行目
三角形の底辺と高さをarea_dataのlength.triangle_length.baseメンバ一と、area_dataのlength.triangle_length.altitudeメンバ一に入力します。
62行目
円の半径をarea_dataのlength.circle_length.radiusメンバ一に入力します。
66行目
面積の計算を行うCalcArea関数を呼び出します。引数はarea_dataのアドレスです。
69行目
CalcArea関数で求めた面積の値を出力します。
86行目
CalcArea関数の定義です。
90行目
図形の種類により、処理を振り分けるために、area_dataのfigure_kindメンバ一を参照します。area_dataのアドレスが引数で渡ってきていますので、それを使って参照するにはアロー演算子(->)を使います
97〜98行目
四角形の面積の計算を行います。横の長さはpArea->lengthメンバ一の中のrectangle_length.widthメンバ一で、縦の長さはpArea->lengthメンバ一の中のrectangle_length.heightメンバ一です。lengthメンバ一の中のメンバ一を参照するにはドット演算子を使いますので注意して下さい。
110〜111行目
三角形の面積の計算を行います。底辺の長さはpArea->lengthメンバ一の中のtriangle_length.baseメンバ一で、高さはpArea->lengthメンバ一の中のtriangle_length.altitudeメンバ一です。
122〜123行目
円の面積の計算を行います。半径はpArea->lengthメンバ一の中のcircle_length.radiusメンバ一です。
132行目
面積の計算が出来たときは返り値として0を、出来なかったときは1を返します。