10

要約:コンパイラーは、コンパイル中にC ++クラスのサイズを静的にどのように決定しますか?

詳細

クラスが使用するメモリの量を決定するためのルールと、メモリがどのように調整されるかを理解しようとしています。

たとえば、次のコードは4つのクラスを宣言しています。最初の2つはそれぞれ16バイトです。ただし、3は最初の2と同じデータメンバーを含んでいますが、48バイトです。4番目のクラスは3番目と同じデータメンバーを持っていますが、順序が異なりますが、32バイトです。

#include <xmmintrin.h>
#include <stdio.h>

class TestClass1 {
  __m128i vect;
};

class TestClass2 {
  char buf[8];
  char buf2[8];
};

class TestClass3 {
  char buf[8];
  __m128i vect;
  char buf2[8];
};

class TestClass4 {
  char buf[8];
  char buf2[8];
  __m128i vect;
};


TestClass1 *ptr1;
TestClass2 *ptr2;
TestClass3 *ptr3;
TestClass4 *ptr4;
int main() {
  ptr1 = new TestClass1();
  ptr2 = new TestClass2();
  ptr3 = new TestClass3();
  ptr4 = new TestClass4();
  printf("sizeof TestClass1 is: %lu\t TestClass2 is: %lu\t TestClass3 is: %lu\t TestClass4 is: %lu\n", sizeof(*ptr1), sizeof(*ptr2), sizeof(*ptr3), sizeof(*ptr4));
  return 0;
}

その答えは、クラスのデータメンバーの配置と関係があることを私は知っています。しかし、データメンバーを持つクラスがあるため、これらのルールが何であり、コンパイルステップ中にどのように適用されるかを正確に理解しようとしています__m128iが、データメンバーは16バイトに整列されていないため、コンパイラーがセグメンテーション違反になりますmovapsデータへのアクセスに使用するコードを生成します。

4

4 に答える 4

14

POD(プレーンな古いデータ)の場合、ルールは通常次のとおりです。

  • 構造内の各メンバーには、いくつかのサイズsといくつかの配置要件aがあります。
  • コンパイラーは、サイズSをゼロに設定し、アライメント要件Aを1(バイト)に設定して開始します。
  • コンパイラーは、構造体の各メンバーを次の順序で処理します。
  1. メンバーの<em>アライメント要件を検討してくださいSが現在の倍数でない場合は、の倍数になるように十分なバイト数Sを追加ます。これにより、メンバーがどこに行くかが決まります。構造体の先頭からオフセットSに移動します( Sの現在の値の場合)。
  2. AをAaの最小公倍数1に設定します。
  3. Sにsを追加して、メンバー用のスペースを確保します。
  • 各メンバーに対して上記のプロセスを実行する場合は、構造のアライメント要件Aを考慮してください。<em> Sが現在Aの倍数でない場合は、A倍数になるようにSに十分な数を追加します。

構造体のサイズは、上記を行ったときのSの値です。

さらに:

  • いずれかのメンバーが配列の場合、そのサイズは要素の数に各要素のサイズを掛けたものであり、その配置要件は要素の配置要件です。
  • いずれかのメンバーが構造である場合、そのサイズと配置要件は上記のように計算されます。
  • いずれかのメンバーがユニオンである場合、そのサイズは、最大メンバーのサイズに、すべてのメンバーの最小公倍数1の倍数にするのに十分なサイズを加えたものになります。

あなたのことを考えてくださいTestClass3

  • Sは0から始まり、Aは1から始まります。
  • char buf[8]8バイトとアライメント1が必要なため、Sは8から8に増加し、Aは1のままです。
  • __m128i vect16バイトとアライメント16が必要です。最初に、正しいアライメントを実現するには、Sを16に増やす必要があります。次に、Aを16に増やす必要があります。次に、スペースを作るためにSを16増やす必要があるためvect S32になります。
  • char buf2[8]8バイトとアライメント1が必要なため、Sは8から24に増加し、Aは16のままです。
  • 最後に、Sは24であり、 A(16)の倍数ではないため、 Sを8から32に増やす必要があります。

