【问题标题】:Explanation of function pointers函数指针说明
【发布时间】:2016-06-09 20:33:59
【问题描述】:

我对一些结合函数指针和函数声明的C++语法理解有问题,即:

通常当我们想要声明一种函数类型时,我们会做这样的事情:

typedef void(*functionPtr)(int);

这对我来说很好。从现在开始 functionPtr 是一个类型,它表示 指向函数的指针,该函数返回 void 并将 int 值作为参数。

我们可以这样使用它:

typedef void(*functionPtr)(int);

void function(int a){
    std::cout << a << std::endl;
}

int main() {

    functionPtr fun = function;
    fun(5);

    return 0;
}

我们会在屏幕上打印5

我们有指向函数fun 的指针,我们将一些现有的指针分配给函数-function,并通过指针执行该函数。很酷。

现在,正如我在一些书中看到的那样,函数和函数指针在某种程度上被视为相同,所以实际上在声明 function() 函数之后,每次我们说函数时,我们的意思是真正的函数和指向函数的指针具有相同的类型,所以以下编译,每条指令都给出相同的结果(屏幕上打印 5 个):

int main() {

    functionPtr fun = function;
    fun(5);
    (*fun)(5);
    (*function)(5);
    function(5);

    return 0;
}

所以现在只要我能想象,指向函数和函数的指针几乎相同,那么对我来说就很好了。

那么我虽然,如果指向函数的指针和真正的函数是相同的,那为什么我不能做以下:

typedef void(functionPtr)(int); //removed *

void function(int a){
    std::cout << a << std::endl;
}

int main() {

    functionPtr fun = function;
    fun(5);

    return 0;
}

这给了我以下错误:

prog.cpp:12:14:警告:“void fun(int)”的声明具有“extern”并已初始化 functionPtr fun = 函数;

因此我明白了,由于某种原因,编译器现在明白了,有趣的是已经存在功能。然后我尝试了以下操作:

int main() {

    functionPtr fun;
    fun(5);

    return 0;
}

我得到了链接错误。我以某种方式理解,由于编译器现在将 fun 视为已经存在的函数,因此由于 fun 没有被定义,我会得到链接错误。因此我更改了变量的名称:

typedef void(functionPtr)(int);

void function(int a){
    std::cout << a << std::endl;
}

int main() {

    functionPtr function;
    function(5);

    return 0;
}

所以现在函数在主阴影全局名称函数中,所以 function(5) 是从声明 functionPtr function; 中使用的,它工作正常并在屏幕上打印 5。

所以现在我很震惊。为什么会这样?同样令人误解的是,当函数指针这样声明时:

typedef void(*functionPtr)(int);

我可以通过以下方式创建 functionPtr 类型的函数:

functionPtr function(int a){
    std::cout << a << std::endl;
}

然而,当声明类似:

typedef void(functionPtr)(int);

做这个:

functionPtr function(int a){
    std::cout << a << std::endl;
}

被编译器解释为函数返回函数。如果是这样,为什么之前的声明 (typedef void(functionPtr)(int);) 知道这是一个返回 void 的函数而不是返回 functionPtr 的函数?

有人可以为我解释一下引擎盖下到底发生了什么吗?

我正在使用启用了 C++14 选项的 g++ C++ 编译器。

