2016-07-16 10 views
21

コンパイラは、プログラムが実行される前に平方根がメモリ内のどこにあるかをどのように知っていますか?私は、アドレスがプログラムが実行される異なる毎回だろうと思ったが、これは動作します:それはアドレスがメモリ内の別のアドレスからの相対であるため、なぜ機能ポインタは `constexpr`ですか?

constexpr double(*fp)(double) = &sqrt; 
cout << fp(5.0); 

ですか? fpの値が大きいため、私はそうは思わない:0x720E1B94。

+0

このジョブはコンパイラではありません。リンカーは物事がどこにあるのか把握します –

+0

実際の住所ではありません – Arunmu

+0

@EdHealそれはまだ実行前ですが、なぜその値がすべての実行で同じになるのかわかりません! – Coolwater

答えて

5

コンパイラは、プログラムが実行される前に平方根がメモリ内のどこにあるかをどのように知っていますか?

ツールチェーンは、機能の配置場所を決定します。

このアドレスはメモリ内の別のアドレスとの相対値であるためですか?

生成されたプログラムがrelocatableまたはposition independentの場合は、そうです。プログラムがどちらのプログラムでもない場合、アドレスは絶対アドレスにすることもできます。

次回プログラムが実行されたときに、まったく同じメモリスポットが使用可能になるのはなぜですか?

メモリスペースがvirtualであるためです。

+2

スペースは仮想ですが、今日でも[ASLR](https://en.wikipedia.org/wiki/Address_space_layout_randomization)もよく使用されています。 – Ruslan

22

コンパイル時に、コンパイラはsqrtのアドレスを認識しません。しかし、コンパイル時には、ポインタのアドレスにアクセスするためのconstexpr関数ポインタを使って何もすることはできません。したがって、コンパイル時の関数ポインタは不透明な値として扱うことができます。

constexpr変数が初期化された後で変数を変更することはできないため、すべてのconstexpr関数ポインタを特定の関数の場所にまで煮詰めることができます。

あなたはこのようなものでした場合:コンパイラは正確に検出することができます

using fptr = float(*)(float); 

constexpr fptr get_func(int x) 
{ 
    return x == 3 ? &sqrtf : &sinf; 
} 

constexpr fptr ptr = get_func(12); 

get_funcは、特定のコンパイル時の値を返す関数。したがってget_func(12)&sinfに減少します。したがって、&sinfがコンパイルするものは、正確にはget_func(12)にコンパイルされます。

+0

* can * or * must *? – Deduplicator

+1

@Deduplicator:両方。 –

+0

これは、コンパイラが '&sinf'の値をどのように知っているかを説明していません。 @ hydeの答えだけでそのことが説明されています。 – fishinear

13

アドレス値はリンカーによって割り当てられているため、コンパイラは正確なアドレス値を認識しません。

cout << fp(5.0); 

これは、正確なアドレスが解決された後に実行時に評価されるためです。

通常、コンパイル時にはわからないため、constexprポインタの実際の値(アドレス)は使用できません。

言及ビャーネ・ストロヴストルップのC++プログラミング言語第4版:

10.4.5アドレス定数式

のような静的に割り当てられたオブジェクト(§6.4.2)のアドレス、グローバル変数は定数です。ただし、その値はコンパイラではなくリンカによって割り当てられるため、コンパイラはそのようなアドレス定数の値を知ることができません。これは、ポインタと参照型の定数式の範囲を制限します。たとえば:

constexpr const char∗ p1 = "asdf"; 
    constexpr const char∗ p2 = p1;  // OK 
    constexpr const char∗ p2 = p1+2; // error : the compiler does not know the value of p1 
    constexpr char c = p1[2];   // OK, c==’d’; the compiler knows the value pointed to by p1 
+2

'constexpr const char * p3 = p1 + 2;'はわかります。 「p1 + 2」は、静的記憶期間を有するサブオブジェクトのアドレスを表す値であり、有効な定数式である。この場合、完全なオブジェクトの必要はありません。非タイプのテンプレート引数として使用することはできませんが、それは別の話です。私はこのような構造が邪魔になるような規格を見つけることはできません。これはC++ 11以降のバージョンに適用されます。私がテストしたすべてのコンパイラがそれを受け入れます。 [この回答](http://stackoverflow.com/a/17660391/4326278)も参照してください。 – bogdan

3

それは簡単です。

は、コンパイラは、このコードで呼ばれるアドレスを知っている方法を考えてみましょう:

puts("hey!"); 

コンパイラがputsの場所のないアイデアを持っていない、それはまたそれのためのランタイム・ルックアップを追加しません(それはなるだろうそれは実際にクラスの仮想メソッドが必要とするものですが、パフォーマンスには悪いです)。ランタイム時に異なるバージョンのダイナミックライブラリを持つ可能性(正確に同じライブラリファイルであってもアドレス空間レイアウトのランダム化はもちろん)は、ビルド時にツールチェーンリンカーがそれを認識しないようにします。

したがって、コンパイルされたバイナリプログラムを起動するときにアドレスを修正するのは、dynamic linkerです。これは、の再配置と呼ばれます。

constexpr:コンパイラは再配置テーブルにこのアドレスを使用するコード内のすべての場所を追加し、プログラムが開始するたびに動的リンカがそのジョブを実行します。

関連する問題