名前空間
変種
操作

オブジェクトとアライメント

From cppreference.com
< c‎ | language

Cプログラムは、オブジェクトを作成、破棄、アクセス、および操作します。

Cにおけるオブジェクトとは、実行環境におけるデータストレージの領域であり、その内容はを表すことができます(値とは、特定のを持つものとして解釈されたときの、オブジェクトの内容の意味です)。

すべてのオブジェクトは、

  • サイズ(sizeofで決定可能)
  • アライメント要件_Alignof(C23まで)alignof(C23以降)(C11以降)
  • ストレージ期間(自動、静的、割り当て済み、スレッドローカル)
  • 生存期間(ストレージ期間または一時的なものと同等)
  • 有効な型(下記参照)
  • 値(不定である可能性あり)
  • オプションで、このオブジェクトを指す識別子

オブジェクトは、宣言割り当て関数文字列リテラル複合リテラル、および構造体または共用体の配列メンバーを返す非lvalue式によって作成されます。

目次

[編集] オブジェクト表現

ビットフィールドを除き、オブジェクトは1つ以上のバイトの連続したシーケンスで構成され、各バイトはCHAR_BITビットで構成され、サイズがnのオブジェクトの型unsigned char[n]memcpyでコピーできます。結果の配列の内容は、オブジェクト表現として知られています。

2つのオブジェクトが同じオブジェクト表現を持つ場合、それらは等しく比較されます(浮動小数点NaNの場合は例外)。逆は真ではありません。等しく比較される2つのオブジェクトは異なるオブジェクト表現を持つ場合があります。なぜなら、オブジェクト表現のすべてのビットが値に関与する必要はないからです。そのようなビットは、アライメント要件を満たすためのパディング、パリティチェック、トラップ表現の表示などに使用される場合があります。

オブジェクト表現がオブジェクト型の任意の値も表さない場合、それはトラップ表現として知られています。文字型のlvalue式を介して読み取る以外の方法でトラップ表現にアクセスすることは、未定義の動作です。構造体または共用体の値は、特定のメンバーがトラップ表現であっても、決してトラップ表現ではありません。

型がcharsigned char、およびunsigned charのオブジェクトの場合、オブジェクト表現のすべてのビットは値表現に関与することが要求され、各可能なビットパターンは(パディング、トラップビット、または複数の表現なしで)異なる値を表します。

複数のバイトを占める整数型shortintlonglong long)のオブジェクトの場合、それらのバイトの使用は実装定義ですが、2つの主要な実装はビッグエンディアン(POWER、Sparc、Itanium)とリトルエンディアン(x86、x86_64)です。ビッグエンディアンプラットフォームは、整数が占めるストレージ領域の最も低いアドレスに最上位バイトを格納し、リトルエンディアンプラットフォームは最も低いアドレスに最下位バイトを格納します。詳細はEndiannessを参照してください。以下の例も参照してください。

ほとんどの実装ではトラップ表現、パディングビット、または整数型の複数の表現を許可しませんが、例外もあります。たとえば、Itanium上の整数型の値はトラップ表現である可能性があります

[編集] 有効な型

すべてのオブジェクトは有効な型を持ち、これはどのlvalueアクセスが有効で、どれが厳密なエイリアシング規則に違反するかを決定します。

オブジェクトが宣言によって作成された場合、そのオブジェクトの宣言された型がオブジェクトの有効な型となります。

オブジェクトが割り当て関数reallocを含む)によって作成された場合、それは宣言された型を持ちません。そのようなオブジェクトは次のように有効な型を取得します。

  • そのオブジェクトへの最初の書き込みが、文字型以外の型を持つlvalueを介して行われた場合、そのlvalueの型はその書き込みおよびそれ以降のすべての読み込みにおいて、このオブジェクトの有効な型になります。
  • memcpyまたはmemmoveが別のオブジェクトをそのオブジェクトにコピーした場合、または別のオブジェクトを文字型の配列としてそのオブジェクトにコピーした場合、ソースオブジェクトの有効な型(もしあれば)はその書き込みおよびそれ以降のすべての読み込みにおいて、このオブジェクトの有効な型になります。
  • 宣言された型を持たないオブジェクトへのその他のアクセスの場合、有効な型はアクセスに使用されたlvalueの型です。

[編集] 厳密なエイリアシング

有効な型T1を持つオブジェクトに対して、異なる型T2のlvalue式(通常はポインタの逆参照)を使用することは、未定義の動作です。ただし、以下の場合を除きます。

  • T2とT1が互換性のある型である。
  • T2が、T1と互換性のある型のcvr修飾バージョンである。
  • T2が、T1と互換性のある型の符号付きまたは符号なしバージョンである。
  • T2が、上記型のいずれかをメンバーとして含む集約型または共用体型であり(再帰的に、サブ集約型または含まれる共用体のメンバーを含む)、
  • T2が文字型(charsigned char、またはunsigned char)である。
int i = 7;
char* pc = (char*)(&i);
 
if (pc[0] == '\x7') // aliasing through char is OK
    puts("This system is little-endian");
