【问题标题】:Meaning of int (*) (int *) = 5 (or any integer value)int (*) (int *) = 5(或任何整数值)的含义
【发布时间】:2015-06-20 14:56:25
【问题描述】:

我想不通:

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

上面的赋值是用 g++ c++11 编译的。我知道int (*) (int *)是一个指向函数的指针,它接受(int *)作为参数并返回一个int,但我不明白你怎么能把它等同于5。起初我以为它是一个不断返回5的函数(根据我最近在 F# 中的学习,可能,哈哈),然后我简要地认为,函数指针指向内存位置 5,但这显然不起作用,十六进制值也不起作用。

认为这可能是因为函数返回一个 int,并且分配一个 int 是可以的(不知何故),我也尝试了这个:

int * (*) (int *) = my_ptr

其中my_ptr 的类型为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;

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

some_data_type ** (*) () = any_value;

【问题讨论】:

  • 嗯...看起来不太对劲,clang 不接受。可能是 gcc 扩展(或错误)。
  • g++ 编译,但 gcc 不工作:error: expected identifier or '(' before ')' token
  • @0x499602D 请注意,代码没有给指针命名。使用int *x = 5,您将其命名为x。使用 int * (*x) (int *) = 5 将无法编译。 (尽管它会编译为 C 代码)。
  • 精简测试用例:int(*) = 5;int(*);

标签: c++ pointers function-pointers


【解决方案1】:

正如其他答案所指出的,这是一个错误

int (*) (int *) = 5;

编译。预期具有含义的该陈述的合理近似是:

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

现在proc 是一个指向函数的指针,它期望地址5 是一个函数的基地址,该函数接受一个int* 并返回一个int

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

在大多数通用计算机上,内存的第一页(4K 页的地址 0-1023)故意无效(未映射)以捕获 null 指针访问。

因此,虽然行为取决于平台,但可以合理地预期在调用 *proc 时会发生页面错误(例如,(*proc)(&v))。在调用*proc 之前,没有任何异常发生。

除非您正在编写动态链接器,否则您几乎可以肯定不应该以数字方式计算地址并将它们分配给指向函数的变量。

【讨论】:

    【解决方案2】:

    这是 g++ 中的一个错误。

     int (*) (int *) 
    

    是一个类型名称。

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

    所以这是用 g++ 编译的。

     int (*) (int *) = 5;
    

    这也可以编译:

     int (*) (int *);
    

    但它们都是无效的声明。

    编辑

    T.C. 在 cmets bugzilla 错误60680 中提到了类似的测试用例但尚未获得批准。该错误已在 bugzilla 中得到确认。

    EDIT2

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

    EDIT3

    我检查过,我可以在最新版本的 g++ 版本 4 (4.9.2)、最新的预发布版本 5 (5.0.1 20150412) 和最新的实验版本 6 (6.0.0 20150412) 上重现该问题。

    【讨论】:

    • MSVC 用error C2059: syntax error : ')' 拒绝了已编辑的发布代码
    • 如果是类型名,为什么不'int (*) (int *) int_func;'工作吗?
    • 对于 GCC bugzilla,“NEW”是一个已确认的错误。 (未确认的错误为“未确认”)。
    • @KonradKapp:如果你说 int (*int_func)(int *); 声明一个名为 int_func 的函数指针,它就可以正常工作。
    • @KonradKapp C++ 使用中缀表示法来放置标识符;同样的原因是int x[5]; 而不是int[5] x;
    【解决方案3】:

    它不是有效的 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++。它声称是一个赋值(因为=),但不包含标识符。

    【讨论】:

      【解决方案4】:
      /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
      
      }
      ...
      

      这仍然不能准确地回答发生了什么,但它应该是朝着正确方向迈出的一步。

      【讨论】:

      • 有趣。这些中间文件的用途是什么?
      • @KonradKapp 从人类代码生成机器代码是一个相当复杂的过程(特别是如果您希望编译器优化其输出)。由于编译非常复杂,它不是一步完成的,大多数编译器都有某种形式的中间表示 (IR)。
      • 拥有 IR 的另一个原因是,如果您有一个定义明确的 IR,您可以将编译器的前端和后端分开。 (例如,前端将 C 编译为您的 IR,后端将 IR 编译为 Intel 机器代码。现在,如果您想添加 ARM 支持,您只需要第二个后端。如果您想编译 Go,您只需要一个第二个前端,最重要的是,Go 编译器立即支持 Intel 和 ARM,因为您可以重用这两个后端。
      • @11684 好的,有道理。很有意思。不过,我无法确定 Roland 在这个答案中给出了什么语言......它看起来像是某种与 C 混合的程序集。
      • IR 不需要打印;我不知道 gcc 使用什么,这可能只是一个可打印的表示@KonradKapp
      猜你喜欢
      • 1970-01-01
      • 2019-05-02
      • 1970-01-01
      • 2021-10-29
      • 2011-10-12
      • 2012-03-15
      • 1970-01-01
      • 1970-01-01
      • 2010-10-15
      相关资源
      最近更新 更多