函数指针的说明

Explanation of function pointers

本文关键字:说明 指针 函数      更新时间:2023-10-16

我在理解一些结合函数指针和函数声明的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"并已初始化 函数Ptr 乐趣 = 函数;

因此我明白了,

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

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);

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

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的函数,而不是返回函数Ptr的函数?

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

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

嗯,这有点令人困惑。

函数

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

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

你遇到一些情况,而它没有。

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

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

现在,这段代码:

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

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

你最

有趣的例子是这个,这里没有使用 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

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

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实际上不是函数指针的 typedef,而是实际函数的 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;
}
在这里,您

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

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

最后一个示例,即您有一个函数返回函数的示例,根本不允许,因为它毫无意义。

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

你可以这样使用它,我已经测试过了。关于这个问题确实有一个棘手的问题。