else
    puts("This system is big-endian");
 
float* pf = (float*)(&i);
float d = *pf; // UB: float lvalue *p cannot be used to access int

これらの規則は、関数が2つのポインタを受け取る場合、コンパイラが一方をもう一方を通じて書き込んだ後に再読み込みするコードを生成する必要があるかどうかを制御します。

// int* and double* cannot alias
void f1(int* pi, double* pd, double d)
{
    // the read from *pi can be done only once, before the loop
    for (int i = 0; i < *pi; i++)
        *pd++ = d;
}
struct S { int a, b; };
 
// int* and struct S* may alias because S is an aggregate type with a member of type int
void f2(int* pi, struct S* ps, struct S s)
{
    // read from *pi must take place after every write through *ps
    for (int i = 0; i < *pi; i++)
        *ps++ = s;
}

restrict修飾子は、上記の規則が許容する場合でも、2つのポインタがエイリアスしないことを示すために使用できることに注意してください。

共用体の非アクティブなメンバーを介して型パンニングも実行できることに注意してください。

[編集] アライメント

すべての完全なオブジェクト型は、アライメント要件と呼ばれるプロパティを持ち、これはsize_t型の整数値であり、この型のオブジェクトを割り当てることができる連続するアドレス間のバイト数を示します。有効なアライメント値は、非負の2のべき乗です。

型のアライメント要件は、_Alignof(C23まで)alignof(C23以降)で照会できます。

(C11 以降)

構造体のすべてのメンバーのアライメント要件を満たすために、一部のメンバーの後にパディングが挿入される場合があります。

#include <stdalign.h>
#include <stdio.h>
 
// objects of struct S can be allocated at any address
// because both S.a and S.b can be allocated at any address
struct S
{
    char a; // size: 1, alignment: 1
    char b; // size: 1, alignment: 1
}; // size: 2, alignment: 1
 
// objects of struct X must be allocated at 4-byte boundaries
// because X.n must be allocated at 4-byte boundaries
// because int's alignment requirement is (usually) 4
struct X
{
    int n;  // size: 4, alignment: 4
    char c; // size: 1, alignment: 1
    // three bytes padding
}; // size: 8, alignment: 4
 
int main(void)
{
    printf("sizeof(struct S) = %zu\n", sizeof(struct S));
    printf("alignof(struct S) = %zu\n", alignof(struct S));
    printf("sizeof(struct X) = %zu\n", sizeof(struct X));
    printf("alignof(struct X) = %zu\n", alignof(struct X));
}

実行結果の例

sizeof(struct S) = 2
alignof(struct S) = 1
sizeof(struct X) = 8
alignof(struct X) = 4

各オブジェクト型は、その型のすべてのオブジェクトにアライメント要件を課します。最も弱い(小さい)アライメントは、型charsigned char、およびunsigned charのアライメントであり、1に等しくなります。最も厳密な(大きい)基本アライメントは実装定義でありmax_align_tのアライメントに等しい(C11以降)

基本アライメントは、すべての種類のストレージ期間のオブジェクトでサポートされます。

オブジェクトのアライメントが、_Alignof(C23まで)alignof(C23以降)を使用してmax_align_tより厳密(大きい)にされた場合、それは拡張アライメント要件を持ちます。拡張アライメントのメンバーを持つ構造体または共用体型は、オーバーアライメント型です。オーバーアライメント型がサポートされているかどうかは実装定義であり、それらのサポートは各ストレージ期間の種類で異なる場合があります。

構造体または共用体型Sがオーバーアライメント型または拡張アライメントを指定するアライメント指定子で宣言されたメンバーを持たない場合、Sは基本アライメントを持ちます。

すべての算術型またはポインタ型のアトミックバージョンは、基本アライメントを持ちます。

(C11 以降)

[編集] 欠陥報告

以下の動作変更を伴う欠陥報告が、以前に発行されたC規格に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
DR 445 C11 型が_Alignasが関与せずに拡張アライメントを持つ可能性がある 基本アライメントを持つ必要がある

[編集] 参考文献

  • C17標準 (ISO/IEC 9899:2018)
  • 3.15 object (p: 5)
  • 6.2.6 Representations of types (p: 33-35)
  • 6.2.8 Alignment of objects (p: 36-37)
  • 6.5/6-7 Expressions (p: 55-56)
  • C11標準 (ISO/IEC 9899:2011)
  • 3.15 object (p: 6)
  • 6.2.6 Representations of types (p: 44-46)
  • 6.2.8 Alignment of objects (p: 48-49)
  • 6.5/6-7 Expressions (p: 77)
  • C99標準 (ISO/IEC 9899:1999)
  • 3.2 alignment (p: 3)
  • 3.14 object (p: 5)
  • 6.2.6 Representations of types (p: 37-39)
  • 6.5/6-7 Expressions (p: 67-68)
  • C89/C90標準 (ISO/IEC 9899:1990)
  • 1.6 Definitions of terms

[編集] 関連項目

C++ドキュメントObjectについて)
English 日本語 中文(简体) 中文(繁體)