Neunomizuの日記

俺だけが俺だけじゃない

# 電気回路が分からなくても分かるC言語のUnionとStructの違い

tags: 情報

もう電気回路なんてしないなんて言わないよ絶対(なんて言わないよ絶対)

1

電気回路←うわぁ

前回インターンしようと思った会社のCTOが電気系でうわぁ...と思ったら案の定落ちていました.

もう電気のことなんか考えたくもないのでC言語の話をします.

UnionとStruct

なんかこいつら(unionstruct)似てないか?と思う人もたくさんいると思います.

ちなみに去年の今頃は僕もそう思っていませんでした.というか自分が授業をやるならアルゴリズムとかよりもこういう似ている気がするけど実際は違うものの違いを聞くと思います.

まぁそんなことはさておき,こいつらの違いはアドレスを出力してしまえば簡単に分かります.

下のようなコードを書きます.

#include <stdio.h>
#include <stdint.h>

typedef union union_t {
  int8_t i8;
  int16_t i16;
  int32_t i32;
  int64_t i64;
} myUnion;

typedef struct struct_t {
  int8_t i8;
  int16_t i16;
  int32_t i32;
  int64_t i64;
} myStruct;

int main (void) {
  myUnion u;
  myStruct s;

  printf("Address\n");
  printf("\n");
  printf("Union's i8 = %p, i16 = %p, i32 = %p, i64 = %p\n", &(u.i8), &(u.i16), &(u.i32), &(u.i64));
  printf("Struct's i8 = %p, i16 = %p, i32 = %p, i64 = %p\n", &(s.i8), &(s.i16), &(s.i32), &(s.i64));
  printf("-------------\n");
  printf("Union & Struct's Size\n");
  printf("\n");
  printf("myUnion's size = %zu\n", sizeof(myUnion));
  printf("myStruct's size = %zu\n", sizeof(myStruct));
  printf("-------------\n");
  printf("Sizes of types\n");
  printf("\n");
  printf("i8 = %zu, i16 = %zu, i32 = %zu, i64 = %zu\n", sizeof(int8_t), sizeof(int16_t),sizeof(int32_t), sizeof(int64_t));
  return 0;  
}

こいつをgccコンパイルして実行してやると以下のような出力を得ます.

Address

Union's i8 = 0x7fffbb250d48, i16 = 0x7fffbb250d48, i32 = 0x7fffbb250d48, i64 = 0x7fffbb250d48
Struct's i8 = 0x7fffbb250d50, i16 = 0x7fffbb250d52, i32 = 0x7fffbb250d54, i64 = 0x7fffbb250d58
-------------
Union & Struct's Size

myUnion's size = 8
myStruct's size = 16
-------------
Sizes of types

i8 = 1, i16 = 2, i32 = 4, i64 = 8

出力解説

Addressの出力から分かるようにunionはアドレスが全て同じで,structはアドレスが全て違います.

次にUnion & Struct's Sizeを見てみます.ここからは(当然?)使っているメモリの大きさが異なります.

最後に各型の大きさが違うことが分かります.

結論から言ってしまうと,unionはメンバーの型のうち最大のもののメモリ領域を確保して,メンバーのいずれかに値を格納します.

一方,structはメンバー変数のメモリ領域をそれぞれ確保して,それぞれに別々の値を格納します

図示すると下のようになります.

Padding

ただ,今回全てのtypeの大きさを足すと1 + 2 + 4 + 8 = 15bytesです.しかし,myStructの大きさは16 bytesです.

C言語では処理系(gccclangなど)によってパッディング(アラインメント)2と言って構造体内の奇数の大きさの型の大きさを偶数にすることがあります.

ここではint8_tが1 byteなのでパッディングされて2 bytesになっているのでしょう.

まとめ

  • unionは複数の型を選んで使えるようにメモリを共有している
  • structは複数の型を同時に格納できるようにそれぞれにメモリを格納している
  • パッディング(アラインメント)によってメモリの和がそのままstructのメモリの大きさになるわけではない
  • 電気回路を二度と勉強したくない