17

以下にリストした仮定に違反するアーキテクチャを知りたいです。また、すべてのアーキテクチャで間違っている仮定があるかどうか (つまり、完全に間違っている仮定があるかどうか) を知りたいです。

  1. sizeof(int *) == sizeof(char *) == sizeof(void *) == sizeof(func_ptr *)

  2. 特定のアーキテクチャーのすべてのポインターのメモリー内表現は、指すデータ型に関係なく同じです。

  3. ポインターのメモリ内表現は、アーキテクチャと同じビット長の整数と同じです。

  4. ポインター データ型の乗算と除算は、コンパイラによってのみ禁止されています。注: はい、これが無意味であることはわかっています。つまり、この誤った使用法を禁止するハードウェア サポートはありますか?

  5. すべてのポインター値は、単一の整数にキャストできます。言い換えれば、セグメントとオフセットをまだ利用しているアーキテクチャは何ですか?

  6. ポインタをインクリメントすることはsizeof(the pointed data type)、ポインタによって格納されたメモリ アドレスに加算することと同じです。が の場合、pは の4 バイト後のメモリ アドレスに等しくなります。int32*p+1p

私は、連続した仮想メモリ空​​間で使用されるポインターに最も慣れています。その使用法については、通常、それらを数直線上のアドレスと考えることで得ることができます。スタック オーバーフローの質問ポインターの比較を参照してください。

4

11 に答える 11

10

これらすべての具体例を示すことはできませんが、最善を尽くします。

sizeof(int *) == sizeof(char *) == sizeof(void *) == sizeof(func_ptr *)

これが誤りであることがわかっているシステムは知りませんが、次のことを考慮してください。

多くの場合、モバイル デバイスには、プログラム コードなどを保存するための読み取り専用メモリがある程度あります。読み取り専用の値 (const 変数) は、読み取り専用メモリに格納される場合があります。また、ROM アドレス空間は通常の RAM アドレス空間よりも小さい場合があるため、ポインターのサイズも異なる場合があります。同様に、関数へのポインタは、プログラムがロードされるこの読み取り専用メモリを指している可能性があり、それ以外の場合は変更できないため、サイズが異なる場合があります (そのため、データをそこに保存することはできません)。

したがって、上記が成り立たないことを観察したプラットフォームは知りませんが、そうなる可能性のあるシステムは想像できます。

特定のアーキテクチャのすべてのポインターのメモリ内表現は、指しているデータ型に関係なく同じです。

メンバー ポインターと通常のポインターを考えてみてください。それらは同じ表現 (またはサイズ) を持っていません。メンバ ポインタは、thisポインタとオフセットで構成されます。

上記のように、一部の CPU は定数データを別のポインター形式を使用する別のメモリ領域にロードすることが考えられます。

ポインターのメモリ内表現は、アーキテクチャと同じビット長の整数と同じです。

そのビット長がどのように定義されているかによって異なります。:)int多くの 64 ビット プラットフォームでは、まだ 32 ビットです。しかし、ポインターは 64 ビットです。すでに述べたように、セグメント化されたメモリ モデルを備えた CPU には、数値のペアで構成されるポインターがあります。同様に、メンバー ポインターは、数値のペアで構成されます。

ポインター データ型の乗算と除算は、コンパイラによってのみ禁止されています。

最終的に、ポインタのデータ型はコンパイラにのみ存在します。CPU が扱うのはポインタではなく、整数とメモリ アドレスです。したがって、ポインター型に対するこれらの操作を禁止できる場所は他にありません。C++ 文字列オブジェクトの連結を禁止するように CPU に要求することもできます。C++ 文字列型は C++ 言語にのみ存在し、生成されたマシン コードには存在しないため、これを行うことはできません。

ただし、あなたの言いたいことに答えるには、Motorola 68000 CPU を調べてください。整数とメモリアドレス用に別々のレジスタがあると思います。つまり、そのような無意味な操作を簡単に禁止できるということです。

すべてのポインター値は、単一の整数にキャストできます。

あなたはそこで安全です。C および C++ 標準では、メモリ空間のレイアウト、CPU アーキテクチャなどに関係なく、これが常に可能であることが保証されています。具体的には、実装定義のマッピングを保証します。つまり、いつでもポインターを整数に変換し、その整数を元のポインターに戻すことができます。しかし、C/C++ 言語は、中間整数値がどうあるべきかについて何も言いません。それは、個々のコンパイラとそれが対象とするハードウェア次第です。

