int(*)(int *)= 5(または任意の整数値)の意味


88

私はこれを理解することはできません:

int main() {
    int (*) (int *) = 5;
    return 0;
}

上記の割り当ては、g ++ c ++ 11でコンパイルされます。それint (*) (int *)(int *)引数として受け入れてintを返す関数へのポインタであることは知っていますが、それを5と同等にする方法がわかりません。最初は、常に5を返す関数だと思いました(最近の学習からF#、おそらくハハ)、それから私は簡単に、関数ポインタがメモリ位置5を指していると思いましたが、それは明らかに機能せず、16進値も機能しません。

関数がintを返すためである可能性があり、intの割り当ては(どういうわけか)大丈夫だと考えて、私もこれを試しました:

int * (*) (int *) = my_ptr

ここmy_ptrで、は型int *であり、int型の最初の場合と同様に、この2番目の関数ポインタと同じ型です。これはコンパイルされません。の代わりに5または任意のint値を割り当ててもmy_ptr、この関数ポインターに対してはコンパイルされません。

では、割り当てはどういう意味ですか?

アップデート1

ベストアンサーに示されているように、これはバグであることが確認されています。ただし、関数ポインタに割り当てた値に実際に何が起こるのか、または割り当てで何が起こるのかはまだわかりません。それについての(良い)説明は大歓迎です!問題をより明確にするために、以下の編集を参照してください。

編集1

gccバージョン4.8.2(Ubuntu 4.8.2)を使用しています

編集2

実際、それを何かと同等にすることは私のコンパイラで機能します。std :: string変数、またはdoubleを返す関数名と同等でも機能します。

2.1を編集

興味深いことに、ポインタではないデータ型を返す関数への関数ポインタにすると、次のようにコンパイルできます。

std::string (*) () = 5.6;

しかし、関数ポインタが何らかのポインタを返す関数を指すとすぐに、次のようにコンパイルされません。

some_data_type ** (*) () = any_value;

3
うーん...それは正しく見えません、そしてclangはそれを受け入れません。gcc拡張(またはバグ)である可能性があります。
Wintermute 2015

4
g ++はコンパイルされますが、gccが機能しません:error: expected identifier or '(' before ')' token
tivn 2015

3
@ 0x499602Dコードはポインタに名前を付けていないことに注意してください。ではint *x = 5、あなたはそれを命名しますx。それで int * (*x) (int *) = 5コンパイルされません。(Cコードとしてコンパイルされますが)。
nos 2015

5
削減されたテストケース:int(*) = 5;およびint(*);
Johannes Schaub-litb 2015

回答:


60

これはg ++のバグです。

 int (*) (int *) 

タイプ名です。

C ++では、識別子のない型名の宣言を作成することはできません。

したがって、これはg ++でコンパイルされます。

 int (*) (int *) = 5;

そしてこれもコンパイルされます:

 int (*) (int *);

しかし、それらは両方とも無効な宣言です。

編集

TCは、コメントで同様のテストケースを持つbugzillaバグ60680について言及していますが、まだ承認されていません。バグはbugzillaで確認されています。

EDIT2

上記の2つの宣言がファイルスコープにある場合、g ++は正しく診断を発行します(ブロックスコープでの診断の発行に失敗します)。

EDIT3

確認したところ、g ++バージョン4の最新リリース(4.9.2)、最新のプレリリースバージョン5(5.0.1 20150412)、および最新の実験バージョン6(6.0.0 20150412)で問題を再現できます。


5
MSVCは、編集された投稿コードを次のように拒否しましたerror C2059: syntax error : ')'
Weather Vane

タイプ名の場合、なぜ 'int(*)(int *)int_func;'を実行しないのですか?作業?
Konrad Kapp 2015

1
GCC Bugzillaの場合、「NEW」は確認済みのバグです。(未確認のバグは「未確認」です)。
TC

4
@KonradKapp :。int (*int_func)(int *); という名前の関数ポインタを宣言していると言えば問題なく動作しますint_func
エドワード

3
@KonradKapp C ++は、識別子の配置に中置記法を使用します。同じ理由でint x[5];、そうではありませんint[5] x;
MM

28

有効なC ++ではありません。あなたの特定のコンパイラがたまたまコンパイルするので、それはそれを有効にしないことを覚えておいてください。コンパイラは、すべての複雑なソフトウェアと同様に、バグがある場合があり、これは1つのように見えます。

対照的に、clang++不平を言う:

funnycast.cpp:3:11: error: expected expression
    int (*) (int *) = 5;
          ^
funnycast.cpp:3:18: error: expected '(' for function-style cast or type construction
    int (*) (int *) = 5;
             ~~~ ^
funnycast.cpp:3:19: error: expected expression
    int (*) (int *) = 5;
                  ^
3 errors generated.

問題のある行は有効なC ++ではないため、これは予想される動作です。これは(のために=)割り当てであると主張しますが、識別子は含まれていません。


9

他の回答が指摘しているように、それはバグです

int (*) (int *) = 5;

コンパイルします。意味があると予想されるこのステートメントの合理的な概算は次のとおりです。

int (*proc)(int*) = (int (*)(int*))(5);

Nowprocは、5アドレスがを取り、int*を返す関数のベースアドレスであることを期待する関数へのポインタintです。

一部のマイクロコントローラー/マイクロプロセッサーで5は、有効なコードアドレスである可能性があり、そのような関数をそこで見つけることができる場合があります。

ほとんどの汎用コンピューターでは、ポインターアクセス0-1023をキャッチするために、メモリの最初のページ(4Kページのアドレス)が意図的に無効(マップされていない)になっていnullます。

したがって、動作はプラットフォームによって異なりますが、*procが呼び出されたときにページフォールトが発生することが合理的に予想されます(例:)(*proc)(&v)*proc呼び出される前は、異常なことは何も起こりません。

ダイナミックリンカーを作成しているのでない限り、アドレスを数値で計算して関数へのポインター変数に割り当てることはほぼ間違いありません。


2
/usr/lib/gcc/x86_64-pc-cygwin/4.9.2/cc1plus.exe -da so.cpp

このコマンドラインは、多くの中間ファイルを生成します。それらの最初のものso.cpp.170r.expandは、、言います:

...
int main() ()
{
  int D.2229;
  int _1;

;;   basic block 2, loop depth 0
;;    pred:       ENTRY
  _1 = 0;
;;    succ:       3

;;   basic block 3, loop depth 0
;;    pred:       2
<L0>:
  return _1;
;;    succ:       EXIT

}
...

これはまだ正確に何が起こっているのか答えていませんが、それは正しい方向への一歩であるはずです。


面白い。これらの中間ファイルの目的は何ですか?
Konrad Kapp 2015

@KonradKapp人間のコードからマシンコードを生成することは非常に複雑なプロセスです(特にコンパイラに出力を最適化させたい場合)。コンパイルは非常に複雑であるため、1つのステップで実行されるわけではありません。ほとんどのコンパイラには、何らかの形式の中間表現(IR)があります。
11684 2015

2
IRを使用するもう1つの理由は、明確に定義されたIRがある場合、コンパイラのフロントエンドとバックエンドを分離できることです。(たとえば、フロントエンドはCをIRにコンパイルし、バックエンドはIRをIntelマシンコードにコンパイルします。ARMサポートを追加する場合は、2番目のバックエンドのみが必要です。Goをコンパイルする場合は、 、そしてあなたは、両方のバックエンドを再利用することができますので、移動コンパイラはすぐにインテルとARMの両方をサポートしていることの上に第2フロントエンド。
11684

@ 11684 OK、理にかなっています。とても興味深い。私はそれがアセンブリC.と混合のいくつかの種類のように見えるのに...言語ローランドはこの答えになったかを決定できなかった
コンラートKAPP

IRは印刷可能である必要はありません。gccが何を使用しているのかわかりません。これは、単に印刷可能な表現である可能性があります@KonradKapp
11684
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.