C++ 函数指针的解引用是如何发生的?

C++ 函数指针的解引用是如何发生的?,c++,c,pointers,function-pointers,C++,C,Pointers,Function Pointers,为什么以及如何取消对函数指针的引用只是“什么都不做” 这就是我所说的: #include<stdio.h> void hello() { printf("hello"); } int main(void) { (*****hello)(); } #包括 void hello(){printf(“hello”);} int main(void){ (******你好)(); } 根据以下评论: 函数指针只是取消引用而已 很好,但是结果函数 指示器将立即显示 转换回

为什么以及如何取消对函数指针的引用只是“什么都不做”

这就是我所说的:

#include<stdio.h>

void hello() { printf("hello"); }

int main(void) { 
    (*****hello)(); 
}
#包括
void hello(){printf(“hello”);}
int main(void){
(******你好)();
}

根据以下评论:

函数指针只是取消引用而已 很好,但是结果函数 指示器将立即显示 转换回函数指针


答案是:

(以你认为的方式)取消对a的引用 函数的指针表示:访问 将内存编码为数据类型 记忆

函数指针不应该是 以这种方式取消引用。相反,它 被称为

我会用一个名字“去引用”一方 与“呼叫”并列。没关系

无论如何:C就是这样设计的 这两个函数名标识符都是 以及变量保持函数的 指针的意思相同:指向代码的地址 记忆。它允许跳转到那个位置 通过使用call()语法 在标识符或变量上



函数指针的解引用究竟是如何工作的?

这不是一个完全正确的问题。至少对于C来说,正确的问题是

右值上下文中的函数值会发生什么变化

(右值上下文是名称或其他引用应用作值的任何位置,而不是基本上除赋值左侧以外的任何位置。名称本身来自赋值的右侧。)

好的,那么右值上下文中的函数值会发生什么变化呢?它立即隐式转换为指向原始函数值的指针。如果使用
*
解除对该指针的引用,则会再次获得相同的函数值,该值会立即隐式转换为指针。你可以想做多少次就做多少次

您可以尝试两个类似的实验:

  • 如果在赋值的左侧的左值上下文中取消引用函数指针,会发生什么情况。(如果您牢记函数是不可变的,那么答案将与您的期望有关。)

  • 数组值也被转换为左值上下文中的指针,但它被转换为指向元素类型的指针,而不是指向数组的指针。因此,取消对它的引用将为您提供一个元素,而不是一个数组,并且您显示的疯狂不会发生

希望这有帮助

另外,关于为什么函数值会隐式转换为指针,答案是对于那些使用函数指针的用户来说,不必到处使用
&
,这是一种极大的便利。还有一个双重便利:处于调用位置的函数指针会自动转换为函数值,因此您不必编写
*
来通过函数指针调用

void f() {
    void (*recurse)() = f; // "f" is a reference; implicitly convert to ptr.
    recurse(); // call operator is defined for pointers
}
与C函数不同,C++函数可以重载,而我没有资格评论C++中的语义如何工作。

< P> C++ 03节4.3/1:< /P> 函数类型T的左值可以转换为“指向T的指针”类型的右值。结果是指向函数的指针

如果尝试对函数引用执行无效操作,例如一元
*
运算符,则该语言首先尝试的是标准转换。这就像将
int
添加到
float
时转换它一样。在函数引用上使用
*
会导致语言采用其指针,在您的示例中,指针是平方1

这适用的另一种情况是分配函数指针时

void f() {
    void (*recurse)() = f; // "f" is a reference; implicitly convert to ptr.
    recurse(); // call operator is defined for pointers
}
请注意,这不会以另一种方式工作

void f() {
    void (&recurse)() = &f; // "&f" is a pointer; ERROR can't convert to ref.
    recurse(); // OK - call operator is *separately* defined for references
}
函数引用变量很好,因为它们(理论上,我从未测试过)向编译器暗示,如果在封闭范围内初始化,则间接分支可能是不必要的

在C99中,取消对函数指针的引用会产生一个函数指示符。§6.3.2.1/4:

函数指示符是具有函数类型的表达式。除非是sizeof运算符或一元&运算符的操作数,否则类型为“函数返回类型”的函数指示符将转换为类型为“指向函数返回类型的指针”的表达式


这更像诺曼的答案,但值得注意的是C99没有右值的概念。

请站在编译器编写者的立场上思考。函数指针有明确定义的含义,它是指向表示机器代码的字节块的指针


当程序员取消引用一个函数指针时,你会怎么做?您是否将机器代码的第一个(或8个)字节重新解释为指针?这种方法行不通的可能性大约是20亿比1。你申报UB吗?已经有很多了。或者你只是忽略了这个尝试?您知道答案。

函数指针的解引用究竟是如何工作的

两步。第一步是编译时,第二步是运行时

在第一步中,编译器看到它有一个指针和一个上下文,在该上下文中该指针被取消引用(例如
(*pFoo)(
),因此它会为这种情况生成代码,这些代码将在第二步中使用


在步骤2中,在运行时执行代码。指针包含一些字节,指示下一步应该执行哪个函数。这些字节以某种方式加载到CPU中。常见的情况是CPU带有显式的
调用[寄存器]
指令。在这样的系统中,函数指针可以是内存中函数的地址,解除保护代码只不过是将该地址加载到寄存器中,然后执行一个
调用[register]
指令。

这是通过一些隐式转换实现的。事实上,根据C标准:

ISO/IEC 2011,第6.3.2.1节左值、数组和函数指示符,第4段

函数指示符是一个表达式t
void (*ptr)(void) = &func;
ptr();
(*ptr)();
(****ptr)();
void create_person(void);
void update_person(void);
void delete_person(void);

struct Person {
    void (*create)(void);
    void (*update)(void);
    void (*delete)(void);
};

static struct Person person = {
    .create = &create_person,
    .update = &update_person,
    .delete = &delete_person,
};

int main(void)
{
    person.create();
    person.update();
    person.delete();
    return 0;
}