ポインターをインクリメントすることは、ポインターによって格納されたメモリ アドレスに sizeof (ポイントされたデータ型) を追加することと同じです。

繰り返しますが、これは保証されています。概念的に考えると、ポインターはアドレスを指しているのではなく、オブジェクトを指している場合、これは完全に理にかなっています。ポインタに 1 を追加すると、明らかに次のオブジェクトを指すようになります。オブジェクトの長さが 20 バイトの場合、ポインタをインクリメントすると 20 バイト移動し、次のオブジェクトに移動します。

ポインタが線形アドレス空間の単なるメモリ アドレスである場合、それが基本的に整数である場合、インクリメントするとアドレスに 1 が追加されます。つまり、次のバイトに移動します。

最後に、あなたの質問へのコメントで述べたように、C++ は単なる言語であることに注意してください。どのアーキテクチャにコンパイルされているかは気にしません。これらの制限の多くは、最近の CPU ではわかりにくいかもしれません。しかし、往年の CPU をターゲットにしている場合はどうでしょうか。次の 10 年の CPU をターゲットにしている場合はどうでしょうか。それらがどのように機能するかさえわからないため、それらについて多くを推測することはできません. 仮想マシンをターゲットにしている場合はどうなりますか? Flash 用のバイトコードを生成するコンパイラは既に存在し、Web サイトからすぐに実行できます。C++ を Python のソース コードにコンパイルするにはどうすればよいでしょうか。

標準で指定されたルール内に留まることで、これらすべてのケースでコードが機能することが保証されます。

于 2009-08-30T17:35:17.753 に答える
8

私は具体的な実例を念頭に置いていませんが、「権限」はC標準です。標準で何かが要求されていない場合は、他の仮定に意図的に準拠しない適合実装を構築できます。これらの仮定のいくつかは、プロセッサが直接フェッチできるメモリアドレスを表す整数としてポインタを実装するのが便利であるという理由だけで、ほとんどの場合当てはまりますが、これは「便利さ」の結果であり、次のように保持することはできません。普遍的な真実。

  1. 標準では必要ありません(この質問を参照してください)。たとえば、sizeof(int*)はと等しくない場合がありますsize(double*)void*任意のポインタ値を格納できることが保証されています。
  2. 標準では必要ありません。定義上、サイズは表現の一部です。サイズが異なる可能性がある場合は、表現も異なる可能性があります。
  3. 必ずしも。実際、「アーキテクチャのビット長」はあいまいな表現です。本当に64ビットプロセッサとは何ですか?アドレスバスですか?レジスタのサイズは?データバス?何?
  4. ポインタを「乗算」または「除算」することは意味がありません。コンパイラーによって禁止されていますが、もちろん、基底表現を乗算または除算することができ(これは私にはあまり意味がありません)、その結果、未定義の動作が発生します。
  5. たぶん私はあなたの主張を理解していませんが、デジタルコンピュータのすべてはある種の2進数です。
  6. はい; すこし。sizeof(pointer_type)より遠い場所を指すことが保証されています。これは、必ずしも数値の算術加算と同等ではありません(つまり、ここではさらに論理的な概念です。実際の表現はアーキテクチャ固有です)
于 2009-08-29T22:08:16.957 に答える
7

6の場合:ポインタは必ずしもメモリアドレスではありません。たとえば、StackOverflowユーザーjalfによる「TheGreatPointerConspiracy 」を参照してください。

はい、上記のコメントで「住所」という言葉を使用しました。これが何を意味するのかを理解することが重要です。私は「データが物理的に保存されるメモリアドレス」を意味するのではなく、単に「値を見つけるために必要なものは何でも」という意味です。iのアドレスは何でもかまいませんが、一度取得すると、いつでもiを見つけて変更できます。」

と:

ポインタはメモリアドレスではありません!これについては上で述べましたが、もう一度言いましょう。ポインタは通常、コンパイラによって単にメモリアドレスとして実装されますが、そうである必要はありません。」

于 2009-08-29T22:18:17.450 に答える
6

