2014-01-15 9 views
6

私は、次の学期に取り組んでいるコンパイラクラスの準備として、C++コンパイラがリファレンスとポインタをどのように扱うのかを学びたいと思っています。私は特に、コンパイラがC++でリファレンスを扱う方法に興味があります。コンパイラレベルのC++リファレンスとポインタ

標準では参照が「エイリアス」であると指定されていますが、コンパイラレベルでその意味が分かりません。私は2つの理論を持っています:

  1. 非参照変数はシンボルテーブルにエントリを持っています。その変数への参照が作成されると、コンパイラはシンボルテーブル内の全く同じエントリ(メモリ内の非参照変数の場所ではない)を「指す」別の語彙素を作成するだけです。

  2. その変数への参照が作成されると、コンパイラはその変数の場所へのポインタをメモリに作成します。言語の文脈を解析するときには、参照の制限(null値なしなど)が処理されます。換言すれば、参照は逆参照されたポインタのための「構文的砂糖」である。

私の知る限り、どちらの解決策でも「別名」が作成されます。コンパイラはどちらか一方を使用しますか?それともコンパイラ依存ですか?

機械語レベルでは両方とも "ポインタ"(整数以外のほとんどすべてがマシンレベルの "ポインタ"です)であることを認識しています。私はマシンコードが生成される前にコンパイラが行うことに興味があります。

編集:私は好奇心が強い理由の一部はPHP uses method #1なので、C++コンパイラが同じように動作するかどうかは疑問です。 Javaは確かにメソッド#1を使用せず、その "参照"は実際に逆参照されたポインタです。 Scott Stanchfieldのthis articleを参照してください。

+0

なぜコンパイラは両方のコンテキストに依存することはできないと思いますか? – Slava

+0

標準では、ストレージを必要とする場合でも参照を必要としないため、2つの理論のどちらかが正しくなる可能性があります。実際に知っている唯一の方法は、コンパイラのソースを開いてコンパイラを実装している人に質問することです。 – bstamour

+0

@Slava:私はそれをできると思います。コンパイラーのコードが「より良い」(より一貫性があり、保守が容易など)一方、私は、一方のアプローチが他のアプローチに比べてどんな利点を持っているか、ある場合にはどんな利点があるか分かりません。 –

答えて

0

ポインタと参照の理解は、コードの実装とはかなり異なります。

私はあなたがそれらを正しく使い、コンパイラ理論の核心に焦点を当てる方法を学ぶことをお勧めします。基本的なコンパイラ理論クラスは、ポインタ、参照、および継承の概念なしでは十分に難しい。ポインタと参照は、より高度なクラスのために残されています。

簡単に言えば:必要なときにポインタを使用してポインタを使用します。

編集1:
コンパイラがいる限り彼らの構文と意味は言語仕様に従って動作として彼らが望む任意の方法で参照とポインタを実装することができます。

単純な実装は、参照を追加の属性を持つポインタとして扱うことです。

メモリ内にはすべて、場所、つまりアドレスがあります。コンパイラは、メモリからレジスタにロードし、レジスタの内容をメモリに格納するために内部ポインタを使用する必要があります。したがって、ポインタ、参照、またはエイリアスのいずれであっても、メモリ内の変数を参照するには、コンパイラは変数のアドレスを必要とします。 (これには、別々に扱われるレジスタ変数は含まれません)。したがって、参照やエイリアスのポインタを使用すると、いくらかのコーディングが保存されます。

4

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) 

ptrToIiのアドレスを割り当てます。 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は許可されていないため、コンパイル時エラーが発生します。