函数指针的解释

时间:2016-02-26 14:55:30

标签: c++ function pointers typedef

我在理解一些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 = function;

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

int main() {

    functionPtr fun;
    fun(5);

    return 0;
}

我收到了链接错误。我以某种方式理解,因为编译器现在将乐趣视为已经存在的函数,然后由于事实,这种乐趣无法定义,我将得到链接错误。因此我改变了变量的名称:

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 ++编译器。

4 个答案:

答案 0 :(得分:8)

嗯,这有点令人困惑。

函数类型和指向函数类型的指针实际上是两种不同的类型(不再类似于int和指向int的指针)。但是,有一个规则,函数类型衰减指向几乎所有上下文中的函数类型。这里衰减松散地意味着转换(类型转换和衰减之间存在差异,但你现在可能对它不感兴趣)。

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

如果没有,你会遇到一些情况。

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

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

现在,这段代码:

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

function不影响任何事情。编译器知道它不是可以调用的函数指针,只调用原始的function

答案 1 :(得分:4)

你的例子中最有趣的是这一个,在这里不使用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的指针”的prvalue。结果是指向   功能。

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

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

答案 2 :(得分:3)

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

typedef void(functionPtr)(int);

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

int main() {

    functionPtr fun = function;
    fun(5);

    return 0;
}

在这里,您要为带有functionPtr的函数创建一个typedef int,并且不返回值。 functionPtr实际上不是函数指针的typedef,而是实际函数的函数。

然后,您尝试声明一个新函数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;
}

这里你定义了一个返回函数指针的函数,但是你没有返回它。根据您的设置,编译器可能会或可能不会抱怨。但function再次与functionPtr完全分开。你基本上写了

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

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

答案 3 :(得分:-1)

typedef void functionPtr (int);

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

int main() {

    functionPtr *func;
    func = function;
    func(5);
    return 0;
}

你可以用这种方式使用它,我测试过了。 这个问题真的很麻烦。