第3章:オブジェクト、値、表現
この章で学ぶこと
- オブジェクトの定義と性質
- ストレージ期間と寿命
- 有効型(Effective Type)
- 値の表現とオブジェクト表現
- パディングとアラインメント
- トラップ表現
- ストレージ期間(storage duration)
- 有効型(effective type)
- アラインメント(alignment)
- 値(value)
---
3.1 オブジェクトとは
規格上の定義
規格 3.15:
> object: region of data storage in the execution environment, the contents of which can represent values
オブジェクトは「メモリ領域」です。オブジェクト指向の「オブジェクト」とは異なる概念です。
オブジェクトの性質
すべてのオブジェクトは以下を持ちます:
---
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.
実例
安全な型
unsigned char と _Bool 以外の型にはトラップ表現が存在しうります。
/* unsigned char はトラップ表現を持たない */
unsigned char *p = (unsigned char *)&obj;
/* どのバイトも安全に読める */
---
3.8 ポインタの有効性
有効なポインタ
無効なポインタ
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 *でアクセス可能)
---
次の章では、未定義動作について詳しく学びます。