第6章:厳密なエイリアシングと有効型規則

この章で学ぶこと

  • 厳密なエイリアシング規則の定義
  • 有効型の決定方法
  • 許可されるアクセス
  • 型パニングの正しい方法
  • コンパイラオプション

---

6.1 厳密なエイリアシング規則

規格上の定義

規格 6.5p7:

> An object shall have its stored value accessed only through an lvalue expression that has one of the following types: > - a type compatible with the effective type of the object, > - a qualified version of a type compatible with the effective type, > - a type that is the signed or unsigned type corresponding to the effective type, > - a type that is the signed or unsigned type corresponding to a qualified version of the effective type, > - an aggregate or union type that includes one of the aforementioned types among its members, or > - a character type.

違反例

int i = 42;
float *fp = (float *)&i;
float f = *fp;  /* 未定義動作:int を float でアクセス */

許可されるアクセス

int i = 42;

/* 互換型 */
int *ip = &i;
int x = *ip;  /* OK */

/* 符号の異なる対応型 */
unsigned int *uip = (unsigned int *)&i;
unsigned int y = *uip;  /* OK */

/* 文字型 */
unsigned char *cp = (unsigned char *)&i;
unsigned char c = *cp;  /* OK */

---

6.2 有効型の決定

宣言型を持つオブジェクト

int x;  /* 有効型: int */

動的確保されたオブジェクト

void *p = malloc(sizeof(int));
/* 有効型: なし */

*(int *)p = 42;
/* 有効型: int */

*(float *)p = 3.14f;
/* 有効型: float(上書き) */

memcpy による有効型のコピー

int x = 42;
float f;
memcpy(&f, &x, sizeof(f));  /* 有効型はコピー元(int)になる? */
/* 実際には char 経由なので安全 */

---

6.3 型パニング(Type Punning)

NG: ポインタキャスト

float f = 3.14f;
int i = *(int *)&f;  /* 未定義動作 */

OK: union を使用

union {
    float f;
    int i;
} u;

u.f = 3.14f;
int i = u.i;  /* OK(C99以降) */

OK: memcpy を使用

float f = 3.14f;
int i;
memcpy(&i, &f, sizeof(i));  /* OK */

---

6.4 コンパイラオプション

GCC/Clang

# 厳密なエイリアシングを有効(デフォルト、-O2以上)
gcc -fstrict-aliasing

# 厳密なエイリアシングを無効
gcc -fno-strict-aliasing

# 違反を警告
gcc -Wstrict-aliasing

無効化の是非

-fno-strict-aliasing は違反を「許可」するわけではなく、最適化を抑制するだけです。移植性のあるコードを書くべきです。

---

6.5 実践的なガイドライン

ルール1: 異なる型のポインタで同じメモリにアクセスしない

/* NG */
int *ip = ...;
float *fp = (float *)ip;
*fp = 3.14f;

/* OK */
union { int i; float f; } u;
u.f = 3.14f;

ルール2: バイト操作は unsigned char を使用

void *p = malloc(n);
unsigned char *bytes = (unsigned char *)p;
/* 各バイトへのアクセスはOK */

ルール3: 型パニングは union または memcpy

/* union */
union { float f; uint32_t u; } converter;
converter.f = value;
uint32_t bits = converter.u;

/* memcpy */
memcpy(&bits, &value, sizeof(bits));

---

6.6 この章のまとめ

許可されるアクセス

  • 互換型
  • 符号の異なる対応型
  • 文字型(char, unsigned char, signed char)
  • メンバに含む集成型
  • 型パニングの方法

  • union(C99以降)
  • memcpy

---

確認問題

問題1

次のコードは安全ですか?

int i = 42;
unsigned int u = *(unsigned int *)&i;

解答

安全。signed/unsigned の対応型でのアクセスは許可されている。

問題2

float のビットパターンを uint32_t で取得する安全な方法は?

解答

/* union */
union { float f; uint32_t u; } conv;
conv.f = value;
uint32_t bits = conv.u;

/* または memcpy */
uint32_t bits;
memcpy(&bits, &value, sizeof(bits));

---

次の章では、メモリモデルと順序付けについて学びます。