第3章:オブジェクト、値、表現

この章で学ぶこと

  • オブジェクトの定義と性質
  • ストレージ期間と寿命
  • 有効型(Effective Type)
  • 値の表現とオブジェクト表現
  • パディングとアラインメント
  • トラップ表現
  • ---

    3.1 オブジェクトとは

    規格上の定義

    規格 3.15:

    > object: region of data storage in the execution environment, the contents of which can represent values

    オブジェクトは「メモリ領域」です。オブジェクト指向の「オブジェクト」とは異なる概念です。

    オブジェクトの性質

    すべてのオブジェクトは以下を持ちます:

  • ストレージ期間(storage duration)
  • 有効型(effective type)
  • アラインメント(alignment)
  • (value)

---

3.2 ストレージ期間

4種類のストレージ期間

静的ストレージ期間(static): プログラム全体で存続。ファイルスコープ変数、static 変数。

static int count;  /* 静的ストレージ期間 */
int global;        /* 静的ストレージ期間 */

自動ストレージ期間(automatic): ブロックに入ってから出るまで。ローカル変数。

void f(void) {
    int local;  /* 自動ストレージ期間 */
}

動的ストレージ期間(allocated): malloc で確保、free で解放されるまで。

int *p = malloc(sizeof(int));  /* 動的ストレージ期間 */
free(p);

スレッドストレージ期間(thread)(C11): スレッドの開始から終了まで。_Thread_local

_Thread_local int per_thread;  /* スレッドストレージ期間 */

---

3.3 寿命(Lifetime)

定義

寿命は、オブジェクトのストレージが予約されている期間です。

寿命外アクセス

寿命が終了したオブジェクトへのアクセスは未定義動作です。

int *dangling(void) {
    int local = 42;
    return &local;  /* local の寿命は関数終了で終わる */
}

int main(void) {
    int *p = dangling();
    *p = 10;  /* 未定義動作:寿命外アクセス */
}

一時的オブジェクト

構造体を値で返す場合など、一時的オブジェクトが生成されます。

struct S { int x; };
struct S f(void) { return (struct S){42}; }

int main(void) {
    int x = f().x;  /* 一時オブジェクトは式の評価終了まで存続 */
}

---

3.4 有効型(Effective Type)

定義

規格 6.5p6:

> The effective type of an object for an access to its stored value is the declared type of the object, if any.

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

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

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

宣言型がないオブジェクト(malloc で確保)の有効型は、最初の書き込みで決定されます。

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

有効型とエイリアシング

有効型と異なる型でアクセスすると、通常は未定義動作です(厳密なエイリアシング規則)。

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

---

3.5 値の表現とオブジェクト表現

定義

オブジェクト表現: オブジェクトを構成するすべてのビット

値の表現: 値を決定するビット(パディングを除く)

規格 6.2.6.1:

> The representations of all types are unspecified except as stated in this subclause.

整数の表現

符号付き整数の表現は3種類が許容されます(C23では2の補数のみ):

  • 2の補数: 最も一般的
  • 1の補数: 歴史的
  • 符号と絶対値: 歴史的
  • /* 8ビット2の補数での -1 */
    /* 11111111 */
    
    /* 8ビット1の補数での -1 */
    /* 11111110 */
    
    /* 8ビット符号絶対値での -1 */
    /* 10000001 */
    

    パディングビット

    整数型にはパディングビット(値に寄与しないビット)が存在する可能性があります。

    /* CHAR_BIT * sizeof(int) - 実際の値ビット = パディングビット */
    

    ---

    3.6 アラインメント

    定義

    アラインメントは、オブジェクトを配置できるアドレスの制約です。

    _Alignof(int);    /* int のアラインメント要件(通常4) */
    _Alignof(double); /* double のアラインメント要件(通常8) */
    

    構造体のパディング

    struct S {
        char c;     /* 1バイト */
        /* 3バイトのパディング(アラインメント調整) */
        int i;      /* 4バイト */
    };
    
    _Static_assert(sizeof(struct S) == 8, "");
    

    アラインメント指定(C11)

    _Alignas(16) double data[4];  /* 16バイト境界に配置 */
    

    未整列アクセス

    アラインメント要件を満たさないアクセスは未定義動作です。

    char buf[8];
    int *p = (int *)(buf + 1);  /* 未整列の可能性 */
    *p = 42;  /* 未定義動作の可能性 */
    

    ---

    3.7 トラップ表現

    定義

    トラップ表現は、オブジェクト表現のうち、値を表さないもの。読み取ると未定義動作。

    規格 6.2.6.1p5:

    > Certain object representations need not represent a value of the object type. If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined.

    実例

  • 符号付き整数の「負のゼロ」(1の補数)
  • 浮動小数点のシグナリングNaN
  • ポインタの無効表現
  • 安全な型

    unsigned char_Bool 以外の型にはトラップ表現が存在しうります。

    /* unsigned char はトラップ表現を持たない */
    unsigned char *p = (unsigned char *)&obj;
    /* どのバイトも安全に読める */
    

    ---

    3.8 ポインタの有効性

    有効なポインタ

  • オブジェクトを指す
  • オブジェクトの「1つ後ろ」を指す(アクセス不可だが比較可能)
  • NULL
  • 無効なポインタ

  • 寿命が終了したオブジェクトを指す
  • free されたメモリを指す
  • 不正な演算の結果
  • int *p = malloc(sizeof(int));
    free(p);
    /* p は無効だが、使用しなければ未定義動作ではない */
    int *q = p;  /* 未定義動作:無効ポインタの使用 */
    

    ---

    3.9 この章のまとめ

    学んだこと

  • オブジェクト: メモリ領域
  • ストレージ期間: 静的/自動/動的/スレッド
  • 有効型: アクセスに使用される型
  • 値の表現: 値を決定するビット
  • アラインメント: アドレス制約
  • トラップ表現: 読み取りが未定義動作

次の章の予告

次章では、未定義動作の完全カタログを学びます。

---

確認問題

問題1

4種類のストレージ期間を挙げてください。

解答

  • 静的(static)
  • 自動(automatic)
  • 動的(allocated)
  • スレッド(thread)

問題2

有効型とは何ですか?

解答

オブジェクトにアクセスする際に使用される型。宣言型を持つオブジェクトではその宣言型、動的確保されたオブジェクトでは最初の書き込みで決定される型。

問題3

unsigned char が特別な型である理由は?

解答

  • トラップ表現を持たない
  • どのオブジェクトの表現も安全に読み取れる
  • エイリアシング規則の例外(任意のオブジェクトに unsigned char * でアクセス可能)

---

次の章では、未定義動作について詳しく学びます。