int(*)(int *)= 5(或任何整数值)的含义


88

我无法弄清楚:

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

上面的分配使用g ++ c ++ 11进行编译。我知道这int (*) (int *)是一个指向接受(int *)as参数并返回int的函数的指针,但我不明白您如何将其等同于5。起初,我认为这是一个不断返回5的函数(根据我最近的学习F#,可能是哈哈),然后,我简要地想到了,该函数指针指向内存位置5,但这显然不起作用,十六进制值也不起作用。

认为可能是因为该函数返回一个int,并且分配一个int是可以的(以某种方式),所以我也尝试这样做:

int * (*) (int *) = my_ptr

wheremy_ptr是type int *,与第二个函数指针的类型相同,第一种情况是int类型。这不会编译。分配5或任何int值而不是my_ptr都不会为此函数指针编译。

那任务是什么意思呢?

更新1

我们已确认这是一个错误,如最佳答案所示。但是,仍然不知道分配给函数指针的值实际发生了什么,或者分配发生了什么。任何(良好)的解释将不胜感激!请参考下面的编辑以更清楚地了解该问题。

编辑1

我正在使用gcc版本4.8.2(在Ubuntu 4.8.2中)

编辑2

实际上,将其等同于对我的编译器有效的任何东西。甚至将其等同于std :: string变量,或者返回双精度值的函数名称,都可以使用。

编辑2.1

有趣的是,使其成为指向返回非指针数据类型的任何函数的函数指针,将使其进行编译,例如

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

但是一旦函数指针指向返回某些指针的函数,它就不会编译,例如

some_data_type ** (*) () = any_value;

3
嗯...看起来不对劲,c不接受。可能是gcc扩展名(或bug)。
Wintermute 2015年

4
g ++编译,但是gcc无法正常工作:error: expected identifier or '(' before ')' token
tivn

3
@ 0x499602D注意,代码没有给指针命名。用int *x = 5你命名的x。有了 int * (*x) (int *) = 5它就不会编译。(尽管它将编译为C代码)。
2015年

5
简化的测试用例:int(*) = 5;int(*);
Johannes Schaub-litb

Answers:


60

这是g ++中的错误。

 int (*) (int *) 

是类型名称。

在C ++中,不能有类型名称没有标识符的声明。

因此,可以使用g ++进行编译。

 int (*) (int *) = 5;

这也编译:

 int (*) (int *);

但它们都是无效的声明。

编辑

TC在评论中提到了bugzilla bug 60680,带有类似的测试用例,但尚未得到批准。该错误已在bugzilla中确认。

编辑2

当以上两个声明在文件范围内时,g ++会正确发出诊断(它无法在块范围内发出诊断)。

编辑3

我检查了一下,并可以在最新版本的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 ++。请记住,因为您的特定编译器碰巧会编译,所以它无效。像所有复杂的软件一样,编译器有时也有错误,而且似乎是一个错误。

相比之下,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);

现在proc是一个指向函数的指针,该指针期望该地址5是采用anint*并返回an的函数的基地址int

在某些微控制器/微处理器上,5可能是一个有效的代码地址,并且可能在其中找到这种功能。

在大多数通用计算机上,内存的第一页(0-10234K页的地址)故意无效(未映射),以便捕获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从人工代码生成机器代码是一个非常复杂的过程(尤其是如果您希望编译器优化其输出)。由于编译非常复杂,因此一步之遥就无法完成,因此大多数编译器都具有某种形式的中间表示(IR)。
2015年

2
拥有IR的另一个原因是,如果您有明确定义的IR,则可以将编译器的前端和后端分开。(例如,前端将C编译为IR,后端将IR编译为Intel机器代码。现在,如果要添加ARM支持,则只需要第二个后端。如果要编译Go,则只需一个第二前端,以及更重要的是围棋编译器立即同时支持英特尔和ARM,因为你可以重复使用两个后端顶部。
11684

@ 11684 OK,很有道理。很有意思。我不能确定什么语言罗兰给了这个答案,但...它看起来像某种与C装配混合
康拉德·卡普

IR不需要打印;我不知道gcc使用了什么,这可能只是一个可打印的表示形式@KonradKapp
15684
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.