g ++コンパイラによる参照の実装方法について説明します。このコードの
#include <iostream>
using namespace std;
int main()
{
int i = 10;
int *ptrToI = &i;
int &refToI = i;
cout << "i = " << i << "\n";
cout << "&i = " << &i << "\n";
cout << "ptrToI = " << ptrToI << "\n";
cout << "*ptrToI = " << *ptrToI << "\n";
cout << "&ptrToI = " << &ptrToI << "\n";
cout << "refToNum = " << refToI << "\n";
//cout << "*refToNum = " << *refToI << "\n";
cout << "&refToNum = " << &refToI << "\n";
return 0;
}
出力はこの
i = 10
&i = 0xbf9e52f8
ptrToI = 0xbf9e52f8
*ptrToI = 10
&ptrToI = 0xbf9e52f4
refToNum = 10
&refToNum = 0xbf9e52f8
のようです(私はこれのためにGDBを使用していました。ここ8,9及び10は、コードの行数)解体で
8 int i = 10;
0x08048698 <main()+18>: movl $0xa,-0x10(%ebp)
を見てみましょう
ここで$0xa
は、i
に割り当てる10(10進数)です。 -0x10(%ebp)
は、ebp register
-16(10進数)の内容を意味します。 -0x10(%ebp)
は、スタック上のi
のアドレスを指しています。
9 int *ptrToI = &i;
0x0804869f <main()+25>: lea -0x10(%ebp),%eax
0x080486a2 <main()+28>: mov %eax,-0x14(%ebp)
ptrToI
にi
のアドレスを割り当てます。 ptrToI
は、アドレス-0x14(%ebp)
にあるスタックに再びあります。つまり、ebp
- 20(10進数)です。
10 int &refToI = i;
0x080486a5 <main()+31>: lea -0x10(%ebp),%eax
0x080486a8 <main()+34>: mov %eax,-0xc(%ebp)
ここにキャッチがあります! 9行目と10行目の逆アセンブリを比較すると、-0x14(%ebp)
が行番号10の-0xc(%ebp)
に置き換えられます。-0xc(%ebp)
はrefToNum
のアドレスです。これはスタックに割り当てられます。しかし、アドレスを知る必要はないので、コードからこのアドレスを取得することはできません。
So;参照はメモリを占有します。この場合、スタック変数はローカル変数として割り当てられているため、スタックメモリです。 どのくらいのメモリを占有していますか? 多くのポインタが占有します。
ここで参照とポインタへのアクセス方法を見てみましょう。簡単にするために、アセンブリスニペットの一部のみを示しました
16 cout << "*ptrToI = " << *ptrToI << "\n";
0x08048746 <main()+192>: mov -0x14(%ebp),%eax
0x08048749 <main()+195>: mov (%eax),%ebx
19 cout << "refToNum = " << refToI << "\n";
0x080487b0 <main()+298>: mov -0xc(%ebp),%eax
0x080487b3 <main()+301>: mov (%eax),%ebx
ここで2つの線を比較すると、似ているでしょう。 -0xc(%ebp)
は実際にはrefToI
のアドレスにアクセスすることはできません。 単純な言い方をすれば、参照を通常のポインタと考えると、参照にアクセスすることは、参照が指すアドレスの値を取得することに似ています。どの私はあなたがここで何が起こっているかを発見することができます推測する2行のコード下のあなたにも同じ結果が得られます
cout << "Value if i = " << *ptrToI << "\n";
cout << " Value if i = " << refToI << "\n";
今、この
15 cout << "ptrToI = " << ptrToI << "\n";
0x08048713 <main()+141>: mov -0x14(%ebp),%ebx
21 cout << "&refToNum = " << &refToI << "\n";
0x080487fb <main()+373>: mov -0xc(%ebp),%eax
を比較する意味。 &refToI
を要求すると、-0xc(%ebp)
のアドレスの内容が返され、-0xc(%ebp)
はrefToi
となり、内容はi
のアドレスになります。
最後に、なぜこの行がコメントされていますか?
//cout << "*refToNum = " << *refToI << "\n";
*refToI
は許可されていないため、コンパイル時エラーが発生します。
なぜコンパイラは両方のコンテキストに依存することはできないと思いますか? – Slava
標準では、ストレージを必要とする場合でも参照を必要としないため、2つの理論のどちらかが正しくなる可能性があります。実際に知っている唯一の方法は、コンパイラのソースを開いてコンパイラを実装している人に質問することです。 – bstamour
@Slava:私はそれをできると思います。コンパイラーのコードが「より良い」(より一貫性があり、保守が容易など)一方、私は、一方のアプローチが他のアプローチに比べてどんな利点を持っているか、ある場合にはどんな利点があるか分かりません。 –