構造体 (Structure)

[構造体] とは, 複数の変数を一つのファテゴリ, ユニットにしたものである. 例えば, 文献リストの作成を考えてみよう:

char author[40]; /* 著者 */
char title[80]; /* タイトル */
int year; /* 発行年 */
char resource[120]; /* 出典元 */

構造体型オブジェクト

構造体型オブジェクトを作成するには:

struct タグ{struct-宣言}

struct タグ

のいずれかを使用する. struct-宣言には, 構造体タグがもつ [メンバ] のリストを指定する. 例えば, 文献リストの場合, 次のように宣言すれば:

struct ref{
  char author[40]; /* 著者 */
  char title[80]; /* タイトル */
  int year; /* 発行年 */
  char resource[120]; /* 出典元 */
};

オブジェクト変数を宣言したいときに, 下の例を使う:

struct ref{
  char author[40]; /* 著者 */
  char title[80]; /* タイトル */
  int year; /* 発行年 */
  char resource[120]; /* 出典元 */
};

struct ref paper, book;

宣言の文法に従えば, 構造体 ref 型とオブジェクトを同時に指定することもできる:

struct ref{
  char author[40]; /* 著者 */
  char title[80]; /* タイトル */
  int year; /* 発行年 */
  char resource[120]; /* 出典元 */
}paper, book;

構造体を初期化する:

struct ref{
  char author[40]; /* 著者 */
  char title[80]; /* タイトル */
  int year; /* 発行年 */
  char resource[120]; /* 出典元 */
}paper = {
  "J. von Neumann",
  "Zur Theorie der Gesellschaftsspiele",
  1928,
  "Mathematische Annalen"
};

struct ref book = {
  "J. von Neumann and O. Morgenstern",
  "Theory of Games and Economic Behavior",
  1944,
  "Princeton University Press"
};

メンバ演算子 .

構造体オブジェクトはメンバを持ち, メンバを参照するための演算子は [メンバ演算子] と [ポインタ] 演算子 の2つがある. ここでは, メンバ演算子を見る.

メンバ演算子 (Member Operator):

構造体型オブジェクト.メンバ名

member_op.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>

struct ref{
  char author[40]; /* 著者 */
  char title[80]; /* タイトル */
  int year; /* 発行年 */
  char resource[120]; /* 出典元 */
};

int main(void){
  struct ref paper = {
    "J. von Neumann",
    "Zur Theorie der Gesellschaftsspiele",
    1928,
    "Mathematische Annalen"
  };

  printf("[AU] %s\n", paper.author);
  printf("[TI] %s\n", paper.title);
  printf("[YR] %d\n", paper.year);
  printf("[SO] %s\n", paper.resource);

  return 0;
}

member_op.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./member_op
[AU] J. von Neumann
[TI] Zur Theorie der Gesellschaftsspiele
[YR] 1928
[SO] Mathematische Annalen

member_op2.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <stdio.h>

struct s{
  char c;
  double r[3];
}x = {
  'A',
  {1.5, -3.0, 2.0}
};

int main(void){
  printf("c\tr[0]\t\tr[1]\t\tr[2]\n");
  printf("%c\t%f\t%f\t%f\n", x.c, x.r[0], x.r[1], x.r[2]);

  return 0;
}

member_op2.c の実行結果は,

c	r[0]		r[1]		r[2]
A	1.500000	-3.000000	2.000000

構造体配列

struct_array.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <string.h>

struct ref{
  char author[40]; /* 著者 */
  char title[80]; /* タイトル */
  int year; /* 発行年 */
  char resource[120]; /* 出典元 */
}paper[100], book[100];

int main(void){
  memcpy(paper[0].author, "J. von Neumann", sizeof(paper[0].author) - 1);
  memcpy(paper[0].title, "Zur Theorie der Gesellschaftsspiele", sizeof(paper[0].title) - 1);
  paper[0].year = 1928;
  memcpy(paper[0].resource, "Mathematische Annalen", sizeof(paper[0].resource) - 1);

  printf("[AU] %s\n", paper[0].author);
  printf("[TI] %s\n", paper[0].title);
  printf("[YR] %d\n", paper[0].year);
  printf("[SO] %s\n", paper[0].resource);

  return 0;
}

struct_array.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./struct_array
[AU] J. von Neumann
[TI] Zur Theorie der Gesellschaftsspiele
[YR] 1928
[SO] Mathematische Annalen

struct_array2.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

struct s{
  int n;
  char str[12];
}x[100] = {
  { 1, "first"  },
  { 2, "second" },
  { 3, "third"  },
  { 4, "Fouth"  },
  { 5, "Fifth"  }
};

int main(void){
  int i;
  for(i = 0; x[i].n != 0; i++)
    printf("x[%d] %d\t%s\n", i, x[i].n, x[i].str);

  return 0;
}

struct_array2.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./struct_array2
x[0] 1  first
x[1] 2  second
x[2] 3  third
x[3] 4  Fouth
x[4] 5  Fifth

構造体の構造体

struct_struct.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <string.h>

struct ref{
  char author[40]; /* 著者 */
  char title[80]; /* タイトル */
  int year; /* 発行年 */
  char resource[120]; /* 出典元 */
};

struct list{
  char class[32];
  struct ref paper[100]; /* 構造体の構造体 */
}list;