【问题讨论】:

    标签: c++ function pointers typedef


    【解决方案1】:

    嗯,这有点令人困惑。

    函数类型和指向函数类型的指针确实是两种不同的类型(不比int 和指向int 的指针更相似)。但是,有一条规则,即在几乎所有上下文中,函数类型衰减 指向指向函数类型的指针。这里的decaying 松散地表示converted(类型转换和decaying 是有区别的,但你现在可能对它不感兴趣)。

    重要的是,几乎每次使用函数类型时,都会得到指向函数类型的指针。但是请注意几乎 - 几乎每次都不是总是

    而且您遇到了一些情况,但实际上并没有。

    typedef void(functionPtr)(int);
    functionPtr fun = function;
    

    此代码试图将一个函数(不是指针!函数!)复制到另一个。但当然,这是不可能的——你不能在 C++ 中复制函数。编译器不允许这样做,我不敢相信你已经编译了它(你是说你有链接器错误?)

    现在,这段代码:

    typedef void(functionPtr)(int);
    functionPtr function;
    function(5);
    

    function 不会影响任何东西。编译器知道它不是一个可以调用的函数指针,只是调用你原来的function

    【讨论】:

    • 这段代码没有被编译。这段代码是:functionPtr fun; fun(5);(没有赋值)这里 fun 被视为某个函数,存在于某处,但链接看不到它,所以我得到链接错误
    • 那么,为什么当typedef void(functionPtr)(int); 是这样的时候,我有:functionPtr fun;乐趣();我得到链接错误?见这里:ideone.com/e20FmU
    • @DawidPi 因为,正如 SergeyA 所解释的那样,该代码不正确。只能在此答案中发布的实现 function 函数的情况下工作。在你的情况下,乐趣不是。
    • 那么这条线是什么意思? :functionPtr fun;,当typedef void(functionPtr)(int);
    【解决方案2】:

    你的例子中最有趣的是这个,这里没有使用 typedef:

    void function(int a) { // declaration and definition of 'function'
        std::cout << a << std::endl;
    }
    
    int main() {
        void function(int); // declaration of 'function'
        function(5);
    }
    

    在 C++ 的大多数上下文中,在本地范围内重新声明 function 会影响全局 ::function。所以期待链接器错误是有道理的 - main()::function 没有定义对吗?

    除了函数在这方面是特殊的。来自 [basic.scope.pdel] 中的注释:

    块范围内的函数声明 并且在块范围内使用 extern 说明符的变量声明指的是属于 一个封闭的命名空间,但它们不会将新名称引入该范围。

    所以该代码示例完全等同于:

    void function(int a) { /* ... */ }
    void function(int ); // just redeclaring it again, which is ok
    
    int main() {
        function(5);
    }
    

    您还可以通过将全局 function 放入某个命名空间 N 来验证这一点。此时,本地范围声明将向:: 添加一个名称,但它没有定义——因此您确实会收到链接器错误。


    你提到的另一个有趣的事情是函数到指针转换的概念,[conv.func]:

    函数类型 T 的左值可以转换为“指向 T 的指针”类型的纯右值。结果是一个指向 函数。

    当你有一个函数调用表达式时——如果你调用的是一个函数类型,它首先被转换为一个指向函数的指针。这就是为什么它们是等价的:

    fun(5);         // OK, call function pointed to by 'fun'
    (*fun)(5);      // OK, first convert *fun back to 'fun'
    function(5);    // OK, first convert to pointer to 'function'
    (*function)(5); // OK, unary* makes function get converted to a pointer
                    // which then gets dereferenced back to function-type
                    // which then gets converted back to a pointer
    

    【讨论】:

      【解决方案3】:

      让我们一一看看你的例子,以及它们的真正含义。

      typedef void(functionPtr)(int);
      
      void function(int a){
          std::cout << a << std::endl;
      }
      
      int main() {
      
          functionPtr fun = function;
          fun(5);
      
          return 0;
      }
      

      在这里,您正在为接受和 int 的函数创建 typedef functionPtr,但不返回值。 functionPtr 实际上不是函数指针的类型定义,而是实际函数的类型定义。

      然后你试图声明一个新函数fun,并分配给它function。不幸的是,你不能分配给函数,所以这不起作用。

      int main() {
      
          functionPtr fun;
          fun(5);
      
          return 0;
      }
      

      同样,您正在声明一个带有您指定的签名的函数fun。但是你没有定义它,所以你在链接阶段失败了。

      typedef void(functionPtr)(int);
      
      void function(int a){
          std::cout << a << std::endl;
      }
      
      int main() {
      
          functionPtr function;
          function(5);
      
          return 0;
      }
      

      这里发生了什么?您定义typedef,并在主体中编写functionPtr function;。这基本上只是您已经编写的函数function 的原型。它重申此功能存在,但除此之外它什么也不做。其实可以这样写:

      typedef void(functionPtr)(int);
      
      void function(int a){
          std::cout << a << std::endl;
      }
      
      int main() {
      
          functionPtr function;
          functionPtr function;
          functionPtr function;
          void function(int);
      
          function(5);
      
          return 0;
      }
      

      你想要多少次,它不会改变任何事情。您所调用的function(5) 始终是同一件事。

      您可以在 C++ 中做的一件事是使用这样的 typedef 声明函数的原型,但您不能以这种方式定义它。

      typedef void(*functionPtr)(int);
      
      functionPtr function(int a){
          std::cout << a << std::endl;
      }
      

      在这里你定义了一个返回函数指针的函数,但是你没有返回它。根据您的设置,编译器可能会抱怨也可能不会抱怨。但同样functionfunctionPtr 完全分开。你基本上已经写了

      void (*)(int)   function(int a) {
          ...
      }
      

      最后一个例子,你有一个函数返回一个函数的例子,是不允许的,因为它没有意义。

      【讨论】:

        【解决方案4】:
        typedef void functionPtr (int);
        
        void function (int a){
            std::cout << a << std::endl;
        }
        
        int main() {
        
            functionPtr *func;
            func = function;
            func(5);
            return 0;
        }
        

        你可以这样使用,我测试过。 这个问题确实有点棘手。

        【讨论】:

        • 这并没有回答问题为什么会出现这个问题。请只添加与之前不同的答案或添加一些相关点。
        猜你喜欢
        • 1970-01-01
        • 2014-03-03
        • 1970-01-01
        • 1970-01-01
        • 2016-02-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多