web-dev-qa-db-ja.com

データ整列とは何ですか? Cでポインタを型キャストするときになぜ、いつ心配する必要があるのでしょうか。

整列システムがどのように機能するのか、なぜ一部のタイプが他のタイプよりも厳密に整列されているのかを説明する適切なドキュメントが見つかりませんでした。

23
Dogus Ural

簡単に説明しようと思います。

データ整列とは何ですか?

コンピュータのアーキテクチャは、プロセッサとメモリで構成されています。メモリはセルに編成されているため、次のようになります。

 0x00 |   data  |  
 0x01 |   ...   |
 0x02 |   ...   |

各メモリセルには、指定したサイズ、格納できるビット数があります。これはアーキテクチャに依存します。

C/C++プログラムで変数を定義すると、1つのまたはそれ以上の異なるセルがプログラムによって占有されます。

例えば

int variable = 12;

各セルに32ビットが含まれていて、intタイプサイズが32ビットであるとします。

variable: | 0 0 0 c |  // c is hexadecimal of 12.

CPUがその変数を操作する必要がある場合、その変数をレジスター内に取り込む必要があります。 CPUは "1クロックをメモリから少量のビットを取り込むことができ、そのサイズは通常Wordと呼ばれます。この次元もアーキテクチャに依存しています。

ここで、オフセットが原因で、2つのセルに格納されている変数があるとします。

たとえば、2つの異なるデータを保存する必要があります( "文字列表現を使用して、より明確にするために"を使用します)。

data1: "ab"
data2: "cdef"

したがって、メモリはそのように構成されます(2つの異なるセル)。

|a b c d|     |e f 0 0|

つまり、data1はセルの半分しか占有しないため、data2は残りの部分と2番目のセルの一部を占有します。

ここで、CPUがdata2を読み取りたいと仮定します。 1つのクロック内で最初のセルを読み取り、もう1つのクロック内で2番目のセルの残りの部分を読み取るため、CPUはデータにアクセスするために2クロックを必要とします。

このメモリの例に従ってaligndata2とすると、paddingのようなものを導入し、2番目のセルでdata2をすべてシフトできます。

|a b 0 0|     |c d e f|
     ---
   padding

このようにして、CPUはdata2にアクセスするために "1クロック"だけを失います。

アラインシステムの機能

Alignシステムは、データをシステムのメモリに揃えるためにpaddingを導入するだけです。アーキテクチャに従って覚えてください。データがメモリに配置されている場合、データにアクセスするためにCPUサイクルを無駄にすることはありません。

これは、パフォーマンス上の理由で行われます(99%の場合)。

28
Biagio Festa

これは「実装定義」です。つまり、配置要件は言語仕様の一部ではありません。

CPUが異なれば、アライメントの要件も異なります。不均一なアドレスで16ビット値をアドレスできなかった人もいました。サイズで割り切れるアドレスに揃えない限り、浮動小数点値をアドレスできない人もいました。等々。正しくアライメントされていないデータオブジェクトへのアクセスが遅くなるものもあれば、アライメントされていないアクセスをトリップするものもあります。

そのため、言語標準は、どの型をどのように配置する必要があるか(詳細がわからなかったため)の詳細には入りませんが、 "実装"(この場合はコンパイラバックエンド)に任せています。

ポインターをタイプキャストする場合、コードが、アドレス指定できないアドレスで特定のオブジェクトをアドレス指定するように強制している可能性があります。 「古い」タイプの配置要件が少なくともが「新しい」タイプの要件と同じように厳格であることを確認する必要があります。

C++(C++ 11以降)では、特定の型の整列要件を伝える alignof 演算子を取得します。 alignas 演算子も取得して、特定のタイプまたはオブジェクトに対してより厳密な配置を適用します。

C(C11以上)では、 _ Alignof および _ Alignas 演算子を取得しますが、_<stdalign.h>_はalignof/alignas便利なマクロ にラップします。 (ありがとう、Lundin-C11は私の得意分野ではありません。)

15
DevSolar

一部のシステムは、たとえば32ビットワード(4バイト)の一部でメモリにアクセスできます。これはハードウェアの制限です。これは、メモリコントローラーに入る実際のアドレスが4で割り切れる必要があることを意味します(バイトをアドレス指定しているため)。したがって、4で割り切れないアドレスにWordを配置しようとすると、2つのオプションがあります。コンパイラは、2つのメモリアクセスからWordを構成するために、いくつかの豪華なコードを生成しようとしますが、常にそうとは限りません。 。時々それは単に与えられたアドレスから4バイトにアクセスするためのコードを生成します。そしてprocessorはデータ整列エラーで失敗します。

これは、言語が課している制限につながります。

コードを検討してください(悪いコード):

uint8_t a[] = {1,2,3,4,5,6};
uint32_t b = *(uint32_t*)&a[1];

そして、aが4つの境界で割り切れるように配置されていると仮定します。次に、2行目は、そのsecond要素のアドレス(つまり、アドレスnot 4で割り切れる)からWordを読み取ろうとしています。アライメントエラーになります。しかし、Cではstrict aliasing ruleによって単に禁止されています。

9
Eugene Sh.