したがって、のサイズTestClass3は32バイトです。

基本タイプ(int、、doubleなど)の場合、配置要件は実装によって定義され、通常は主にハードウェアによって決定されます。多くのプロセッサでは、データが特定のアラインメントを持っている場合(通常、メモリ内のアドレスがサイズの倍数である場合)、データのロードと保存が高速になります。これを超えて、上記のルールは主にロジックに準拠しています。必要以上のスペースを使用せずに、位置合わせの要件を満たす必要がある場所に各メンバーを配置します。

脚注

1これは、一般的なケースとして、最小公倍数のアライメント要件を使用していると言いました。ただし、アライメント要件は常に2の累乗であるため、アライメント要件のセットの最小公倍数が最大になります。

于 2013-01-24T21:14:35.090 に答える
8

クラスのサイズをどのように決定するかは、完全にコンパイラー次第です。コンパイラは通常、プラットフォームに依存する特定のアプリケーションバイナリインターフェイスに一致するようにコンパイルされます。

ただし、観察した動作はかなり典型的なものです。コンパイラーは、メンバーがそれぞれのサイズの倍数で始まるようにメンバーを整列させようとしています。の場合TestClass3、メンバーの1つはタイプ__m128isizeof(__m128i) == 16です。したがって、16の倍数であるバイトで開始するようにそのメンバーを整列させようとします。最初のメンバーはタイプでchar[8]あるため、8バイトを使用します。コンパイラーが_m128iオブジェクトをこの最初のメンバーの直後に配置する場合、16の倍数ではない位置8から開始します。

0               8               16              24              32              48
┌───────────────┬───────────────────────────────┬───────────────┬┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
│    char[8]    │            __m128i            │    char[8]    │           
└───────────────┴───────────────────────────────┴───────────────┴┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄

したがって、代わりにこれを行うことを好みます:

0               8               16              24              32              48
┌───────────────┬┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┬───────────────────────────────┬───────────────┐┄┄┄
│    char[8]    │               │           __m128i             │    char[8]    │
└───────────────┴┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┴───────────────────────────────┴───────────────┘┄┄┄

これにより、48バイトのサイズになります。

TestClass4レイアウトを取得するためにメンバーを並べ替えると、次のようになります。

0               8               16              24              32              48
┌───────────────┬───────────────┬───────────────────────────────┬┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
│    char[8]    │    char[8]    │           __m128i             │        
└───────────────┴───────────────┴───────────────────────────────┴┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄

これで、すべてが正しく整列されます。配列は1(要素のサイズ)の倍数のオフセットにあり、__m128iオブジェクトは16の倍数のオフセットにあります。合計サイズは32バイトです。

コンパイラがこの再配置自体を実行しない理由は、標準では、クラスの後のメンバーがより高いアドレスを持つ必要があると指定しているためです。

同じアクセス制御(条項11)を持つ(非ユニオン)クラスの非静的データメンバーは、後のメンバーがクラスオブジェクト内でより高いアドレスを持つように割り当てられます。

于 2013-01-24T21:23:12.590 に答える
0

ルールは、使用中のアプリケーションバイナリインターフェイス仕様によって明確に設定されています。これにより、このインターフェイスを共有するプログラムのさまざまなシステム間の互換性が保証されます。

GCCの場合、これはItaniumABIです。

(残念ながら、ミラーは見つかりましたが、公開されなくなりました。)

于 2013-01-24T21:21:27.147 に答える
-1

アラインメントを確実にしたい場合は、hファイルで「pragmapack(1)」を使用する必要があります。次の投稿を参照してください:http: //tedlogan.com/techblog2.html

于 2013-01-24T21:15:50.837 に答える