int main(void){
  memcpy(list.class, "Game Theory", sizeof(list.class)-1);
  memcpy(list.paper[0].author, "J. von Neumann", sizeof(list.paper[0].author)-1);
  memcpy(list.paper[0].title, "Zur Theorie der Gesellschaftsspiele", sizeof(list.paper[0].title)-1);
  memcpy(list.paper[0].resource, "Mathematische Annalen", sizeof(list.paper[0].resource)-1);
  list.paper[0].year = 1928;

  printf("[AU] %s\n", list.paper[0].author);
  printf("[TI] %s\n", list.paper[0].title);
  printf("[YR] %d\n", list.paper[0].year);
  printf("[SO] %s\n", list.paper[0].resource);
  printf("[FI] %s\n", list.class);

  return 0;
}

struct_struct.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./struct_struct
[AU] J. von Neumann
[TI] Zur Theorie der Gesellschaftsspiele
[YR] 1928
[SO] Mathematische Annalen
[FI] Game Theory

struct_struct2.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>

struct s{
  char c;
  char str[12];
};

struct st{
  int n;
  struct s x;
}y[20] = {
  { 1, {'a', "first"  } },
  { 2, {'b', "second" } },
  { 3, {'c', "third"  } }
};

int main(void){
  int i;
  for(i = 0; y[i].n != 0; i++)
    printf("y[%d] %d %c %s\n", i, y[i].n, y[i].x.c, y[i].x.str);

  return 0;
}

struct_struct2.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./struct_struct2
y[0] 1 a first
y[1] 2 b second
y[2] 3 c third

ポインタ演算子 ->

ポインタ演算子 (Pointer Operator):

ポインタ->メンバ名

pointer_arith.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <string.h>

struct s{
  char n;
  char str[12];
}x[20] = {
  { 1, "first"  },
  { 2, "second" },
  { 3, "third"  }
};

int main(void){
  struct s *p;
  
  for(p = x; (p->n) != 0; p++)
    printf("x[%td] %d %s\n", p-x, p->n, p->str);
  
  p->n = 4;
  memcpy(p->str, "fourth", sizeof(p->str)-1);
  printf("x[%td] %d %s\n", p-x, p->n, p->str);

  return 0;
}

pointer_arith.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./pointer_arith
x[0] 1 first
x[1] 2 second
x[2] 3 third
x[3] 4 fourth

自己参照型構造体

構造体のメンバに自己へのポインタが入っている構造体を [自己参照型構造体] (自己参照構造体, Self-Referential Structure) という.

self_struct.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>

struct s{
  int n;
  struct s *p;
}x, *a;

int main(void){
  a = &x;
  a->n = 1;
  printf("(1) x.n = %d\t[%p]\n", x.n, &(x.n));
  /* &x.n = &(x.n) */
  /* &(x.n) = &(a->n) */

  printf("(2) x.p \t[%p]->", x.p);
  a->p = a;
  printf("[%p]\n", x.p);

  x.p->n = 2;
  printf("(3) x.n = %d\t[%p]\n", x.n, &(x.n));

  return 0;
}

self_struct.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./self_struct
(1) x.n = 1  [0x100001080]
(2) x.p      [0x0]->[0x100001080]
(3) x.n = 2  [0x100001080]

自己参照型構造体の応用: リスト

list.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

struct list{
  int n;
  char str[12];
  struct list *next;
};

struct list *createData(int num, char *word);
/* 返却値が struct list * 型の関数 */

int main(void){
  int i; // インクリメント用
  char *keywords[10] = {
    "\\0",
    "if",
    "while",
    "for",
    "do",
    "switch",
    "case",
    "break",
    "int",
    "char"
  };

  struct list *head, *new, *data;
  /*
    struct list *head は リストの先頭アドレスを保存ている.
    struct list *new は リストの一個一個の基本要素構造体へのアドレスを次々と保存している.
    struct list *data は 各構造体のつながりをして, リスト構造に構成させる.
  */

  head = createData(0, keywords[0]); // "\\0" をリストに入れる
  data = head;
  for(i = 1; i< 10; i++){
    new = createData(i, keywords[i]);
    data->next = new;
    data = new;
  }

  printf("[Address]\tn\tstr\tnext\n");

  for(data = head; data != NULL; data = data->next)
    printf("[%p]\t%d\t%s\t%p\n", data, data->n, data->str, data->next);
  /*
    リストを出力する
  */

  return 0;
}

struct list *createData(int num, char *word){
  struct list *data; // この構造体はローカル関数内だけ使える.

  data = (struct list *)malloc(sizeof(struct list));
  if(data == NULL)
    exit(1);
  data->n = num;
  memcpy(data->str, word, sizeof(data->str)-1);
  data->next = NULL; // すべての構造体をそれぞれ繋がっていないために, NULL で初期化する.
  
  return data;
}

list.c の実行結果は:

[Address]	n	str	next
[0x100100080]	0	\0	0x1001000a0
[0x1001000a0]	1	if	0x1001000c0
[0x1001000c0]	2	while	0x1001000e0
[0x1001000e0]	3	for	0x100100100
[0x100100100]	4	do	0x100100120
[0x100100120]	5	switch	0x100100140
[0x100100140]	6	case	0x100100160
[0x100100160]	7	break	0x100100180
[0x100100180]	8	int	0x1001001a0
[0x1001001a0]	9	char	0x0