C99標準からのポインターに関するいくつかの詳細情報:

  • 6.2.5§27は、void*char*が同一の表現を持つことを保証します。つまり、変換せずに交換可能に使用できます。つまり、同じアドレスが同じビットパターンで示されます(他のポインタタイプに当てはまる必要はありません)。
  • 6.3.2.3§1は、不完全な型またはオブジェクト型へのポインタは、キャストして(およびから)void*、また元に戻すことができ、それでも有効であると述べています。これには関数ポインタは含まれていません!
  • 6.3.2.3§6は整数に(および整数から)キャストできる状態でvoid*あり、7.18.1.4§1は適切な型intptr_tを提供しuintptr_tます。問題:これらの型はオプションです-標準では、ポインタの値を実際に保持するのに十分な大きさの整数型は必要ないと明示的に述べられています!
于 2009-08-29T22:46:37.580 に答える
3

sizeof(char*) != sizeof(void(*)(void)?-36ビットアドレッシングモードのx86ではありません(Pentium1以降のほぼすべてのIntelCPUでサポートされています)

「ポインタのメモリ内表現は、同じビット長の整数と同じです」-最新のアーキテクチャにはメモリ内表現はありません。タグ付きメモリは、Cが標準化される前に流行したことはなく、すでに廃止されていました。実際、メモリは整数を保持せず、ビットとほぼ間違いなくワードを保持します(バイトではなく、ほとんどの物理メモリでは8ビットのみを読み取ることはできません)。

「ポインタの乗算は不可能です」-68000ファミリ。アドレスレジスタ(ポインタを保持するレジスタ)は、そのIIRCをサポートしていませんでした。

「すべてのポインタを整数にキャストできます」-PICではありません。

「T*をインクリメントすることは、sizeof(T)をメモリアドレスに追加することと同じです」-定義上、trueです。と同等&pointer[1]です。

于 2009-09-01T10:25:39.093 に答える
2

一般的に、すべての質問に対する答えは「はい」です。これは、人気のある言語を実装するマシンだけが日の目を見て、今世紀まで続いたためです。言語標準はこれらの「不変条件」またはアサーションを変更する権利を留保しますが、普遍的に真実であるためにいくつかの言い換えを必要とする項目3および4を除いて、実際の製品では発生していません。

セグメント化されたMMU設計を構築することは確かに可能です。これは、過去数年間に学術的に普及した機能ベースのアーキテクチャにほぼ対応しますが、そのようなシステムは通常、そのような機能を有効にして一般的に使用されていません。このようなシステムは、おそらく大きなポインタを持っていたため、アサーションと競合している可能性があります。

多くの場合大きなポインターを持つセグメント化/機能MMUに加えて、より極端な設計では、データ型をポインターにエンコードしようとしました。これらのうち、これまでに構築されたものはほとんどありません。(この質問は、基本的な単語指向のポインター・イズ・ア・ワード・アーキテクチャーのすべての代替案を提示します。)

具体的には:

  1. 特定のアーキテクチャのすべてのポインタのメモリ内表現は、ポイントされているデータ型に関係なく同じです。強い型の言語ではなくハードウェアで保護を実装しようとした非常に奇抜な過去の設計を除いて、真実です。
  2. ポインタのメモリ内表現は、アーキテクチャと同じビット長の整数と同じです。たぶん、確かにある種の積分型は同じです 。LP64とLLP64を参照してください。
  3. ポインタデータ型の乗算と除算は、コンパイラによってのみ禁止されています。
  4. すべてのポインタ値を単一の整数にキャストできます。言い換えれば、どのアーキテクチャがまだセグメントとオフセットを利用していますか?今日、セグメントとオフセットを使用するものはありませんが、Cintは多くの場合十分な大きさではないため、ポインターを保持するためにlongまたはが必要になる場合があります。long long
  5. ポインタをインクリメントすることは、ポインタによって格納されているメモリアドレスにsizeof(ポイントされたデータ型)を追加することと同じです。pがint32*の場合、p+1はpの4バイト後のメモリアドレスに等しくなります。はい。

すべてのIntelアーキテクチャCPU、つまりすべてのPeeCeeには、壮大で伝説的な複雑さの精巧なセグメンテーションユニットが含まれていることに注意してください。ただし、事実上無効になっています。PC OSが起動するたびに、セグメントベースを0に設定し、セグメント長を〜0に設定して、セグメントを無効にし、フラットメモリモデルを提供します。

于 2009-08-29T23:07:28.960 に答える
2

ポインタのメモリ内表現は、アーキテクチャと同じビット長の整数と同じです。

たとえば、80186では32ビットポインタが2つのレジスタ(オフセットレジスタとセグメントレジスタ)に保持され、アクセス中にどのレジスタにどのハーフワードが入ったかが重要であるため、この仮定は誤りだと思います。

ポインタデータ型の乗算と除算は、コンパイラによってのみ禁止されています。

タイプを乗算または除算することはできません。; P

ポインタを乗算または除算する理由がわかりません。

すべてのポインタ値を単一の整数にキャストできます。言い換えれば、どのアーキテクチャがまだセグメントとオフセットを利用していますか?

intptr_tC99標準では、整数型であるにポインタを格納できます。あ、はい。

ポインタをインクリメントすることは、ポインタによって格納されているメモリアドレスにsizeof(ポイントされたデータ型)を追加することと同じです。pがint32*の場合、p+1はpの4バイト後のメモリアドレスに等しくなります。

x + yここで、xはaT *でありy、は整数であり(T *)((intptr_t)x + y * sizeof(T))、私が知る限りでは同等です。配置が問題になる場合がありますが、にパディングが提供される場合がありますsizeof。よくわかりません。

于 2009-08-29T22:11:53.173 に答える
2

他のことはわかりませんが、DOSの場合、#3の仮定は正しくありません。DOSは16ビットであり、さまざまなトリックを使用して、16ビットを超えるメモリをマップします。

于 2009-08-29T22:16:56.797 に答える
2

1950 年代、1960 年代、1970 年代には、多くの「ワード アドレス」アーキテクチャがありました。しかし、C コンパイラを使用した主流の例を思い出せません。1980 年代のICL / Three Rivers PERQ マシンは、ワード アドレスで、書き込み可能なコントロール ストア (マイクロコード) を備えていました。そのインスタンス化の 1 つには C コンパイラとPNXと呼ばれる Unix のフレーバーがありましたが、C コンパイラには特別なマイクロコードが必要でした。

基本的な問題は、ワード アドレス マシンの char* 型が扱いにくいことです。ただし、どのように実装しても構いません。あなたはしばしばsizeof(int *) != sizeof(char *)...

興味深いことに、C よりも前に、基本的なポインター型がワード アドレスであるBCPLという言語がありました。つまり、ポインターをインクリメントすると、次の単語のアドレスがptr!1得られ、単語 at が得られますptr + 1。バイトをアドレス指定するための別の演算子がありましたptr%42

于 2009-09-01T10:00:29.817 に答える
1

以下にリストした仮定に違反するアーキテクチャを知りたいです。

Stephen C が PERQ マシンについて言及し、MSalters が 68000 と PIC について言及したようです。

ある種の不当な仮定に適合しない標準準拠の C コンパイラを備えた奇妙で素晴らしいアーキテクチャの名前を挙げて、他の誰も実際に質問に答えなかったことに失望しています。

sizeof(int *) == sizeof(char *) == sizeof(void *) == sizeof(func_ptr *) ?

必ずしも。いくつかの例:

ハーバード アーキテクチャの 8 ビット プロセッサ (PIC、8051、および M8C) 用のほとんどのコンパイラは、sizeof(int *) == sizeof(char *) を作成しますが、sizeof(func_ptr *) とは異なります。

これらのファミリの非常に小さなチップの中には、RAM が 256 バイト (またはそれ以下) のものもありますが、PROGMEM (フラッシュまたは ROM) が数キロバイトあるため、コンパイラは sizeof(int *) == sizeof(char *) を 1 に等しくします (a単一の 8 ビット バイト) ですが、sizeof(func_ptr *) は 2 (2 つの 8 ビット バイト) です。

数キロバイトの RAM と 128 キロバイトほどの PROGMEM を備えたこれらのファミリの大型チップの多くのコンパイラは、sizeof(int *) == sizeof(char *) を 2 (2 つの 8 ビット バイト) に等しくしますが、sizeof( func_ptr *) 3 (3 つの 8 ビット バイト) に等しい。

いくつかのハーバード アーキテクチャ チップは、完全な 2^16 ("64KByte") の PROGMEM (フラッシュまたは ROM) と、別の 2^16 ("64KByte") の RAM + メモリ マップド I/O を正確に格納できます。このようなチップのコンパイラは sizeof(func_ptr *) を常に 2 (2 バイト) にします。しかし、多くの場合、他の種類のポインター sizeof(int *) == sizeof(char *) == sizeof(void *) を「long ptr」の 3 バイト汎用ポインターにする方法があります。そのポインタがRAMまたはPROGMEMを指しているかどうかを示す追加のマジックビットがあります。(これは、多くの異なるサブルーチンからその関数を呼び出すときに、「print_text_to_the_LCD()」関数に渡す必要がある種類のポインターであり、RAM のどこにでもある可能性のあるバッファー内の変数文字列のアドレスを使用する場合もあれば、1 つの変数を使用する場合もあります)。 PROGMEM のどこにでもある可能性がある多くの定数文字列)。このようなコンパイラには、多くの場合、プログラマーが同じプログラム内で 3 種類の char ポインター (場所を示すのに 2 バイトしか必要としない定数文字列) を指定できるようにするための特別なキーワード ("short" または "near"、"long" または "far") があります。 PROGMEM では、それらは RAM 内のどこにあるかを示すために 2 バイトしか必要としない非定数文字列に配置されます。

