これは選択された回答を持つ古い質問であることは知っていますが、アライメントされたメモリアクセスとアライメントされていないメモリアクセスの違いに対する答えを誰も説明していません...
ドラムでも、スラムでも、フラッシュでも、その他でも構いません。簡単な例として sram を取り上げます。これはビットから構築されます。特定の sram は、固定数のビット幅と固定数の行の深さから構築されます。幅が 32 ビットで、深さが数行または数行あるとしましょう。
この sram のアドレス 0x0000 に 32 ビットの書き込みを行うと、この sram 周辺のメモリ コントローラーは行 0 に対して 1 回の書き込みサイクルを実行できます。
この sram のアドレス 0x0001 に 32 ビットの書き込みを行う場合、それが許可されていると仮定すると、コントローラーは行 0 の読み取りを行い、3 つのバイトを変更して 1 つを保持し、それを行 0 に書き込み、次に行を読み取る必要があります。 1 1 バイトを変更し、残りの 3 バイトは見つかったままにし、それを書き戻します。どのバイトが変更されるか、またはシステムのエンディアンとは関係ありません。
前者はアライメントされており、後者はアライメントされていません。明らかにパフォーマンスの違いに加えて、4 つのメモリ サイクルを実行し、バイト レーンをマージできるように追加のロジックが必要です。
アドレス 0x0000 から 32 ビットを読み取る場合、行 0 の 1 回の読み取りで完了です。しかし、0x0001 から読み取ると、row0 と row1 の 2 つの読み取りを行う必要があり、システム設計によっては、これらの 64 ビットをプロセッサに送り返すだけで、バス クロックは 1 つではなく 2 つになる可能性があります。または、メモリ コントローラーに余分なロジックがあり、1 つのバス サイクルで 32 ビットがデータ バス上で整列されます。
16ビットの読み取りは少し優れています。0x0000、0x0001、および0x0002からの読み取りはrow0からの読み取りにすぎず、システム/プロセッサの設計に基づいてこれらの32ビットを送り返すことができ、プロセッサはそれらを抽出するか、メモリコントローラーでシフトします。プロセッサが回転する必要がないように、特定のバイトレーンに着陸します。両方ではないにしても、どちらか一方が必要です。0x0003 からの読み取りは上記のようなものですが、行 0 と行 1 を読み取る必要があるのは、それぞれにバイトが 1 つずつあるためです。その後、プロセッサが抽出するために 64 ビットを送り返すか、メモリ コントローラがビットを 1 つの 32 ビット バス応答に結合します (これらの例では、プロセッサとメモリ コントローラ間のバスが 32 ビット幅であると想定しています)。
この例の sram では、16 ビットの書き込みは常に少なくとも 1 つの読み取り-変更-書き込みで終了します。アドレス 0x0003 を読み取り、2 つの行をそれぞれ 1 バイトずつ変更して書き戻します。
8ビットでは、そのバイトを含む1行を読み取るだけで済みますが、書き込みは1行の読み取り-変更-書き込みです。
トラップを無効にすることはできますが、armv4 は unaligned を好みませんでした。結果は上記のようにはなりませんが、重要ではありません。現在の arm は unaligned を許可し、上記の動作を提供します。制御レジスタで少し変更すると、unaligned で中止されます。転送します。mips は以前は許可していませんでしたが、今は何をしているのかわかりません。x86、68K などは許可されており、メモリ コントローラーが最も多くの作業を行う必要があった可能性があります。
明らかにそれを許可しない設計は、プログラマーの負担であると言う人もいれば、プログラマーにとって余分な作業がない、またはプログラマーにとってより簡単であると言う人もいるかもしれませんが、パフォーマンスとロジックの削減のためです。アライメントされているかどうかに関係なく、8ビット変数を作成してメモリを節約しようとせず、32ビットワードまたはレジスタまたはバスの自然なサイズを焼き付けたほうがよい理由もわかります。数バイトの小さなコストでパフォーマンスが向上する場合があります。言うまでもなく、32ビットレジスタが8ビット変数、マスキング、および場合によっては符号拡張を模倣するようにするために、コンパイラが追加する必要がある余分なコードは言うまでもありません。レジスタのネイティブ サイズを使用する場合、これらの追加の命令は必要ありません。
コンパイラが常にターゲットに合わせてデータを正しく配置することに同意しません。それを破る方法があります。また、ターゲットが unaligned をサポートしていない場合は、障害が発生します。プログラマーがこれについて話す必要はありません。コンパイラーが、あなたが思いつく可能性のある合法的なコードに基づいて常に正しいことを行っていれば、パフォーマンスのためでない限り、この質問をする理由はありません。void ptr アドレスをアラインするかどうかを制御しない場合は、常に mem2() アラインされていないアクセスを使用するか、ptr の値に基づいてコードで if-then-else を nik として実行する必要があります。指摘した。void として宣言することにより、C コンパイラはアラインメントを正しく処理する方法がなくなり、保証されなくなります。char *prt を取得してこれらの関数に渡すと、 mem2() 関数内またはこれら 2 つの関数の外側にコードを追加しなくても、コンパイラが正しく処理できるようになります。あなたの質問に書かれているように、 mem2() が唯一の正解です。
デスクトップ/ラップトップで使用される DRAM は、64 または 72 (ecc を使用) ビット幅になる傾向があり、それらへのすべてのアクセスが整列されます。メモリ スティックは、実際には 8 ビット幅または 16 または 32 ビット幅のチップで構成されていますが。(これは、さまざまな理由で電話/タブレットで変更される可能性があります) メモリ コントローラーと、理想的には少なくとも 1 つのキャッシュがこのドラムの前に配置され、バス幅よりも小さいアライメントされていない、またはアライメントされたアクセスが処理されます。はるかに高速なキャッシュ sram を使用し、dram アクセスはすべて全バス幅アクセスにアラインされます。ドラムの前にキャッシュがなく、コントローラーが全幅アクセス用に設計されている場合、それは最悪のパフォーマンスです。バイトレーンを個別にライトアップするように設計されている場合(8ビット幅のチップを想定)、読み取り-変更-書き込みはありませんが、より複雑なコントローラーがあります。典型的なユース ケースがキャッシュを使用する場合 (設計にキャッシュがある場合)、各バイト レーンに対してコントローラーで追加の作業を行うのは意味がないかもしれませんが、フル バス幅サイズの転送を行う方法を知っているだけです。または倍数。