2016-11-29 5 views
-1

は、次のコードを考えてみましょう:ストア機能アドレス

error: initializer element is not a compile-time constant

しかしvoid*ための変数はしていません:

void f() {}; 
void* v = f; 
int i = f; 
int main() { } 

はなぜint変数に関数のアドレスを格納することは私にerrorを与えますか?

+3

パイソンのネストに象を置くことはできません。ところで、グローバルオブジェクトの初期化は一定でなければなりません。 – haccks

+3

誰がintに変数のアドレスを格納できると言ったのですか?言い換えれば、 'sizeof(int)'は 'sizeof(void *) 'と同じですか? – LPs

+3

なぜあなたはそれをしたいですか?あなたはこのための関数ポインタを持っています – sas

答えて

2

私はこれをコンパイルすると、私が手:

$ gcc foo.c 

foo.c:3:5: warning: incompatible pointer to integer conversion initializing 'int' with an expression of type 'void()' [-Wint-conversion] 
int i = f; 
    ^ ~ 
foo.c:3:9: error: initializer element is not a compile-time constant 
int i = f; 
     ^
1 warning and 1 error generated. 

は本当に、私は警告がエラーであるべきだと思います。あなたは整数にアドレスを入れようとしていますが、それは一般的に悪い形です。

つまり、コンパイラが先に進んで変換を行うと、その結果はコンパイル時定数ではない(変換された値なので)。したがって、エラー。

C++について質問しているわけではありませんが、私は2つの言語がどう違うか興味がありましたので、コンパイラの動作を確認しました。その言語では、fの両方の割り当ては正当な理由で不正です。 void*は、パラメータなしの関数へのポインタではなく、intもポインタではありません。 vi両方の

$ g++ foo.c 
clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated 
foo.c:2:7: error: cannot initialize a variable of type 'void *' with an lvalue of type 'void()' 
void* v = f; 
    ^ ~ 
foo.c:3:5: error: cannot initialize a variable of type 'int' with an lvalue of type 'void()' 
int i = f; 
    ^ ~ 
2 errors generated. 
+0

これらの割り当てはCでも許されません。問題は、C++ではなくCについてです。 – Lundin

+0

あなたはそれがCの質問であることについて正しいです。私は自分の好奇心のために追加しただけであることを明確にしています。 私はさまざまなC言語の方言を試して、関数をvoid *に変換することについて不平を言うかどうかを調べました。もちろん、これはclangのバグかもしれませんが、私はそれが不許可ではなく未定義の動作である可能性が高いと思う傾向があります。 'GCC -stc = C90 foo.c'、 'GCC -std = iso9899::1999 foo.c'、 ' GCC -std = iso9899:2011 foo.cを 具体的には、これらの全ては、同じ結果が得られました' – burlyearly

0

初期化は違法です。

ただし、一部のコンパイラでは、関数ポインタからコンパイラ拡張としてvoid*に変換することができます。

あなたの警告でコンパイルを試してみてください(あなたが慣れているように)、void* v = f;行でも警告が表示されることがあります。関数fへのポインタを格納する

正しい方法は、ファイルスコープで宣言

void (*p)() = f; 
3
  • 変数(「グローバル」)静的記憶持続時間を有するであろう。

    静的記憶期間を持つすべての変数は、コンパイル時定数に初期化する必要があります。 Cでは、他の変数(even constもありません)はコンパイル時定数としてカウントされないため、エラーになります。

  • さらに、整数へのポインタを割り当てることはできません。これは有効ではありません。ポインタをキャストすることで、手動でポインタを整数型に手動で変換する必要があります。

  • さらに、キャストvoid* v = f;は明確に定義されていません。 C標準は、void*とオブジェクト型へのポインタの間のキャストのみを指定します。


今、あなたは、関数のアドレスを取得するために何をすべきか、このです:

#include <stdint.h> 

uintptr_t i = (uintptr_t) f; 

int main (void) 
{ ... 

uintptr_tは、任意のポインタのアドレスを格納するのに十分な大きさであることが保証されます。あなたが本当にintへのポインタの基礎となるビットを入れたい場合

+0

'すべてのポインタ 'はポインタだけでなくオブジェクトへのポインタも意味しますか? – alexolut

+0

@alexolut実際、厳密に言えば、C標準はvoidへのポインタしか言及していないと思います。実際には、整数が符号なしで十分に大きいと仮定すると、関数ポインタを整数に変換することは常に有効です。 – Lundin

+0

グローバル変数はコンパイル時定数で初期化する必要がありますが、関数のアドレスはコンパイル時には分かりません。 uintptr_t変数にもどのように割り当てることができますか? – alexolut

0

あなたはこれを試みることができる:

typedef union _UPTRINT 
{ 
    void *ptr; 
    int i; 
} UPTRINT; 

が続いiとしてそれをUPTRINT::ptrに関数ポインタを割り当て、アクセス(ポインタとintsがあると仮定してお使いのプラットフォームで同じサイズ、必要に応じてiのタイプを調整します)。

+0

これは決して動作することを保証するものではなく、キャストと同じことです。 'void *'と 'int'はサイズが異なるだけでなく(64ビットシステムでも8ビットシステムでも16ビットシステムでは失敗します)、表現が異なる場合があります。アドレスは、通常、署名されていません。 – Lundin

+0

私は答えの最後の文でそれを説明しました。 –

+0

私の指摘は、これにはint型をまったく関与させたり、自家製の型を使用する理由はありません。 'uintptr_t'は状況に応じて存在します。これは、アドレスを格納できる移植可能な整数型に最も近いものです。 – Lundin

関連する問題