これが馬鹿げた質問ではないことを願っていますが、アセンブリのポインタに心を包み込もうとしています。
正確な違いは何ですか:
mov eax, ebx
そして
mov [eax], ebx
そしていつdword ptr [eax]
使用すべきです?
また、mov eax, [ebx]
コンパイルエラーが発生します。これはなぜですか?
既に述べたように、オペランドを括弧で囲むと、そのオペランドはCのポインターであるかのようにdereferencedになります。つまり、 、括弧は、値を読み取ることを意味しますfrom(または値を保存するinto)その値を直接読み取るのではなく、そのメモリの場所。
したがって、この:
mov eax, ebx
単にebx
の値をeax
にコピーします。擬似C表記では、これはeax = ebx
になります。
これに対して:
mov eax, [ebx]
ebx
のコンテンツを逆参照し、eax
にポイント先の値を保存します。擬似C表記では、これはeax = *ebx
になります。
最後に、これ:
mov [eax], ebx
ebx
の値をeax
が指すメモリ位置に保存します。繰り返しますが、疑似C表記では*eax = ebx
です。
ここのレジスタは、シンボリック変数名などのメモリオペランドで置き換えることもできます。したがって、この:
mov eax, [myVar]
変数myVar
のアドレスを逆参照し、その変数のcontentsをeax = myVar
のようなeax
に格納します。
対照的に、これ:
mov eax, myVar
eax = &myVar
のように、変数myVar
のaddressをeax
に保存します。
少なくとも、それがmostアセンブラーの仕組みです。 Microsoftのアセンブラー(MASMと呼ばれる)、およびMicrosoft C/C++コンパイラーのインラインアセンブリは、少し異なります。上記の2つの命令を同等のものとして扱い、基本的にメモリオペランドを囲む括弧を無視します。
MASMで変数のアドレスを取得するには、OFFSET
キーワードを使用します。
mov eax, OFFSET myVar
ただし、MASMにはこの寛容な構文があり、だらしなくすることができますが、そうすべきではありません。変数を間接参照してその実際の値を取得する場合は、常に括弧を含めます。適切な構文を使用して明示的にコードを記述した場合、間違った結果が得られることはありません。他の人が理解しやすくなります。さらに、MASMの「私が書いたものではなく、私が意味することをする」松葉杖に頼るのではなく、他のアセンブラーが書くと予想される方法でコードを書く習慣を身に付けるように強制します。
「私が書いたものではなく、私が意味することをする」松葉杖について言えば、MASMは一般に、変数のサイズを知っているので、オペランドサイズ指定子を省略することもできます。しかし、ここでも、明確さと一貫性のためにそれを書くことをお勧めします。したがって、myVar
がint
である場合、次のようにします。
mov eax, DWORD PTR [myVar] ; eax = myVar
または
mov DWORD PTR [myVar], eax ; myVar = eax
この表記は necessaryNASMのような他のアセンブラーでは は強く型付けされておらず、myVar
はDWORD
サイズのメモリ位置です。
レジスタの名前がそのサイズを示しているため、レジスタオペランドを間接参照する場合、これはまったく必要ありません。 al
とah
は常にBYTE
サイズ、ax
は常にWord
サイズ、eax
は常にDWORD
- sized、およびrax
は常にQWORD
- sizedです。ただし、メモリオペランドの表記方法との一貫性を保つために、必要に応じて含めることは問題ありません。
また、
mov eax, [ebx]
を実行しようとすると、コンパイルエラーが発生します。これはなぜですか?
ええと…あなたはすべきではありません。これは、MSVCのインラインアセンブリでうまく組み立てられます。すでに見たように、それは次と同等です:
mov eax, DWORD PTR [ebx]
ebx
が指すメモリ位置が逆参照され、DWORD
サイズの値がeax
にロードされることを意味します。
mov a, [eax]
ができない理由eaxが指している場所へのポインタを "a"にすべきではないのですか?
いいえ。このオペランドの組み合わせは許可されていません。 MOV
命令のドキュメント からわかるように、本質的に5つの可能性があります(代替のエンコーディングとセグメントを無視):
mov register, register ; copy one register to another
mov register, memory ; load value from memory into register
mov memory, register ; store value from register into memory
mov register, immediate ; move immediate value (constant) into register
mov memory, immediate ; store immediate value (constant) in memory
mov memory, memory
がないことに注意してください。これはあなたが試みていたものです。
ただし、単にコーディングすることで、a
がeax
が指しているものを指すようにすることができます。
mov DWORD PTR [a], eax
a
とeax
は同じ値になりました。 eax
がポインタだった場合、a
は同じメモリ位置へのポインタになります。
a
が指しているvalueにeax
を設定する場合は、次のようにする必要があります。
mov eax, DWORD PTR [eax] ; eax = *eax
mov DWORD PTR [a], eax ; a = eax
もちろん、これはポインターを破壊し、逆参照された値に置き換えます。ポインターを失いたくない場合は、2番目の「スクラッチ」レジスターを使用する必要があります。何かのようなもの:
mov edx, DWORD PTR [eax] ; edx = *eax
mov DWORD PTR [a], edx ; a = edx
これはすべてややこしいことです。 mov
命令は、x86 ISAで多数の潜在的な意味でオーバーロードされています。これは、CISCアーキテクチャとしてのx86のルーツによるものです。対照的に、最新のRISCアーキテクチャは、レジスタ間移動、メモリロード、およびメモリストアをより適切に分離します。 x86はそれらすべてを単一のmov
命令に詰め込みます。戻って今すぐ修正するには遅すぎます。構文に慣れる必要があり、場合によっては一目見ます。