1950 年代と 1960 年代に製造されたほとんどのコンピューターは、18 ビット (またはそれ以下) のアドレス バスで、 36 ビット ワード長または18 ビット ワード長を使用します。そのようなコンピューター用の C コンパイラーは、すべての整数と関数をワード境界で揃える必要があるため、sizeof(int *) == sizeof(func_ptr *) = 2 で9 ビットの bytesを使用することが多いと聞きました。ただし、sizeof(char *) == sizeof(void *) == 4 は、そのようなポインターを完全な 36 ビット ワードに格納する特別な PDP-10 命令を利用するためのものです。その完全な 36 ビット ワードには 18 ビット ワード アドレスが含まれ、その他の 18 ビットにはさらにいくつかのビットが含まれており、(とりわけ) そのワード内のポイント先の文字のビット位置を示します。

特定のアーキテクチャのすべてのポインターのメモリ内表現は、指すデータ型に関係なく同じですか?

必ずしも。いくつかの例:

上記のどのアーキテクチャでも、ポインタのサイズはさまざまです。では、どのようにして「同じ」表現を持つことができるのでしょうか?

一部のシステムの一部のコンパイラは、「記述子」を使用して、文字ポインターやその他の種類のポインターを実装します。このような記述子は、" " の最初の "char" を指すポインターと " " の最初の "char" を指すポインターとは異なります。これらは間違いなく異なるデータ型です。大きな配列によって以前に占有されていたメモリ内のまったく同じ場所に。記述子を使用すると、そのようなマシンは、他のマシンでこのような問題を引き起こすバッファ オーバーフローをキャッチしてトラップできます。char big_array[4000]char small_array[10]

SAFElite および同様の「ソフト プロセッサ」で使用される「低脂肪ポインタ」には、ポインタが指すバッファのサイズに関する同様の「追加情報」があります。Low-Fat ポインターには、バッファー オーバーフローをキャッチしてトラップするという同じ利点があります。

ポインターのメモリ内表現は、アーキテクチャと同じビット長の整数と同じですか?

必ずしも。いくつかの例:

タグ付きアーキテクチャ」マシンでは、メモリの各ワードには、そのワードが整数なのかポインタなのか、それ以外なのかを示すいくつかのビットがあります。このようなマシンでは、タグ ビットを見ると、その単語が整数かポインタかがわかります。

Nova ミニコンピューターには、「間接スレッド コード」に影響を与えた各単語に「間接ビット」があると聞いています。整数を格納するとそのビットがクリアされ、ポインタを格納するとそのビットが設定されるようです。

ポインター データ型の乗算と除算は、コンパイラによってのみ禁止されています。注: はい、これが無意味であることはわかっています。つまり、この誤った使用法を禁止するハードウェア サポートはありますか?

はい、一部のハードウェアはそのような操作を直接サポートしていません。

他の人がすでに述べたように、68000 と 6809 の「乗算」命令は、(一部の)「データレジスタ」でのみ機能します。「アドレスレジスタ」の値に直接適用することはできません。(コンパイラがこのような制限を回避するのは非常に簡単です。つまり、これらの値をアドレス レジスタから適切なデータ レジスタに MOV してから、MUL を使用します)。

すべてのポインター値を単一のデータ型にキャストできますか?

はい。

memcpy() が正しく機能するために、C 標準では、すべての種類のすべてのポインター値を void ポインター ("void *") にキャストできることが義務付けられています。

セグメントとオフセットをまだ使用しているアーキテクチャであっても、これを機能させるにはコンパイラが必要です。

すべてのポインター値を単一の整数にキャストできますか? 言い換えれば、セグメントとオフセットをまだ利用しているアーキテクチャは何ですか?

わからない。

すべてのポインター値は、" " で定義された "size_t" および "ptrdiff_t" 整数データ型にキャストできると思われます<stddef.h>

ポインターをインクリメントすることは、ポインターによって格納されたメモリ アドレスに sizeof (ポイントされたデータ型) を追加することと同じです。p が int32* の場合、p+1 は p の 4 バイト後のメモリ アドレスに等しくなります。

あなたがここで何を求めているのかは不明です。

Q: ある種の構造体またはプリミティブ データ型 (" #include <stdint.h> ... int32_t example_array[1000]; ..." など) の配列があり、その配列を指すポインターをインクリメントするとします (たとえば、"int32_t p = &example_array[99]; .. . p++; ...")、ポインターはその配列の次の連続するメンバーを指していますか?これは、メモリ内で sizeof(ポイントされたデータ型) バイトです?

A: はい、コンパイラは、標準に準拠するために、ポインターを 1 回インクリメントした後、配列内の次の独立した連続する int32_t を指すようにする必要があります。

Q: では、p が int32* の場合、p+1 は p の 4 バイト後のメモリ アドレスに等しいのですか?

A: sizeof( int32_t ) が実際に 4 に等しい場合、はい。それ以外の場合、sizeof( int32_t ) が 2 または 1 に等しい最新の DSP を含む特定のワード アドレス指定可能なマシンなどの場合、p+1 は p の後のメモリ アドレス 2 または 1 "C バイト" に等しくなります。

Q: では、ポインタを取得して「int」にキャストすると...

A: 「全世界は VAX の異端」の 1 つのタイプです。

Q: ...そして、その「int」をポインタにキャストして戻します...

A: 別のタイプの「全世界が VAX の異端」です。

Q: int32_t へのポインターであるポインター p を取得し、ポインターを格納するのに十分な大きさのsizeof( int32_t )整数型にキャストし、その整数型に追加し、後でその整数型をキャストして戻すとします。ポインタに - 私がそれをすべて行うと、結果のポインタはp + 1に等しくなりますか?

必ずしも。

多くの DSP と他のいくつかの最新のチップは、8 ビット チップで使用されるバイト指向の処理ではなく、ワード指向のアドレッシングを備えています。

そのようなチップ用の C コンパイラの中には、各単語に 2 文字を詰め込むものもありますが、int32_t を保持するにはそのような単語が 2 つ必要なので、4 であると報告されています ( 24 ビットsizeof( int32_t )用の C コンパイラがあるという噂を聞いたことがあります)。これを行う Motorola 56000)。

コンパイラは、int32_t へのポインターを使用して "p++" を実行すると、ポインターが次の int32_t 値にインクリメントされるように調整する必要があります。コンパイラがそれを行う方法はいくつかあります。

標準に準拠した方法の 1 つは、int32_t への各ポインターを「ネイティブ ワード アドレス」として格納することです。単一のint32_t値を保持するのに 2 ワードかかるため、C コンパイラは " int32_t * p; ... p++" をコンパイルして、そのポインタ値を 2 だけインクリメントするアセンブリ言語にします。int32_t * p; ... int x = (int)p; x += sizeof( int32_t ); p = (int32_t *)x;ポインター値を 4 ずつインクリメントするアセンブリ言語にコンパイルします。

私は、連続した仮想メモリ空​​間で使用されるポインターに最も慣れています。

いくつかの PIC および 8086 およびその他のシステムには、連続していない RAM があります。これは、「ハードウェアをより単純にする」アドレスに RAM のいくつかのブロックがあります。これらのブロック間のアドレス空間のギャップに、メモリ マップド I/O が接続されていないか、何も接続されていません。

それは思ったよりもさらに厄介です。

場合によっては、読み取り-変更-書き込みによって引き起こされる問題を回避するためにビット バンディング ハードウェアを使用する場合など、2 つ以上の異なるアドレスを使用して、RAM 内のまったく同じビットを読み書きすることができます。

于 2015-01-21T03:08:31.230 に答える